@sc4rfurryx/proteusjs 1.0.0

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 (82) hide show
  1. package/API.md +438 -0
  2. package/FEATURES.md +286 -0
  3. package/LICENSE +21 -0
  4. package/README.md +645 -0
  5. package/dist/.tsbuildinfo +1 -0
  6. package/dist/proteus.cjs.js +16014 -0
  7. package/dist/proteus.cjs.js.map +1 -0
  8. package/dist/proteus.d.ts +3018 -0
  9. package/dist/proteus.esm.js +16005 -0
  10. package/dist/proteus.esm.js.map +1 -0
  11. package/dist/proteus.esm.min.js +8 -0
  12. package/dist/proteus.esm.min.js.map +1 -0
  13. package/dist/proteus.js +16020 -0
  14. package/dist/proteus.js.map +1 -0
  15. package/dist/proteus.min.js +8 -0
  16. package/dist/proteus.min.js.map +1 -0
  17. package/package.json +98 -0
  18. package/src/__tests__/mvp-integration.test.ts +518 -0
  19. package/src/accessibility/AccessibilityEngine.ts +2106 -0
  20. package/src/accessibility/ScreenReaderSupport.ts +444 -0
  21. package/src/accessibility/__tests__/ScreenReaderSupport.test.ts +435 -0
  22. package/src/animations/FLIPAnimationSystem.ts +491 -0
  23. package/src/compatibility/BrowserCompatibility.ts +1076 -0
  24. package/src/containers/BreakpointSystem.ts +347 -0
  25. package/src/containers/ContainerBreakpoints.ts +726 -0
  26. package/src/containers/ContainerManager.ts +370 -0
  27. package/src/containers/ContainerUnits.ts +336 -0
  28. package/src/containers/ContextIsolation.ts +394 -0
  29. package/src/containers/ElementQueries.ts +411 -0
  30. package/src/containers/SmartContainer.ts +536 -0
  31. package/src/containers/SmartContainers.ts +376 -0
  32. package/src/containers/__tests__/ContainerBreakpoints.test.ts +411 -0
  33. package/src/containers/__tests__/SmartContainers.test.ts +281 -0
  34. package/src/content/ResponsiveImages.ts +570 -0
  35. package/src/core/EventSystem.ts +147 -0
  36. package/src/core/MemoryManager.ts +321 -0
  37. package/src/core/PerformanceMonitor.ts +238 -0
  38. package/src/core/PluginSystem.ts +275 -0
  39. package/src/core/ProteusJS.test.ts +164 -0
  40. package/src/core/ProteusJS.ts +962 -0
  41. package/src/developer/PerformanceProfiler.ts +567 -0
  42. package/src/developer/VisualDebuggingTools.ts +656 -0
  43. package/src/developer/ZeroConfigSystem.ts +593 -0
  44. package/src/index.ts +35 -0
  45. package/src/integration.test.ts +227 -0
  46. package/src/layout/AdaptiveGrid.ts +429 -0
  47. package/src/layout/ContentReordering.ts +532 -0
  48. package/src/layout/FlexboxEnhancer.ts +406 -0
  49. package/src/layout/FlowLayout.ts +545 -0
  50. package/src/layout/SpacingSystem.ts +512 -0
  51. package/src/observers/IntersectionObserverPolyfill.ts +289 -0
  52. package/src/observers/ObserverManager.ts +299 -0
  53. package/src/observers/ResizeObserverPolyfill.ts +179 -0
  54. package/src/performance/BatchDOMOperations.ts +519 -0
  55. package/src/performance/CSSOptimizationEngine.ts +646 -0
  56. package/src/performance/CacheOptimizationSystem.ts +601 -0
  57. package/src/performance/EfficientEventHandler.ts +740 -0
  58. package/src/performance/LazyEvaluationSystem.ts +532 -0
  59. package/src/performance/MemoryManagementSystem.ts +497 -0
  60. package/src/performance/PerformanceMonitor.ts +931 -0
  61. package/src/performance/__tests__/BatchDOMOperations.test.ts +309 -0
  62. package/src/performance/__tests__/EfficientEventHandler.test.ts +268 -0
  63. package/src/performance/__tests__/PerformanceMonitor.test.ts +422 -0
  64. package/src/polyfills/BrowserPolyfills.ts +586 -0
  65. package/src/polyfills/__tests__/BrowserPolyfills.test.ts +328 -0
  66. package/src/test/setup.ts +115 -0
  67. package/src/theming/SmartThemeSystem.ts +591 -0
  68. package/src/types/index.ts +134 -0
  69. package/src/typography/ClampScaling.ts +356 -0
  70. package/src/typography/FluidTypography.ts +759 -0
  71. package/src/typography/LineHeightOptimization.ts +430 -0
  72. package/src/typography/LineHeightOptimizer.ts +326 -0
  73. package/src/typography/TextFitting.ts +355 -0
  74. package/src/typography/TypographicScale.ts +428 -0
  75. package/src/typography/VerticalRhythm.ts +369 -0
  76. package/src/typography/__tests__/FluidTypography.test.ts +432 -0
  77. package/src/typography/__tests__/LineHeightOptimization.test.ts +436 -0
  78. package/src/utils/Logger.ts +173 -0
  79. package/src/utils/debounce.ts +259 -0
  80. package/src/utils/performance.ts +371 -0
  81. package/src/utils/support.ts +106 -0
  82. package/src/utils/version.ts +24 -0
@@ -0,0 +1,411 @@
1
+ /**
2
+ * ContainerBreakpoints Test Suite
3
+ * Comprehensive tests for breakpoint management system
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
7
+ import { ContainerBreakpoints, BreakpointMap } from '../ContainerBreakpoints';
8
+
9
+ // Mock ResizeObserver
10
+ global.ResizeObserver = vi.fn().mockImplementation((callback) => ({
11
+ observe: vi.fn(),
12
+ unobserve: vi.fn(),
13
+ disconnect: vi.fn(),
14
+ }));
15
+
16
+ describe('ContainerBreakpoints', () => {
17
+ let containerBreakpoints: ContainerBreakpoints;
18
+ let testElement: HTMLElement;
19
+ let mockCallback: ReturnType<typeof vi.fn>;
20
+
21
+ beforeEach(() => {
22
+ // Clean up DOM
23
+ document.body.innerHTML = '';
24
+
25
+ // Create test element
26
+ testElement = document.createElement('div');
27
+ testElement.id = 'test-element';
28
+ testElement.style.width = '400px';
29
+ testElement.style.height = '300px';
30
+ document.body.appendChild(testElement);
31
+
32
+ containerBreakpoints = new ContainerBreakpoints();
33
+ mockCallback = vi.fn();
34
+
35
+ // Mock getBoundingClientRect
36
+ vi.spyOn(testElement, 'getBoundingClientRect').mockReturnValue({
37
+ width: 400,
38
+ height: 300,
39
+ top: 0,
40
+ left: 0,
41
+ bottom: 300,
42
+ right: 400,
43
+ x: 0,
44
+ y: 0,
45
+ toJSON: () => ({})
46
+ } as DOMRect);
47
+ });
48
+
49
+ afterEach(() => {
50
+ containerBreakpoints.destroy();
51
+ document.body.innerHTML = '';
52
+ vi.restoreAllMocks();
53
+ });
54
+
55
+ describe('Breakpoint Registration', () => {
56
+ it('should register breakpoints successfully', () => {
57
+ const breakpoints: BreakpointMap = {
58
+ sm: '300px',
59
+ md: '500px',
60
+ lg: '800px'
61
+ };
62
+
63
+ const id = containerBreakpoints.register(testElement, breakpoints, mockCallback);
64
+
65
+ expect(id).toBeTruthy();
66
+ expect(id).toMatch(/^proteus-breakpoint-\d+-\d+$/);
67
+ });
68
+
69
+ it('should handle invalid breakpoint values gracefully', () => {
70
+ const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
71
+
72
+ const breakpoints: BreakpointMap = {
73
+ invalid: 'not-a-valid-value',
74
+ valid: '300px'
75
+ };
76
+
77
+ const id = containerBreakpoints.register(testElement, breakpoints);
78
+
79
+ expect(id).toBeTruthy();
80
+ expect(consoleSpy).toHaveBeenCalled();
81
+
82
+ consoleSpy.mockRestore();
83
+ });
84
+
85
+ it('should register multiple breakpoint sets', () => {
86
+ const element1 = document.createElement('div');
87
+ const element2 = document.createElement('div');
88
+ document.body.appendChild(element1);
89
+ document.body.appendChild(element2);
90
+
91
+ const registrations = [
92
+ {
93
+ element: element1,
94
+ breakpoints: { sm: '300px', md: '500px' }
95
+ },
96
+ {
97
+ element: element2,
98
+ breakpoints: { xs: '200px', lg: '800px' }
99
+ }
100
+ ];
101
+
102
+ const ids = containerBreakpoints.registerMultiple(registrations);
103
+
104
+ expect(ids).toHaveLength(2);
105
+ expect(ids.every(id => id.length > 0)).toBe(true);
106
+ });
107
+ });
108
+
109
+ describe('Breakpoint Detection', () => {
110
+ it('should detect correct breakpoint for current width', () => {
111
+ const breakpoints: BreakpointMap = {
112
+ sm: '300px',
113
+ md: '500px',
114
+ lg: '800px'
115
+ };
116
+
117
+ const id = containerBreakpoints.register(testElement, breakpoints, mockCallback);
118
+
119
+ // Element width is 400px, should be 'sm' (300px <= 400px < 500px)
120
+ const currentBreakpoint = containerBreakpoints.getCurrentBreakpoint(id);
121
+ expect(currentBreakpoint).toBe('sm');
122
+ });
123
+
124
+ it('should call callback when breakpoint changes', () => {
125
+ const breakpoints: BreakpointMap = {
126
+ sm: '300px',
127
+ md: '500px',
128
+ lg: '800px'
129
+ };
130
+
131
+ containerBreakpoints.register(testElement, breakpoints, mockCallback);
132
+
133
+ // Initial callback should be called
134
+ expect(mockCallback).toHaveBeenCalledWith(
135
+ 'sm',
136
+ expect.objectContaining({
137
+ width: 400,
138
+ height: 300,
139
+ breakpoint: 'sm',
140
+ element: testElement
141
+ })
142
+ );
143
+ });
144
+
145
+ it('should update breakpoint when element resizes', () => {
146
+ const breakpoints: BreakpointMap = {
147
+ sm: '300px',
148
+ md: '500px',
149
+ lg: '800px'
150
+ };
151
+
152
+ const id = containerBreakpoints.register(testElement, breakpoints, mockCallback);
153
+
154
+ // Simulate resize to 600px width
155
+ vi.spyOn(testElement, 'getBoundingClientRect').mockReturnValue({
156
+ width: 600,
157
+ height: 300,
158
+ top: 0,
159
+ left: 0,
160
+ bottom: 300,
161
+ right: 600,
162
+ x: 0,
163
+ y: 0,
164
+ toJSON: () => ({})
165
+ } as DOMRect);
166
+
167
+ containerBreakpoints.updateElement(testElement);
168
+
169
+ const currentBreakpoint = containerBreakpoints.getCurrentBreakpoint(id);
170
+ expect(currentBreakpoint).toBe('md');
171
+ });
172
+ });
173
+
174
+ describe('CSS Class Management', () => {
175
+ it('should add breakpoint CSS classes', () => {
176
+ const breakpoints: BreakpointMap = {
177
+ sm: '300px',
178
+ md: '500px'
179
+ };
180
+
181
+ containerBreakpoints.register(testElement, breakpoints);
182
+
183
+ expect(testElement.classList.contains('proteus-sm')).toBe(true);
184
+ expect(testElement.getAttribute('data-proteus-breakpoint')).toBe('sm');
185
+ });
186
+
187
+ it('should update CSS classes when breakpoint changes', () => {
188
+ const breakpoints: BreakpointMap = {
189
+ sm: '300px',
190
+ md: '500px'
191
+ };
192
+
193
+ containerBreakpoints.register(testElement, breakpoints);
194
+
195
+ // Initially should have 'sm' class
196
+ expect(testElement.classList.contains('proteus-sm')).toBe(true);
197
+
198
+ // Simulate resize to trigger 'md' breakpoint
199
+ vi.spyOn(testElement, 'getBoundingClientRect').mockReturnValue({
200
+ width: 600,
201
+ height: 300,
202
+ top: 0,
203
+ left: 0,
204
+ bottom: 300,
205
+ right: 600,
206
+ x: 0,
207
+ y: 0,
208
+ toJSON: () => ({})
209
+ } as DOMRect);
210
+
211
+ containerBreakpoints.updateElement(testElement);
212
+
213
+ expect(testElement.classList.contains('proteus-sm')).toBe(false);
214
+ expect(testElement.classList.contains('proteus-md')).toBe(true);
215
+ expect(testElement.getAttribute('data-proteus-breakpoint')).toBe('md');
216
+ });
217
+
218
+ it('should remove CSS classes when unregistering', () => {
219
+ const breakpoints: BreakpointMap = {
220
+ sm: '300px',
221
+ md: '500px'
222
+ };
223
+
224
+ const id = containerBreakpoints.register(testElement, breakpoints);
225
+
226
+ expect(testElement.classList.contains('proteus-sm')).toBe(true);
227
+
228
+ containerBreakpoints.unregister(id);
229
+
230
+ expect(testElement.classList.contains('proteus-sm')).toBe(false);
231
+ expect(testElement.hasAttribute('data-proteus-breakpoint')).toBe(false);
232
+ });
233
+ });
234
+
235
+ describe('Event Dispatching', () => {
236
+ it('should dispatch custom events on breakpoint change', () => {
237
+ const eventSpy = vi.fn();
238
+ testElement.addEventListener('proteus:breakpoint-change', eventSpy);
239
+
240
+ const breakpoints: BreakpointMap = {
241
+ sm: '300px',
242
+ md: '500px'
243
+ };
244
+
245
+ containerBreakpoints.register(testElement, breakpoints);
246
+
247
+ expect(eventSpy).toHaveBeenCalledWith(
248
+ expect.objectContaining({
249
+ type: 'proteus:breakpoint-change',
250
+ detail: expect.objectContaining({
251
+ breakpoint: 'sm',
252
+ width: 400,
253
+ height: 300
254
+ })
255
+ })
256
+ );
257
+ });
258
+ });
259
+
260
+ describe('Breakpoint Value Parsing', () => {
261
+ it('should parse pixel values correctly', () => {
262
+ const breakpoints: BreakpointMap = {
263
+ sm: '300px',
264
+ md: '500px'
265
+ };
266
+
267
+ const id = containerBreakpoints.register(testElement, breakpoints);
268
+ const config = containerBreakpoints['breakpoints'].get(id);
269
+
270
+ expect(config?.parsedBreakpoints).toEqual([
271
+ { name: 'sm', value: 300, unit: 'px', originalValue: '300px' },
272
+ { name: 'md', value: 500, unit: 'px', originalValue: '500px' }
273
+ ]);
274
+ });
275
+
276
+ it('should parse em values correctly', () => {
277
+ const breakpoints: BreakpointMap = {
278
+ sm: '20em',
279
+ md: '30em'
280
+ };
281
+
282
+ const id = containerBreakpoints.register(testElement, breakpoints);
283
+ const config = containerBreakpoints['breakpoints'].get(id);
284
+
285
+ // em values should be converted to pixels (assuming 16px base)
286
+ expect(config?.parsedBreakpoints[0].value).toBe(320); // 20 * 16
287
+ expect(config?.parsedBreakpoints[1].value).toBe(480); // 30 * 16
288
+ });
289
+
290
+ it('should handle unitless values as pixels', () => {
291
+ const breakpoints: BreakpointMap = {
292
+ sm: '300',
293
+ md: '500'
294
+ };
295
+
296
+ const id = containerBreakpoints.register(testElement, breakpoints);
297
+ const config = containerBreakpoints['breakpoints'].get(id);
298
+
299
+ expect(config?.parsedBreakpoints[0].value).toBe(300);
300
+ expect(config?.parsedBreakpoints[0].unit).toBe('px');
301
+ });
302
+ });
303
+
304
+ describe('Management and Metrics', () => {
305
+ it('should provide performance metrics', () => {
306
+ const breakpoints: BreakpointMap = {
307
+ sm: '300px',
308
+ md: '500px',
309
+ lg: '800px'
310
+ };
311
+
312
+ containerBreakpoints.register(testElement, breakpoints);
313
+
314
+ const metrics = containerBreakpoints.getMetrics();
315
+
316
+ expect(metrics.totalRegistrations).toBe(1);
317
+ expect(metrics.activeElements).toBe(1);
318
+ expect(metrics.averageBreakpoints).toBe(3);
319
+ expect(metrics.breakpointDistribution).toEqual({
320
+ sm: 1,
321
+ md: 1,
322
+ lg: 1
323
+ });
324
+ });
325
+
326
+ it('should get all active breakpoints', () => {
327
+ const breakpoints: BreakpointMap = {
328
+ sm: '300px',
329
+ md: '500px'
330
+ };
331
+
332
+ containerBreakpoints.register(testElement, breakpoints);
333
+
334
+ const activeBreakpoints = containerBreakpoints.getAllActiveBreakpoints();
335
+
336
+ expect(activeBreakpoints).toHaveLength(1);
337
+ expect(activeBreakpoints[0]).toEqual(
338
+ expect.objectContaining({
339
+ element: testElement,
340
+ breakpoint: 'sm',
341
+ width: 400,
342
+ height: 300
343
+ })
344
+ );
345
+ });
346
+
347
+ it('should unregister all breakpoints for an element', () => {
348
+ const breakpoints1: BreakpointMap = { sm: '300px' };
349
+ const breakpoints2: BreakpointMap = { md: '500px' };
350
+
351
+ containerBreakpoints.register(testElement, breakpoints1);
352
+ containerBreakpoints.register(testElement, breakpoints2);
353
+
354
+ expect(containerBreakpoints.getMetrics().totalRegistrations).toBe(2);
355
+
356
+ containerBreakpoints.unregisterElement(testElement);
357
+
358
+ expect(containerBreakpoints.getMetrics().totalRegistrations).toBe(0);
359
+ });
360
+
361
+ it('should update all breakpoints at once', () => {
362
+ const element1 = document.createElement('div');
363
+ const element2 = document.createElement('div');
364
+ document.body.appendChild(element1);
365
+ document.body.appendChild(element2);
366
+
367
+ const callback1 = vi.fn();
368
+ const callback2 = vi.fn();
369
+
370
+ containerBreakpoints.register(element1, { sm: '300px' }, callback1);
371
+ containerBreakpoints.register(element2, { md: '500px' }, callback2);
372
+
373
+ // Clear previous calls
374
+ callback1.mockClear();
375
+ callback2.mockClear();
376
+
377
+ containerBreakpoints.updateAll();
378
+
379
+ // Both callbacks should be called during update
380
+ expect(callback1).toHaveBeenCalled();
381
+ expect(callback2).toHaveBeenCalled();
382
+ });
383
+ });
384
+
385
+ describe('Error Handling', () => {
386
+ it('should handle cleanup properly', () => {
387
+ const breakpoints: BreakpointMap = {
388
+ sm: '300px',
389
+ md: '500px'
390
+ };
391
+
392
+ containerBreakpoints.register(testElement, breakpoints);
393
+
394
+ expect(() => {
395
+ containerBreakpoints.destroy();
396
+ }).not.toThrow();
397
+
398
+ // CSS classes should be removed
399
+ expect(testElement.classList.contains('proteus-sm')).toBe(false);
400
+ });
401
+
402
+ it('should handle invalid registration IDs gracefully', () => {
403
+ const currentBreakpoint = containerBreakpoints.getCurrentBreakpoint('invalid-id');
404
+ expect(currentBreakpoint).toBeNull();
405
+
406
+ expect(() => {
407
+ containerBreakpoints.unregister('invalid-id');
408
+ }).not.toThrow();
409
+ });
410
+ });
411
+ });
@@ -0,0 +1,281 @@
1
+ /**
2
+ * SmartContainers Test Suite
3
+ * Comprehensive tests for container detection and management
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
7
+ import { SmartContainers, ContainerInfo } from '../SmartContainers';
8
+
9
+ // Mock ResizeObserver
10
+ global.ResizeObserver = vi.fn().mockImplementation((callback) => ({
11
+ observe: vi.fn(),
12
+ unobserve: vi.fn(),
13
+ disconnect: vi.fn(),
14
+ }));
15
+
16
+ // Mock MutationObserver
17
+ global.MutationObserver = vi.fn().mockImplementation((callback) => ({
18
+ observe: vi.fn(),
19
+ disconnect: vi.fn(),
20
+ }));
21
+
22
+ describe('SmartContainers', () => {
23
+ let smartContainers: SmartContainers;
24
+ let testContainer: HTMLElement;
25
+
26
+ beforeEach(() => {
27
+ // Clean up DOM
28
+ document.body.innerHTML = '';
29
+
30
+ // Create test container
31
+ testContainer = document.createElement('div');
32
+ testContainer.id = 'test-container';
33
+ testContainer.style.width = '400px';
34
+ testContainer.style.height = '300px';
35
+ testContainer.style.display = 'block';
36
+ document.body.appendChild(testContainer);
37
+
38
+ smartContainers = new SmartContainers();
39
+ });
40
+
41
+ afterEach(() => {
42
+ smartContainers.destroy();
43
+ document.body.innerHTML = '';
44
+ });
45
+
46
+ describe('Container Detection', () => {
47
+ it('should detect basic block containers', async () => {
48
+ const containers = await smartContainers.detectContainers();
49
+
50
+ expect(containers.length).toBeGreaterThan(0);
51
+ const testContainerInfo = containers.find(c => c.element === testContainer);
52
+ expect(testContainerInfo).toBeDefined();
53
+ expect(testContainerInfo?.type).toBe('block');
54
+ });
55
+
56
+ it('should detect grid containers', async () => {
57
+ testContainer.style.display = 'grid';
58
+
59
+ const containers = await smartContainers.detectContainers();
60
+ const gridContainer = containers.find(c => c.element === testContainer);
61
+
62
+ expect(gridContainer?.type).toBe('grid');
63
+ expect(gridContainer?.confidence).toBeGreaterThan(0.4);
64
+ });
65
+
66
+ it('should detect flex containers', async () => {
67
+ testContainer.style.display = 'flex';
68
+
69
+ const containers = await smartContainers.detectContainers();
70
+ const flexContainer = containers.find(c => c.element === testContainer);
71
+
72
+ expect(flexContainer?.type).toBe('flex');
73
+ expect(flexContainer?.confidence).toBeGreaterThan(0.3);
74
+ });
75
+
76
+ it('should exclude small elements', async () => {
77
+ const smallElement = document.createElement('div');
78
+ smallElement.style.width = '10px';
79
+ smallElement.style.height = '10px';
80
+ document.body.appendChild(smallElement);
81
+
82
+ const containers = await smartContainers.detectContainers();
83
+ const smallContainer = containers.find(c => c.element === smallElement);
84
+
85
+ expect(smallContainer).toBeUndefined();
86
+ });
87
+
88
+ it('should respect confidence threshold', async () => {
89
+ const containers = await smartContainers.detectContainers({ minConfidence: 0.8 });
90
+
91
+ // Should have fewer containers with high confidence threshold
92
+ expect(containers.length).toBeLessThanOrEqual(
93
+ (await smartContainers.detectContainers({ minConfidence: 0.1 })).length
94
+ );
95
+ });
96
+ });
97
+
98
+ describe('Container Units', () => {
99
+ beforeEach(() => {
100
+ // Mock getBoundingClientRect
101
+ vi.spyOn(testContainer, 'getBoundingClientRect').mockReturnValue({
102
+ width: 400,
103
+ height: 300,
104
+ top: 0,
105
+ left: 0,
106
+ bottom: 300,
107
+ right: 400,
108
+ x: 0,
109
+ y: 0,
110
+ toJSON: () => ({})
111
+ } as DOMRect);
112
+ });
113
+
114
+ it('should calculate container width units (cw)', () => {
115
+ const unit = smartContainers.calculateContainerUnit(testContainer, 'cw');
116
+ expect(unit).toBe(4); // 400px / 100 = 4px per cw
117
+ });
118
+
119
+ it('should calculate container height units (ch)', () => {
120
+ const unit = smartContainers.calculateContainerUnit(testContainer, 'ch');
121
+ expect(unit).toBe(3); // 300px / 100 = 3px per ch
122
+ });
123
+
124
+ it('should calculate container minimum units (cmin)', () => {
125
+ const unit = smartContainers.calculateContainerUnit(testContainer, 'cmin');
126
+ expect(unit).toBe(3); // min(400, 300) / 100 = 3px per cmin
127
+ });
128
+
129
+ it('should calculate container maximum units (cmax)', () => {
130
+ const unit = smartContainers.calculateContainerUnit(testContainer, 'cmax');
131
+ expect(unit).toBe(4); // max(400, 300) / 100 = 4px per cmax
132
+ });
133
+
134
+ it('should handle unknown units gracefully', () => {
135
+ const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
136
+ const unit = smartContainers.calculateContainerUnit(testContainer, 'unknown');
137
+
138
+ expect(unit).toBe(0);
139
+ expect(consoleSpy).toHaveBeenCalledWith(
140
+ expect.stringContaining('Unknown container unit "unknown"')
141
+ );
142
+
143
+ consoleSpy.mockRestore();
144
+ });
145
+ });
146
+
147
+ describe('Container Management', () => {
148
+ it('should track container information', async () => {
149
+ await smartContainers.detectContainers();
150
+
151
+ const containerInfo = smartContainers.getContainerInfo(testContainer);
152
+ expect(containerInfo).toBeDefined();
153
+ expect(containerInfo?.element).toBe(testContainer);
154
+ });
155
+
156
+ it('should update container information', async () => {
157
+ await smartContainers.detectContainers();
158
+
159
+ // Change container style
160
+ testContainer.style.display = 'grid';
161
+
162
+ const updatedInfo = smartContainers.updateContainer(testContainer);
163
+ expect(updatedInfo?.type).toBe('grid');
164
+ });
165
+
166
+ it('should start and stop monitoring', () => {
167
+ smartContainers.startMonitoring();
168
+ expect(smartContainers['isActive']).toBe(true);
169
+
170
+ smartContainers.stopMonitoring();
171
+ expect(smartContainers['isActive']).toBe(false);
172
+ });
173
+ });
174
+
175
+ describe('Responsive Content Detection', () => {
176
+ it('should detect responsive images', async () => {
177
+ const img = document.createElement('img');
178
+ img.srcset = 'image-320w.jpg 320w, image-640w.jpg 640w';
179
+ testContainer.appendChild(img);
180
+
181
+ const containers = await smartContainers.detectContainers();
182
+ const containerInfo = containers.find(c => c.element === testContainer);
183
+
184
+ expect(containerInfo?.hasResponsiveContent).toBe(true);
185
+ });
186
+
187
+ it('should detect responsive videos', async () => {
188
+ const iframe = document.createElement('iframe');
189
+ iframe.src = 'https://www.youtube.com/embed/test';
190
+ testContainer.appendChild(iframe);
191
+
192
+ const containers = await smartContainers.detectContainers();
193
+ const containerInfo = containers.find(c => c.element === testContainer);
194
+
195
+ expect(containerInfo?.hasResponsiveContent).toBe(true);
196
+ });
197
+
198
+ it('should detect grid/flex children', async () => {
199
+ const child = document.createElement('div');
200
+ child.style.display = 'grid';
201
+ testContainer.appendChild(child);
202
+
203
+ const containers = await smartContainers.detectContainers();
204
+ const containerInfo = containers.find(c => c.element === testContainer);
205
+
206
+ expect(containerInfo?.hasResponsiveContent).toBe(true);
207
+ });
208
+ });
209
+
210
+ describe('Metrics and Analysis', () => {
211
+ it('should provide performance metrics', async () => {
212
+ await smartContainers.detectContainers();
213
+
214
+ const metrics = smartContainers.getMetrics();
215
+
216
+ expect(metrics.totalContainers).toBeGreaterThan(0);
217
+ expect(metrics.averageConfidence).toBeGreaterThan(0);
218
+ expect(metrics.typeDistribution).toBeDefined();
219
+ });
220
+
221
+ it('should filter containers by type', async () => {
222
+ // Create different container types
223
+ const gridContainer = document.createElement('div');
224
+ gridContainer.style.display = 'grid';
225
+ gridContainer.style.width = '200px';
226
+ gridContainer.style.height = '200px';
227
+ document.body.appendChild(gridContainer);
228
+
229
+ await smartContainers.detectContainers();
230
+
231
+ const gridContainers = smartContainers.getContainersByType('grid');
232
+ const blockContainers = smartContainers.getContainersByType('block');
233
+
234
+ expect(gridContainers.length).toBeGreaterThan(0);
235
+ expect(blockContainers.length).toBeGreaterThan(0);
236
+ expect(gridContainers.every(c => c.type === 'grid')).toBe(true);
237
+ expect(blockContainers.every(c => c.type === 'block')).toBe(true);
238
+ });
239
+
240
+ it('should filter high-confidence containers', async () => {
241
+ await smartContainers.detectContainers();
242
+
243
+ const highConfidenceContainers = smartContainers.getHighConfidenceContainers(0.5);
244
+
245
+ expect(highConfidenceContainers.every(c => c.confidence >= 0.5)).toBe(true);
246
+ });
247
+
248
+ it('should refresh containers', async () => {
249
+ const initialContainers = await smartContainers.detectContainers();
250
+
251
+ // Add new container
252
+ const newContainer = document.createElement('div');
253
+ newContainer.style.width = '300px';
254
+ newContainer.style.height = '200px';
255
+ newContainer.style.display = 'flex';
256
+ document.body.appendChild(newContainer);
257
+
258
+ const refreshedContainers = await smartContainers.refreshContainers();
259
+
260
+ expect(refreshedContainers.length).toBeGreaterThan(initialContainers.length);
261
+ });
262
+ });
263
+
264
+ describe('Error Handling', () => {
265
+ it('should handle missing elements gracefully', () => {
266
+ const nonExistentElement = document.createElement('div');
267
+
268
+ const containerInfo = smartContainers.getContainerInfo(nonExistentElement);
269
+ expect(containerInfo).toBeNull();
270
+
271
+ const updatedInfo = smartContainers.updateContainer(nonExistentElement);
272
+ expect(updatedInfo).toBeNull();
273
+ });
274
+
275
+ it('should handle cleanup properly', () => {
276
+ expect(() => {
277
+ smartContainers.destroy();
278
+ }).not.toThrow();
279
+ });
280
+ });
281
+ });