@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.
- package/API.md +438 -0
- package/FEATURES.md +286 -0
- package/LICENSE +21 -0
- package/README.md +645 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/proteus.cjs.js +16014 -0
- package/dist/proteus.cjs.js.map +1 -0
- package/dist/proteus.d.ts +3018 -0
- package/dist/proteus.esm.js +16005 -0
- package/dist/proteus.esm.js.map +1 -0
- package/dist/proteus.esm.min.js +8 -0
- package/dist/proteus.esm.min.js.map +1 -0
- package/dist/proteus.js +16020 -0
- package/dist/proteus.js.map +1 -0
- package/dist/proteus.min.js +8 -0
- package/dist/proteus.min.js.map +1 -0
- package/package.json +98 -0
- package/src/__tests__/mvp-integration.test.ts +518 -0
- package/src/accessibility/AccessibilityEngine.ts +2106 -0
- package/src/accessibility/ScreenReaderSupport.ts +444 -0
- package/src/accessibility/__tests__/ScreenReaderSupport.test.ts +435 -0
- package/src/animations/FLIPAnimationSystem.ts +491 -0
- package/src/compatibility/BrowserCompatibility.ts +1076 -0
- package/src/containers/BreakpointSystem.ts +347 -0
- package/src/containers/ContainerBreakpoints.ts +726 -0
- package/src/containers/ContainerManager.ts +370 -0
- package/src/containers/ContainerUnits.ts +336 -0
- package/src/containers/ContextIsolation.ts +394 -0
- package/src/containers/ElementQueries.ts +411 -0
- package/src/containers/SmartContainer.ts +536 -0
- package/src/containers/SmartContainers.ts +376 -0
- package/src/containers/__tests__/ContainerBreakpoints.test.ts +411 -0
- package/src/containers/__tests__/SmartContainers.test.ts +281 -0
- package/src/content/ResponsiveImages.ts +570 -0
- package/src/core/EventSystem.ts +147 -0
- package/src/core/MemoryManager.ts +321 -0
- package/src/core/PerformanceMonitor.ts +238 -0
- package/src/core/PluginSystem.ts +275 -0
- package/src/core/ProteusJS.test.ts +164 -0
- package/src/core/ProteusJS.ts +962 -0
- package/src/developer/PerformanceProfiler.ts +567 -0
- package/src/developer/VisualDebuggingTools.ts +656 -0
- package/src/developer/ZeroConfigSystem.ts +593 -0
- package/src/index.ts +35 -0
- package/src/integration.test.ts +227 -0
- package/src/layout/AdaptiveGrid.ts +429 -0
- package/src/layout/ContentReordering.ts +532 -0
- package/src/layout/FlexboxEnhancer.ts +406 -0
- package/src/layout/FlowLayout.ts +545 -0
- package/src/layout/SpacingSystem.ts +512 -0
- package/src/observers/IntersectionObserverPolyfill.ts +289 -0
- package/src/observers/ObserverManager.ts +299 -0
- package/src/observers/ResizeObserverPolyfill.ts +179 -0
- package/src/performance/BatchDOMOperations.ts +519 -0
- package/src/performance/CSSOptimizationEngine.ts +646 -0
- package/src/performance/CacheOptimizationSystem.ts +601 -0
- package/src/performance/EfficientEventHandler.ts +740 -0
- package/src/performance/LazyEvaluationSystem.ts +532 -0
- package/src/performance/MemoryManagementSystem.ts +497 -0
- package/src/performance/PerformanceMonitor.ts +931 -0
- package/src/performance/__tests__/BatchDOMOperations.test.ts +309 -0
- package/src/performance/__tests__/EfficientEventHandler.test.ts +268 -0
- package/src/performance/__tests__/PerformanceMonitor.test.ts +422 -0
- package/src/polyfills/BrowserPolyfills.ts +586 -0
- package/src/polyfills/__tests__/BrowserPolyfills.test.ts +328 -0
- package/src/test/setup.ts +115 -0
- package/src/theming/SmartThemeSystem.ts +591 -0
- package/src/types/index.ts +134 -0
- package/src/typography/ClampScaling.ts +356 -0
- package/src/typography/FluidTypography.ts +759 -0
- package/src/typography/LineHeightOptimization.ts +430 -0
- package/src/typography/LineHeightOptimizer.ts +326 -0
- package/src/typography/TextFitting.ts +355 -0
- package/src/typography/TypographicScale.ts +428 -0
- package/src/typography/VerticalRhythm.ts +369 -0
- package/src/typography/__tests__/FluidTypography.test.ts +432 -0
- package/src/typography/__tests__/LineHeightOptimization.test.ts +436 -0
- package/src/utils/Logger.ts +173 -0
- package/src/utils/debounce.ts +259 -0
- package/src/utils/performance.ts +371 -0
- package/src/utils/support.ts +106 -0
- package/src/utils/version.ts +24 -0
@@ -0,0 +1,435 @@
|
|
1
|
+
/**
|
2
|
+
* ScreenReaderSupport Test Suite
|
3
|
+
* Comprehensive tests for screen reader compatibility
|
4
|
+
*/
|
5
|
+
|
6
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
7
|
+
import { ScreenReaderSupport, AnnouncementConfig, LiveRegionConfig, AriaLabelConfig } from '../ScreenReaderSupport';
|
8
|
+
|
9
|
+
describe('ScreenReaderSupport', () => {
|
10
|
+
let screenReaderSupport: ScreenReaderSupport;
|
11
|
+
|
12
|
+
beforeEach(() => {
|
13
|
+
// Clean up DOM
|
14
|
+
document.body.innerHTML = '';
|
15
|
+
|
16
|
+
screenReaderSupport = new ScreenReaderSupport();
|
17
|
+
});
|
18
|
+
|
19
|
+
afterEach(() => {
|
20
|
+
screenReaderSupport.destroy();
|
21
|
+
document.body.innerHTML = '';
|
22
|
+
vi.restoreAllMocks();
|
23
|
+
});
|
24
|
+
|
25
|
+
describe('Initialization', () => {
|
26
|
+
it('should initialize with default live regions', () => {
|
27
|
+
const statusRegion = document.getElementById('proteus-live-status');
|
28
|
+
const alertRegion = document.getElementById('proteus-live-alert');
|
29
|
+
|
30
|
+
expect(statusRegion).toBeTruthy();
|
31
|
+
expect(alertRegion).toBeTruthy();
|
32
|
+
expect(statusRegion?.getAttribute('aria-live')).toBe('polite');
|
33
|
+
expect(alertRegion?.getAttribute('aria-live')).toBe('assertive');
|
34
|
+
});
|
35
|
+
|
36
|
+
it('should detect keyboard navigation', () => {
|
37
|
+
const keydownEvent = new KeyboardEvent('keydown', { key: 'Tab' });
|
38
|
+
document.dispatchEvent(keydownEvent);
|
39
|
+
|
40
|
+
expect(document.body.classList.contains('proteus-keyboard-user')).toBe(true);
|
41
|
+
|
42
|
+
const mousedownEvent = new MouseEvent('mousedown');
|
43
|
+
document.dispatchEvent(mousedownEvent);
|
44
|
+
|
45
|
+
expect(document.body.classList.contains('proteus-keyboard-user')).toBe(false);
|
46
|
+
});
|
47
|
+
});
|
48
|
+
|
49
|
+
describe('Announcements', () => {
|
50
|
+
it('should announce messages to screen readers', () => {
|
51
|
+
const message = 'Test announcement';
|
52
|
+
|
53
|
+
screenReaderSupport.announce(message);
|
54
|
+
|
55
|
+
const statusRegion = document.getElementById('proteus-live-status');
|
56
|
+
|
57
|
+
// Wait for async processing
|
58
|
+
setTimeout(() => {
|
59
|
+
expect(statusRegion?.textContent).toBe(message);
|
60
|
+
}, 100);
|
61
|
+
});
|
62
|
+
|
63
|
+
it('should handle assertive announcements', () => {
|
64
|
+
const message = 'Urgent announcement';
|
65
|
+
const config: AnnouncementConfig = { priority: 'assertive' };
|
66
|
+
|
67
|
+
screenReaderSupport.announce(message, config);
|
68
|
+
|
69
|
+
const alertRegion = document.getElementById('proteus-live-alert');
|
70
|
+
|
71
|
+
setTimeout(() => {
|
72
|
+
expect(alertRegion?.textContent).toBe(message);
|
73
|
+
}, 100);
|
74
|
+
});
|
75
|
+
|
76
|
+
it('should prevent duplicate announcements', () => {
|
77
|
+
const message = 'Duplicate test';
|
78
|
+
|
79
|
+
screenReaderSupport.announce(message);
|
80
|
+
screenReaderSupport.announce(message); // Should be ignored
|
81
|
+
|
82
|
+
const statusRegion = document.getElementById('proteus-live-status');
|
83
|
+
|
84
|
+
setTimeout(() => {
|
85
|
+
expect(statusRegion?.textContent).toBe(message);
|
86
|
+
}, 100);
|
87
|
+
});
|
88
|
+
|
89
|
+
it('should handle announcement interruption', () => {
|
90
|
+
screenReaderSupport.announce('First message');
|
91
|
+
screenReaderSupport.announce('Second message', { priority: 'polite', interrupt: true });
|
92
|
+
|
93
|
+
const statusRegion = document.getElementById('proteus-live-status');
|
94
|
+
|
95
|
+
setTimeout(() => {
|
96
|
+
expect(statusRegion?.textContent).toBe('Second message');
|
97
|
+
}, 100);
|
98
|
+
});
|
99
|
+
|
100
|
+
it('should ignore empty announcements', () => {
|
101
|
+
const statusRegion = document.getElementById('proteus-live-status');
|
102
|
+
const initialContent = statusRegion?.textContent;
|
103
|
+
|
104
|
+
screenReaderSupport.announce('');
|
105
|
+
screenReaderSupport.announce(' ');
|
106
|
+
|
107
|
+
expect(statusRegion?.textContent).toBe(initialContent);
|
108
|
+
});
|
109
|
+
});
|
110
|
+
|
111
|
+
describe('Live Regions', () => {
|
112
|
+
it('should create custom live regions', () => {
|
113
|
+
const config: LiveRegionConfig = {
|
114
|
+
type: 'status',
|
115
|
+
atomic: true,
|
116
|
+
relevant: 'additions',
|
117
|
+
busy: false
|
118
|
+
};
|
119
|
+
|
120
|
+
const region = screenReaderSupport.createLiveRegion('custom', config);
|
121
|
+
|
122
|
+
expect(region.id).toBe('proteus-live-custom');
|
123
|
+
expect(region.getAttribute('aria-live')).toBe('polite');
|
124
|
+
expect(region.getAttribute('aria-atomic')).toBe('true');
|
125
|
+
expect(region.getAttribute('aria-relevant')).toBe('additions');
|
126
|
+
expect(region.getAttribute('aria-busy')).toBe('false');
|
127
|
+
});
|
128
|
+
|
129
|
+
it('should update live region content', () => {
|
130
|
+
const region = screenReaderSupport.createLiveRegion('test', {
|
131
|
+
type: 'status',
|
132
|
+
atomic: false,
|
133
|
+
relevant: 'additions',
|
134
|
+
busy: false
|
135
|
+
});
|
136
|
+
|
137
|
+
screenReaderSupport.updateLiveRegion('test', 'Updated content');
|
138
|
+
|
139
|
+
expect(region.textContent).toBe('Updated content');
|
140
|
+
});
|
141
|
+
|
142
|
+
it('should handle different live region types', () => {
|
143
|
+
const alertRegion = screenReaderSupport.createLiveRegion('alert-test', {
|
144
|
+
type: 'alert',
|
145
|
+
atomic: true,
|
146
|
+
relevant: 'all',
|
147
|
+
busy: false
|
148
|
+
});
|
149
|
+
|
150
|
+
const logRegion = screenReaderSupport.createLiveRegion('log-test', {
|
151
|
+
type: 'log',
|
152
|
+
atomic: false,
|
153
|
+
relevant: 'additions',
|
154
|
+
busy: false
|
155
|
+
});
|
156
|
+
|
157
|
+
expect(alertRegion.getAttribute('aria-live')).toBe('assertive');
|
158
|
+
expect(alertRegion.getAttribute('role')).toBe('alert');
|
159
|
+
expect(logRegion.getAttribute('aria-live')).toBe('polite');
|
160
|
+
expect(logRegion.getAttribute('role')).toBe('status');
|
161
|
+
});
|
162
|
+
});
|
163
|
+
|
164
|
+
describe('ARIA Labels', () => {
|
165
|
+
let testElement: HTMLElement;
|
166
|
+
|
167
|
+
beforeEach(() => {
|
168
|
+
testElement = document.createElement('div');
|
169
|
+
document.body.appendChild(testElement);
|
170
|
+
});
|
171
|
+
|
172
|
+
it('should apply basic ARIA labels', () => {
|
173
|
+
const config: AriaLabelConfig = {
|
174
|
+
label: 'Test label',
|
175
|
+
role: 'button'
|
176
|
+
};
|
177
|
+
|
178
|
+
screenReaderSupport.applyAriaLabels(testElement, config);
|
179
|
+
|
180
|
+
expect(testElement.getAttribute('aria-label')).toBe('Test label');
|
181
|
+
expect(testElement.getAttribute('role')).toBe('button');
|
182
|
+
});
|
183
|
+
|
184
|
+
it('should apply state attributes', () => {
|
185
|
+
const config: AriaLabelConfig = {
|
186
|
+
expanded: true,
|
187
|
+
selected: false,
|
188
|
+
disabled: true,
|
189
|
+
hidden: false
|
190
|
+
};
|
191
|
+
|
192
|
+
screenReaderSupport.applyAriaLabels(testElement, config);
|
193
|
+
|
194
|
+
expect(testElement.getAttribute('aria-expanded')).toBe('true');
|
195
|
+
expect(testElement.getAttribute('aria-selected')).toBe('false');
|
196
|
+
expect(testElement.getAttribute('aria-disabled')).toBe('true');
|
197
|
+
expect(testElement.getAttribute('aria-hidden')).toBe('false');
|
198
|
+
});
|
199
|
+
|
200
|
+
it('should apply relationship attributes', () => {
|
201
|
+
const labelElement = document.createElement('label');
|
202
|
+
labelElement.id = 'test-label';
|
203
|
+
document.body.appendChild(labelElement);
|
204
|
+
|
205
|
+
const descElement = document.createElement('div');
|
206
|
+
descElement.id = 'test-desc';
|
207
|
+
document.body.appendChild(descElement);
|
208
|
+
|
209
|
+
const config: AriaLabelConfig = {
|
210
|
+
labelledBy: 'test-label',
|
211
|
+
describedBy: 'test-desc'
|
212
|
+
};
|
213
|
+
|
214
|
+
screenReaderSupport.applyAriaLabels(testElement, config);
|
215
|
+
|
216
|
+
expect(testElement.getAttribute('aria-labelledby')).toBe('test-label');
|
217
|
+
expect(testElement.getAttribute('aria-describedby')).toBe('test-desc');
|
218
|
+
});
|
219
|
+
});
|
220
|
+
|
221
|
+
describe('Container Announcements', () => {
|
222
|
+
it('should announce container changes', () => {
|
223
|
+
const container = document.createElement('div');
|
224
|
+
container.setAttribute('aria-label', 'Product grid');
|
225
|
+
|
226
|
+
screenReaderSupport.announceContainerChange(container, 'tablet', 768);
|
227
|
+
|
228
|
+
setTimeout(() => {
|
229
|
+
const statusRegion = document.getElementById('proteus-live-status');
|
230
|
+
expect(statusRegion?.textContent).toContain('Product grid');
|
231
|
+
expect(statusRegion?.textContent).toContain('tablet breakpoint');
|
232
|
+
expect(statusRegion?.textContent).toContain('768 pixels');
|
233
|
+
}, 600);
|
234
|
+
});
|
235
|
+
|
236
|
+
it('should announce typography changes', () => {
|
237
|
+
const textElement = document.createElement('p');
|
238
|
+
textElement.textContent = 'Sample text';
|
239
|
+
|
240
|
+
screenReaderSupport.announceTypographyChange(textElement, '18px');
|
241
|
+
|
242
|
+
setTimeout(() => {
|
243
|
+
const statusRegion = document.getElementById('proteus-live-status');
|
244
|
+
expect(statusRegion?.textContent).toContain('text size adjusted');
|
245
|
+
expect(statusRegion?.textContent).toContain('18px');
|
246
|
+
}, 400);
|
247
|
+
});
|
248
|
+
});
|
249
|
+
|
250
|
+
describe('Skip Links', () => {
|
251
|
+
it('should create skip links', () => {
|
252
|
+
const targets = [
|
253
|
+
{ id: 'main-content', label: 'Skip to main content' },
|
254
|
+
{ id: 'navigation', label: 'Skip to navigation' }
|
255
|
+
];
|
256
|
+
|
257
|
+
const skipContainer = screenReaderSupport.createSkipLinks(targets);
|
258
|
+
|
259
|
+
expect(skipContainer.getAttribute('role')).toBe('navigation');
|
260
|
+
expect(skipContainer.getAttribute('aria-label')).toBe('Skip links');
|
261
|
+
|
262
|
+
const links = skipContainer.querySelectorAll('a');
|
263
|
+
expect(links).toHaveLength(2);
|
264
|
+
expect(links[0].href).toContain('#main-content');
|
265
|
+
expect(links[0].textContent).toBe('Skip to main content');
|
266
|
+
expect(links[1].href).toContain('#navigation');
|
267
|
+
expect(links[1].textContent).toBe('Skip to navigation');
|
268
|
+
});
|
269
|
+
|
270
|
+
it('should position skip links correctly', () => {
|
271
|
+
const targets = [{ id: 'main', label: 'Skip to main' }];
|
272
|
+
const skipContainer = screenReaderSupport.createSkipLinks(targets);
|
273
|
+
const link = skipContainer.querySelector('a') as HTMLElement;
|
274
|
+
|
275
|
+
expect(link.style.position).toBe('absolute');
|
276
|
+
expect(link.style.top).toBe('-40px'); // Hidden by default
|
277
|
+
|
278
|
+
// Simulate focus
|
279
|
+
link.focus();
|
280
|
+
link.dispatchEvent(new FocusEvent('focus'));
|
281
|
+
|
282
|
+
expect(link.style.top).toBe('6px'); // Visible on focus
|
283
|
+
});
|
284
|
+
});
|
285
|
+
|
286
|
+
describe('Focus Management', () => {
|
287
|
+
it('should manage focus for dynamic content', () => {
|
288
|
+
const element = document.createElement('div');
|
289
|
+
element.textContent = 'Dynamic content';
|
290
|
+
document.body.appendChild(element);
|
291
|
+
|
292
|
+
const focusSpy = vi.spyOn(element, 'focus');
|
293
|
+
|
294
|
+
screenReaderSupport.manageFocus(element, 'Content updated');
|
295
|
+
|
296
|
+
expect(element.getAttribute('tabindex')).toBe('-1');
|
297
|
+
expect(focusSpy).toHaveBeenCalled();
|
298
|
+
|
299
|
+
setTimeout(() => {
|
300
|
+
const alertRegion = document.getElementById('proteus-live-alert');
|
301
|
+
expect(alertRegion?.textContent).toContain('Focus moved to');
|
302
|
+
expect(alertRegion?.textContent).toContain('Content updated');
|
303
|
+
}, 100);
|
304
|
+
});
|
305
|
+
|
306
|
+
it('should preserve existing tabindex', () => {
|
307
|
+
const element = document.createElement('button');
|
308
|
+
element.setAttribute('tabindex', '0');
|
309
|
+
document.body.appendChild(element);
|
310
|
+
|
311
|
+
screenReaderSupport.manageFocus(element, 'Button focused');
|
312
|
+
|
313
|
+
expect(element.getAttribute('tabindex')).toBe('0');
|
314
|
+
});
|
315
|
+
});
|
316
|
+
|
317
|
+
describe('Screen Reader Detection', () => {
|
318
|
+
it('should detect screen reader presence', () => {
|
319
|
+
// Mock screen reader indicators
|
320
|
+
Object.defineProperty(navigator, 'userAgent', {
|
321
|
+
value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) NVDA/2021.1',
|
322
|
+
configurable: true
|
323
|
+
});
|
324
|
+
|
325
|
+
const newSupport = new ScreenReaderSupport();
|
326
|
+
|
327
|
+
expect(newSupport.isScreenReaderActive()).toBe(true);
|
328
|
+
|
329
|
+
newSupport.destroy();
|
330
|
+
});
|
331
|
+
|
332
|
+
it('should handle missing screen reader gracefully', () => {
|
333
|
+
expect(screenReaderSupport.isScreenReaderActive()).toBeDefined();
|
334
|
+
});
|
335
|
+
});
|
336
|
+
|
337
|
+
describe('Element Description', () => {
|
338
|
+
it('should get element description from aria-label', () => {
|
339
|
+
const element = document.createElement('div');
|
340
|
+
element.setAttribute('aria-label', 'Custom label');
|
341
|
+
|
342
|
+
screenReaderSupport.announceContainerChange(element, 'md', 600);
|
343
|
+
|
344
|
+
setTimeout(() => {
|
345
|
+
const statusRegion = document.getElementById('proteus-live-status');
|
346
|
+
expect(statusRegion?.textContent).toContain('Custom label');
|
347
|
+
}, 600);
|
348
|
+
});
|
349
|
+
|
350
|
+
it('should get element description from aria-labelledby', () => {
|
351
|
+
const labelElement = document.createElement('span');
|
352
|
+
labelElement.id = 'element-label';
|
353
|
+
labelElement.textContent = 'Labeled element';
|
354
|
+
document.body.appendChild(labelElement);
|
355
|
+
|
356
|
+
const element = document.createElement('div');
|
357
|
+
element.setAttribute('aria-labelledby', 'element-label');
|
358
|
+
|
359
|
+
screenReaderSupport.announceContainerChange(element, 'lg', 800);
|
360
|
+
|
361
|
+
setTimeout(() => {
|
362
|
+
const statusRegion = document.getElementById('proteus-live-status');
|
363
|
+
expect(statusRegion?.textContent).toContain('Labeled element');
|
364
|
+
}, 600);
|
365
|
+
});
|
366
|
+
|
367
|
+
it('should fallback to text content', () => {
|
368
|
+
const element = document.createElement('div');
|
369
|
+
element.textContent = 'Element text';
|
370
|
+
|
371
|
+
screenReaderSupport.announceContainerChange(element, 'sm', 400);
|
372
|
+
|
373
|
+
setTimeout(() => {
|
374
|
+
const statusRegion = document.getElementById('proteus-live-status');
|
375
|
+
expect(statusRegion?.textContent).toContain('Element text');
|
376
|
+
}, 600);
|
377
|
+
});
|
378
|
+
|
379
|
+
it('should fallback to tag name and class', () => {
|
380
|
+
const element = document.createElement('section');
|
381
|
+
element.className = 'content-area';
|
382
|
+
|
383
|
+
screenReaderSupport.announceContainerChange(element, 'md', 600);
|
384
|
+
|
385
|
+
setTimeout(() => {
|
386
|
+
const statusRegion = document.getElementById('proteus-live-status');
|
387
|
+
expect(statusRegion?.textContent).toContain('section with class content-area');
|
388
|
+
}, 600);
|
389
|
+
});
|
390
|
+
});
|
391
|
+
|
392
|
+
describe('Cleanup', () => {
|
393
|
+
it('should clean up resources on destroy', () => {
|
394
|
+
const customRegion = screenReaderSupport.createLiveRegion('cleanup-test', {
|
395
|
+
type: 'status',
|
396
|
+
atomic: false,
|
397
|
+
relevant: 'additions',
|
398
|
+
busy: false
|
399
|
+
});
|
400
|
+
|
401
|
+
expect(document.getElementById('proteus-live-cleanup-test')).toBeTruthy();
|
402
|
+
|
403
|
+
screenReaderSupport.destroy();
|
404
|
+
|
405
|
+
expect(document.getElementById('proteus-live-status')).toBeNull();
|
406
|
+
expect(document.getElementById('proteus-live-alert')).toBeNull();
|
407
|
+
expect(document.getElementById('proteus-live-cleanup-test')).toBeNull();
|
408
|
+
});
|
409
|
+
|
410
|
+
it('should handle multiple destroy calls gracefully', () => {
|
411
|
+
expect(() => {
|
412
|
+
screenReaderSupport.destroy();
|
413
|
+
screenReaderSupport.destroy();
|
414
|
+
}).not.toThrow();
|
415
|
+
});
|
416
|
+
});
|
417
|
+
|
418
|
+
describe('Error Handling', () => {
|
419
|
+
it('should handle invalid live region updates', () => {
|
420
|
+
expect(() => {
|
421
|
+
screenReaderSupport.updateLiveRegion('non-existent', 'content');
|
422
|
+
}).not.toThrow();
|
423
|
+
});
|
424
|
+
|
425
|
+
it('should handle invalid elements in ARIA methods', () => {
|
426
|
+
expect(() => {
|
427
|
+
screenReaderSupport.applyAriaLabels(null as any, {});
|
428
|
+
}).not.toThrow();
|
429
|
+
|
430
|
+
expect(() => {
|
431
|
+
screenReaderSupport.manageFocus(null as any, 'test');
|
432
|
+
}).not.toThrow();
|
433
|
+
});
|
434
|
+
});
|
435
|
+
});
|