@rogieking/figui3 2.10.0 → 2.10.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.html DELETED
@@ -1,843 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>FigUI Component Tests</title>
7
- <link rel="stylesheet" href="fig.css">
8
- <style>
9
- :root {
10
- --figma-color-bg: #1e1e1e;
11
- --figma-color-bg-secondary: #2c2c2c;
12
- --figma-color-bg-tertiary: #383838;
13
- --figma-color-bg-hover: #444;
14
- --figma-color-text: #fff;
15
- --figma-color-text-secondary: #b3b3b3;
16
- --figma-color-text-tertiary: #808080;
17
- --figma-color-border: #444;
18
- --figma-color-border-selected: #0d99ff;
19
- --figma-color-bg-brand: #0d99ff;
20
- --figma-color-text-onbrand: #fff;
21
- --figma-color-bg-danger: #f24822;
22
- --figma-color-text-danger: #f24822;
23
- }
24
-
25
- * {
26
- box-sizing: border-box;
27
- }
28
-
29
- body {
30
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
31
- background: var(--figma-color-bg);
32
- color: var(--figma-color-text);
33
- padding: 2rem;
34
- margin: 0;
35
- }
36
-
37
- h1 {
38
- margin: 0 0 1rem;
39
- font-size: 1.5rem;
40
- }
41
-
42
- .test-summary {
43
- display: flex;
44
- gap: 1rem;
45
- margin-bottom: 1.5rem;
46
- padding: 1rem;
47
- background: var(--figma-color-bg-secondary);
48
- border-radius: 8px;
49
- }
50
-
51
- .test-stat {
52
- display: flex;
53
- flex-direction: column;
54
- align-items: center;
55
- padding: 0.5rem 1rem;
56
- }
57
-
58
- .test-stat-value {
59
- font-size: 2rem;
60
- font-weight: bold;
61
- }
62
-
63
- .test-stat-label {
64
- font-size: 0.75rem;
65
- color: var(--figma-color-text-secondary);
66
- text-transform: uppercase;
67
- }
68
-
69
- .test-stat.passed .test-stat-value { color: #30d158; }
70
- .test-stat.failed .test-stat-value { color: #f24822; }
71
- .test-stat.skipped .test-stat-value { color: #ff9f0a; }
72
-
73
- .test-group {
74
- margin-bottom: 1.5rem;
75
- }
76
-
77
- .test-group-header {
78
- display: flex;
79
- align-items: center;
80
- gap: 0.5rem;
81
- padding: 0.5rem;
82
- background: var(--figma-color-bg-tertiary);
83
- border-radius: 4px;
84
- cursor: pointer;
85
- user-select: none;
86
- }
87
-
88
- .test-group-header:hover {
89
- background: var(--figma-color-bg-hover);
90
- }
91
-
92
- .test-group-name {
93
- flex: 1;
94
- font-weight: 600;
95
- }
96
-
97
- .test-group-count {
98
- font-size: 0.875rem;
99
- color: var(--figma-color-text-secondary);
100
- }
101
-
102
- .test-group-status {
103
- width: 8px;
104
- height: 8px;
105
- border-radius: 50%;
106
- }
107
-
108
- .test-group-status.passed { background: #30d158; }
109
- .test-group-status.failed { background: #f24822; }
110
-
111
- .test-list {
112
- margin-top: 0.5rem;
113
- padding-left: 1rem;
114
- }
115
-
116
- .test-item {
117
- display: flex;
118
- align-items: center;
119
- gap: 0.5rem;
120
- padding: 0.25rem 0;
121
- font-size: 0.875rem;
122
- }
123
-
124
- .test-icon {
125
- width: 16px;
126
- height: 16px;
127
- display: flex;
128
- align-items: center;
129
- justify-content: center;
130
- font-size: 12px;
131
- }
132
-
133
- .test-icon.passed { color: #30d158; }
134
- .test-icon.failed { color: #f24822; }
135
- .test-icon.skipped { color: #ff9f0a; }
136
-
137
- .test-name {
138
- flex: 1;
139
- }
140
-
141
- .test-error {
142
- color: #f24822;
143
- font-size: 0.75rem;
144
- margin-left: 1.5rem;
145
- padding: 0.25rem 0.5rem;
146
- background: rgba(242, 72, 34, 0.1);
147
- border-radius: 4px;
148
- font-family: monospace;
149
- }
150
-
151
- .test-duration {
152
- color: var(--figma-color-text-tertiary);
153
- font-size: 0.75rem;
154
- }
155
-
156
- #test-container {
157
- position: fixed;
158
- top: -9999px;
159
- left: -9999px;
160
- visibility: hidden;
161
- }
162
-
163
- .run-button {
164
- background: var(--figma-color-bg-brand);
165
- color: var(--figma-color-text-onbrand);
166
- border: none;
167
- padding: 0.5rem 1rem;
168
- border-radius: 4px;
169
- cursor: pointer;
170
- font-size: 0.875rem;
171
- margin-bottom: 1rem;
172
- }
173
-
174
- .run-button:hover {
175
- filter: brightness(1.1);
176
- }
177
-
178
- .run-button:disabled {
179
- opacity: 0.5;
180
- cursor: not-allowed;
181
- }
182
- </style>
183
- </head>
184
- <body>
185
- <h1>FigUI Component Tests</h1>
186
-
187
- <button class="run-button" id="run-tests">Run Tests</button>
188
-
189
- <div class="test-summary" id="summary">
190
- <div class="test-stat passed">
191
- <span class="test-stat-value" id="passed-count">0</span>
192
- <span class="test-stat-label">Passed</span>
193
- </div>
194
- <div class="test-stat failed">
195
- <span class="test-stat-value" id="failed-count">0</span>
196
- <span class="test-stat-label">Failed</span>
197
- </div>
198
- <div class="test-stat skipped">
199
- <span class="test-stat-value" id="skipped-count">0</span>
200
- <span class="test-stat-label">Skipped</span>
201
- </div>
202
- <div class="test-stat">
203
- <span class="test-stat-value" id="total-count">0</span>
204
- <span class="test-stat-label">Total</span>
205
- </div>
206
- </div>
207
-
208
- <div id="results"></div>
209
-
210
- <!-- Hidden container for test elements -->
211
- <div id="test-container"></div>
212
-
213
- <script src="fig.js"></script>
214
- <script>
215
- // Simple Test Framework
216
- class TestRunner {
217
- constructor() {
218
- this.groups = [];
219
- this.currentGroup = null;
220
- this.container = document.getElementById('test-container');
221
- this.results = { passed: 0, failed: 0, skipped: 0, total: 0 };
222
- }
223
-
224
- describe(name, fn) {
225
- this.currentGroup = { name, tests: [] };
226
- this.groups.push(this.currentGroup);
227
- fn();
228
- this.currentGroup = null;
229
- }
230
-
231
- it(name, fn) {
232
- if (this.currentGroup) {
233
- this.currentGroup.tests.push({ name, fn });
234
- }
235
- }
236
-
237
- skip(name, fn) {
238
- if (this.currentGroup) {
239
- this.currentGroup.tests.push({ name, fn, skipped: true });
240
- }
241
- }
242
-
243
- createElement(tag, attrs = {}) {
244
- const el = document.createElement(tag);
245
- for (const [key, value] of Object.entries(attrs)) {
246
- el.setAttribute(key, value);
247
- }
248
- this.container.appendChild(el);
249
- return el;
250
- }
251
-
252
- cleanup() {
253
- this.container.innerHTML = '';
254
- }
255
-
256
- async waitFrame() {
257
- return new Promise(resolve => requestAnimationFrame(resolve));
258
- }
259
-
260
- async waitFrames(n = 2) {
261
- for (let i = 0; i < n; i++) {
262
- await this.waitFrame();
263
- }
264
- }
265
-
266
- async run() {
267
- this.results = { passed: 0, failed: 0, skipped: 0, total: 0 };
268
- const resultsEl = document.getElementById('results');
269
- resultsEl.innerHTML = '';
270
-
271
- for (const group of this.groups) {
272
- const groupEl = document.createElement('div');
273
- groupEl.className = 'test-group';
274
-
275
- const headerEl = document.createElement('div');
276
- headerEl.className = 'test-group-header';
277
-
278
- const statusEl = document.createElement('div');
279
- statusEl.className = 'test-group-status';
280
-
281
- const nameEl = document.createElement('span');
282
- nameEl.className = 'test-group-name';
283
- nameEl.textContent = group.name;
284
-
285
- const countEl = document.createElement('span');
286
- countEl.className = 'test-group-count';
287
-
288
- headerEl.appendChild(statusEl);
289
- headerEl.appendChild(nameEl);
290
- headerEl.appendChild(countEl);
291
- groupEl.appendChild(headerEl);
292
-
293
- const listEl = document.createElement('div');
294
- listEl.className = 'test-list';
295
- groupEl.appendChild(listEl);
296
-
297
- let groupPassed = 0;
298
- let groupFailed = 0;
299
-
300
- for (const test of group.tests) {
301
- this.results.total++;
302
- const testEl = document.createElement('div');
303
-
304
- if (test.skipped) {
305
- this.results.skipped++;
306
- testEl.innerHTML = `
307
- <div class="test-item">
308
- <span class="test-icon skipped">⊘</span>
309
- <span class="test-name">${test.name}</span>
310
- </div>
311
- `;
312
- } else {
313
- try {
314
- this.cleanup();
315
- await test.fn();
316
- this.results.passed++;
317
- groupPassed++;
318
- testEl.innerHTML = `
319
- <div class="test-item">
320
- <span class="test-icon passed">✓</span>
321
- <span class="test-name">${test.name}</span>
322
- </div>
323
- `;
324
- } catch (error) {
325
- this.results.failed++;
326
- groupFailed++;
327
- testEl.innerHTML = `
328
- <div class="test-item">
329
- <span class="test-icon failed">✗</span>
330
- <span class="test-name">${test.name}</span>
331
- </div>
332
- <div class="test-error">${error.message}</div>
333
- `;
334
- console.error(`Test failed: ${group.name} > ${test.name}`, error);
335
- }
336
- }
337
-
338
- listEl.appendChild(testEl);
339
- }
340
-
341
- countEl.textContent = `${groupPassed}/${group.tests.length}`;
342
- statusEl.className = `test-group-status ${groupFailed > 0 ? 'failed' : 'passed'}`;
343
-
344
- // Toggle visibility
345
- headerEl.addEventListener('click', () => {
346
- listEl.style.display = listEl.style.display === 'none' ? 'block' : 'none';
347
- });
348
-
349
- resultsEl.appendChild(groupEl);
350
- }
351
-
352
- // Update summary
353
- document.getElementById('passed-count').textContent = this.results.passed;
354
- document.getElementById('failed-count').textContent = this.results.failed;
355
- document.getElementById('skipped-count').textContent = this.results.skipped;
356
- document.getElementById('total-count').textContent = this.results.total;
357
-
358
- this.cleanup();
359
- }
360
- }
361
-
362
- // Assertion helpers
363
- function assert(condition, message = 'Assertion failed') {
364
- if (!condition) throw new Error(message);
365
- }
366
-
367
- function assertEqual(actual, expected, message = '') {
368
- if (actual !== expected) {
369
- throw new Error(`${message} Expected "${expected}", got "${actual}"`);
370
- }
371
- }
372
-
373
- function assertExists(value, message = 'Value should exist') {
374
- if (value === null || value === undefined) {
375
- throw new Error(message);
376
- }
377
- }
378
-
379
- function assertHasAttribute(el, attr, message = '') {
380
- if (!el.hasAttribute(attr)) {
381
- throw new Error(message || `Element should have attribute "${attr}"`);
382
- }
383
- }
384
-
385
- function assertNotHasAttribute(el, attr, message = '') {
386
- if (el.hasAttribute(attr)) {
387
- throw new Error(message || `Element should not have attribute "${attr}"`);
388
- }
389
- }
390
-
391
- // Initialize test runner
392
- const test = new TestRunner();
393
-
394
- // ============================================
395
- // FIG-BUTTON TESTS
396
- // ============================================
397
- test.describe('fig-button', () => {
398
- test.it('should render with shadow DOM', async () => {
399
- const btn = test.createElement('fig-button');
400
- btn.textContent = 'Test';
401
- await test.waitFrames();
402
- assertExists(btn.shadowRoot, 'Should have shadow root');
403
- });
404
-
405
- test.it('should reflect disabled attribute', async () => {
406
- const btn = test.createElement('fig-button', { disabled: '' });
407
- btn.textContent = 'Disabled';
408
- await test.waitFrames();
409
- assert(btn.hasAttribute('disabled'), 'Should have disabled attribute');
410
- });
411
-
412
- test.it('should handle type="toggle" and toggle selected', async () => {
413
- const btn = test.createElement('fig-button', { type: 'toggle' });
414
- btn.textContent = 'Toggle';
415
- await test.waitFrames();
416
- btn.click();
417
- await test.waitFrames();
418
- assertHasAttribute(btn, 'selected', 'Should be selected after click');
419
- });
420
- });
421
-
422
- // ============================================
423
- // FIG-DROPDOWN TESTS
424
- // ============================================
425
- test.describe('fig-dropdown', () => {
426
- test.it('should render with options', async () => {
427
- const dd = test.createElement('fig-dropdown');
428
- dd.innerHTML = '<option value="a">Option A</option><option value="b">Option B</option>';
429
- await test.waitFrames();
430
- assertEqual(dd.value, 'a', 'Should have first option as value');
431
- });
432
-
433
- test.it('should have value getter/setter', async () => {
434
- const dd = test.createElement('fig-dropdown');
435
- dd.innerHTML = '<option value="a">A</option><option value="b">B</option>';
436
- await test.waitFrames();
437
- dd.value = 'b';
438
- assertEqual(dd.value, 'b', 'Value should be updated');
439
- });
440
- });
441
-
442
- // ============================================
443
- // FIG-TOOLTIP TESTS
444
- // ============================================
445
- test.describe('fig-tooltip', () => {
446
- test.it('should have role="tooltip" on popup', async () => {
447
- const tt = test.createElement('fig-tooltip', { text: 'Hello' });
448
- tt.innerHTML = '<button>Hover me</button>';
449
- await test.waitFrames();
450
- tt.render();
451
- assertExists(tt.popup, 'Should have popup');
452
- assertEqual(tt.popup.getAttribute('role'), 'tooltip', 'Popup should have role="tooltip"');
453
- });
454
- });
455
-
456
- // ============================================
457
- // FIG-TAB / FIG-TABS TESTS
458
- // ============================================
459
- test.describe('fig-tabs', () => {
460
- test.it('should have role="tablist"', async () => {
461
- const tabs = test.createElement('fig-tabs');
462
- tabs.innerHTML = '<fig-tab value="1">Tab 1</fig-tab><fig-tab value="2">Tab 2</fig-tab>';
463
- await test.waitFrames();
464
- assertEqual(tabs.getAttribute('role'), 'tablist', 'Should have role="tablist"');
465
- });
466
-
467
- test.it('child tabs should have role="tab"', async () => {
468
- const tabs = test.createElement('fig-tabs');
469
- tabs.innerHTML = '<fig-tab value="1">Tab 1</fig-tab>';
470
- await test.waitFrames();
471
- const tab = tabs.querySelector('fig-tab');
472
- assertEqual(tab.getAttribute('role'), 'tab', 'Tab should have role="tab"');
473
- });
474
-
475
- test.it('should have value getter/setter', async () => {
476
- const tabs = test.createElement('fig-tabs');
477
- tabs.innerHTML = '<fig-tab value="a">A</fig-tab><fig-tab value="b">B</fig-tab>';
478
- await test.waitFrames();
479
- tabs.value = 'b';
480
- await test.waitFrames();
481
- const selectedTab = tabs.querySelector('fig-tab[selected]');
482
- assertEqual(selectedTab?.getAttribute('value'), 'b', 'Tab B should be selected');
483
- });
484
-
485
- test.it('should support disabled attribute', async () => {
486
- const tabs = test.createElement('fig-tabs', { disabled: '' });
487
- tabs.innerHTML = '<fig-tab value="1">Tab 1</fig-tab>';
488
- await test.waitFrames();
489
- const tab = tabs.querySelector('fig-tab');
490
- assertHasAttribute(tab, 'disabled', 'Child tabs should be disabled');
491
- });
492
- });
493
-
494
- // ============================================
495
- // FIG-SEGMENT / FIG-SEGMENTED-CONTROL TESTS
496
- // ============================================
497
- test.describe('fig-segmented-control', () => {
498
- test.it('should auto-select first segment if none selected', async () => {
499
- const ctrl = test.createElement('fig-segmented-control');
500
- ctrl.innerHTML = '<fig-segment value="a">A</fig-segment><fig-segment value="b">B</fig-segment>';
501
- await test.waitFrames(3);
502
- const firstSeg = ctrl.querySelector('fig-segment');
503
- assertHasAttribute(firstSeg, 'selected', 'First segment should be auto-selected');
504
- });
505
-
506
- test.it('should respect pre-selected segment', async () => {
507
- const ctrl = test.createElement('fig-segmented-control');
508
- ctrl.innerHTML = '<fig-segment value="a">A</fig-segment><fig-segment value="b" selected="true">B</fig-segment>';
509
- await test.waitFrames(3);
510
- const secondSeg = ctrl.querySelectorAll('fig-segment')[1];
511
- assertHasAttribute(secondSeg, 'selected', 'Second segment should remain selected');
512
- });
513
-
514
- test.it('should have selectedSegment getter', async () => {
515
- const ctrl = test.createElement('fig-segmented-control');
516
- ctrl.innerHTML = '<fig-segment value="a">A</fig-segment>';
517
- await test.waitFrames(3);
518
- assertExists(ctrl.selectedSegment, 'Should have selectedSegment');
519
- });
520
- });
521
-
522
- // ============================================
523
- // FIG-SLIDER TESTS
524
- // ============================================
525
- test.describe('fig-slider', () => {
526
- test.it('should render with correct ARIA attributes', async () => {
527
- const slider = test.createElement('fig-slider', { min: '0', max: '100', value: '50' });
528
- await test.waitFrames();
529
- const input = slider.querySelector('input[type="range"]');
530
- assertExists(input, 'Should have range input');
531
- assertEqual(input.getAttribute('aria-valuemin'), '0', 'Should have aria-valuemin');
532
- assertEqual(input.getAttribute('aria-valuemax'), '100', 'Should have aria-valuemax');
533
- assertEqual(input.getAttribute('aria-valuenow'), '50', 'Should have aria-valuenow');
534
- });
535
-
536
- test.it('should emit input and change events', async () => {
537
- const slider = test.createElement('fig-slider', { value: '50' });
538
- await test.waitFrames();
539
-
540
- let inputFired = false;
541
- let changeFired = false;
542
- slider.addEventListener('input', () => inputFired = true);
543
- slider.addEventListener('change', () => changeFired = true);
544
-
545
- const input = slider.querySelector('input');
546
- input.value = '75';
547
- input.dispatchEvent(new Event('input', { bubbles: true }));
548
- input.dispatchEvent(new Event('change', { bubbles: true }));
549
-
550
- assert(inputFired, 'Input event should fire');
551
- assert(changeFired, 'Change event should fire');
552
- });
553
- });
554
-
555
- // ============================================
556
- // FIG-INPUT-TEXT TESTS
557
- // ============================================
558
- test.describe('fig-input-text', () => {
559
- test.it('should render input element', async () => {
560
- const input = test.createElement('fig-input-text', { value: 'test', placeholder: 'Enter text' });
561
- await test.waitFrames();
562
- const inputEl = input.querySelector('input');
563
- assertExists(inputEl, 'Should have input element');
564
- assertEqual(inputEl.value, 'test', 'Should have correct value');
565
- });
566
-
567
- test.it('should support disabled attribute', async () => {
568
- const input = test.createElement('fig-input-text', { disabled: '' });
569
- await test.waitFrames();
570
- const inputEl = input.querySelector('input');
571
- assert(inputEl.disabled, 'Input should be disabled');
572
- });
573
- });
574
-
575
- // ============================================
576
- // FIG-INPUT-NUMBER TESTS
577
- // ============================================
578
- test.describe('fig-input-number', () => {
579
- test.it('should render with units', async () => {
580
- const input = test.createElement('fig-input-number', { value: '50', units: '%' });
581
- await test.waitFrames();
582
- const inputEl = input.querySelector('input');
583
- assertExists(inputEl, 'Should have input element');
584
- assert(inputEl.value.includes('50'), 'Should display value');
585
- assert(inputEl.value.includes('%'), 'Should display units');
586
- });
587
-
588
- test.it('should have min/max constraints', async () => {
589
- const input = test.createElement('fig-input-number', { min: '0', max: '100', value: '50' });
590
- await test.waitFrames();
591
- assertEqual(input.min, 0, 'Should have min');
592
- assertEqual(input.max, 100, 'Should have max');
593
- });
594
- });
595
-
596
- // ============================================
597
- // FIG-CHECKBOX TESTS
598
- // ============================================
599
- test.describe('fig-checkbox', () => {
600
- test.it('should render checkbox input', async () => {
601
- const cb = test.createElement('fig-checkbox');
602
- await test.waitFrames();
603
- const input = cb.querySelector('input[type="checkbox"]');
604
- assertExists(input, 'Should have checkbox input');
605
- assertEqual(input.getAttribute('role'), 'checkbox', 'Should have role="checkbox"');
606
- });
607
-
608
- test.it('should have checked getter/setter', async () => {
609
- const cb = test.createElement('fig-checkbox');
610
- await test.waitFrames();
611
- cb.checked = true;
612
- assert(cb.checked, 'Should be checked');
613
- assertHasAttribute(cb, 'checked', 'Should have checked attribute');
614
- });
615
-
616
- test.it('should have value getter/setter', async () => {
617
- const cb = test.createElement('fig-checkbox', { value: 'test-value' });
618
- await test.waitFrames();
619
- assertEqual(cb.value, 'test-value', 'Should have correct value');
620
- });
621
-
622
- test.it('should emit input and change events', async () => {
623
- const cb = test.createElement('fig-checkbox');
624
- await test.waitFrames();
625
-
626
- let inputFired = false;
627
- let changeFired = false;
628
- cb.addEventListener('input', () => inputFired = true);
629
- cb.addEventListener('change', () => changeFired = true);
630
-
631
- const input = cb.querySelector('input');
632
- input.click();
633
-
634
- assert(inputFired, 'Input event should fire');
635
- assert(changeFired, 'Change event should fire');
636
- });
637
- });
638
-
639
- // ============================================
640
- // FIG-SWITCH TESTS
641
- // ============================================
642
- test.describe('fig-switch', () => {
643
- test.it('should have role="switch"', async () => {
644
- const sw = test.createElement('fig-switch');
645
- await test.waitFrames();
646
- const input = sw.querySelector('input');
647
- assertEqual(input.getAttribute('role'), 'switch', 'Should have role="switch"');
648
- });
649
- });
650
-
651
- // ============================================
652
- // FIG-RADIO TESTS
653
- // ============================================
654
- test.describe('fig-radio', () => {
655
- test.it('should render radio input', async () => {
656
- const radio = test.createElement('fig-radio', { name: 'test-group' });
657
- await test.waitFrames();
658
- const input = radio.querySelector('input[type="radio"]');
659
- assertExists(input, 'Should have radio input');
660
- assertEqual(input.name, 'test-group', 'Should have correct name');
661
- });
662
- });
663
-
664
- // ============================================
665
- // FIG-COMBO-INPUT TESTS
666
- // ============================================
667
- test.describe('fig-combo-input', () => {
668
- test.it('should render with options', async () => {
669
- const combo = test.createElement('fig-combo-input', { options: 'a,b,c', placeholder: 'Select' });
670
- await test.waitFrames();
671
- const input = combo.querySelector('fig-input-text');
672
- const dropdown = combo.querySelector('fig-dropdown');
673
- assertExists(input, 'Should have text input');
674
- assertExists(dropdown, 'Should have dropdown');
675
- });
676
-
677
- test.it('should support disabled attribute', async () => {
678
- const combo = test.createElement('fig-combo-input', { options: 'a,b', disabled: '' });
679
- await test.waitFrames(3);
680
- const input = combo.querySelector('fig-input-text');
681
- assertHasAttribute(input, 'disabled', 'Input should be disabled');
682
- });
683
- });
684
-
685
- // ============================================
686
- // FIG-CHIT TESTS
687
- // ============================================
688
- test.describe('fig-chit', () => {
689
- test.it('should render with background color', async () => {
690
- const chit = test.createElement('fig-chit', { background: '#ff0000' });
691
- await test.waitFrames();
692
- assertEqual(chit.background, '#ff0000', 'Should have correct background');
693
- });
694
-
695
- test.it('should support alpha attribute', async () => {
696
- const chit = test.createElement('fig-chit', { background: '#ff0000', alpha: '0.5' });
697
- await test.waitFrames();
698
- const alphaStyle = chit.style.getPropertyValue('--alpha');
699
- assertEqual(alphaStyle, '0.5', 'Should have alpha CSS variable');
700
- });
701
- });
702
-
703
- // ============================================
704
- // FIG-INPUT-COLOR TESTS
705
- // ============================================
706
- test.describe('fig-input-color', () => {
707
- test.it('should parse hex color', async () => {
708
- const input = test.createElement('fig-input-color', { value: '#ff0000', text: 'true' });
709
- await test.waitFrames();
710
- assertEqual(input.hexOpaque, '#FF0000', 'Should have correct hex');
711
- });
712
-
713
- test.it('should support alpha in hex', async () => {
714
- const input = test.createElement('fig-input-color', { value: '#ff000080', text: 'true', alpha: 'true' });
715
- await test.waitFrames();
716
- assert(input.alpha < 100, 'Alpha should be less than 100');
717
- });
718
- });
719
-
720
- // ============================================
721
- // FIG-INPUT-FILL TESTS
722
- // ============================================
723
- test.describe('fig-input-fill', () => {
724
- test.it('should parse solid fill', async () => {
725
- const fill = test.createElement('fig-input-fill', {
726
- value: '{"type":"solid","color":"#ff0000","opacity":100}'
727
- });
728
- await test.waitFrames();
729
- const value = fill.value;
730
- assertEqual(value.type, 'solid', 'Should be solid type');
731
- assertEqual(value.color, '#ff0000', 'Should have correct color');
732
- });
733
-
734
- test.it('should parse gradient fill', async () => {
735
- const fill = test.createElement('fig-input-fill', {
736
- value: '{"type":"linear","angle":90,"stops":[{"color":"#ff0000","position":0},{"color":"#0000ff","position":100}]}'
737
- });
738
- await test.waitFrames();
739
- const value = fill.value;
740
- assertEqual(value.type, 'linear', 'Should be linear type');
741
- });
742
- });
743
-
744
- // ============================================
745
- // FIG-INPUT-JOYSTICK TESTS
746
- // ============================================
747
- test.describe('fig-input-joystick', () => {
748
- test.it('should initialize with default position', async () => {
749
- const joy = test.createElement('fig-input-joystick');
750
- await test.waitFrames();
751
- const value = joy.value;
752
- assertEqual(value[0], 50, 'X should be 50');
753
- assertEqual(value[1], 50, 'Y should be 50');
754
- });
755
-
756
- test.it('should have focusable plane', async () => {
757
- const joy = test.createElement('fig-input-joystick');
758
- await test.waitFrames();
759
- const plane = joy.querySelector('.fig-input-joystick-plane-container');
760
- assertEqual(plane.getAttribute('tabindex'), '0', 'Should be focusable');
761
- });
762
- });
763
-
764
- // ============================================
765
- // FIG-INPUT-ANGLE TESTS
766
- // ============================================
767
- test.describe('fig-input-angle', () => {
768
- test.it('should initialize with value', async () => {
769
- const angle = test.createElement('fig-input-angle', { value: '45' });
770
- await test.waitFrames();
771
- assertEqual(angle.value, 45, 'Should have correct angle');
772
- });
773
-
774
- test.it('should have value getter/setter', async () => {
775
- const angle = test.createElement('fig-input-angle');
776
- await test.waitFrames();
777
- angle.value = 90;
778
- assertEqual(angle.value, 90, 'Should update angle');
779
- });
780
- });
781
-
782
- // ============================================
783
- // FIG-FIELD TESTS
784
- // ============================================
785
- test.describe('fig-field', () => {
786
- test.it('should link label to input', async () => {
787
- const field = test.createElement('fig-field');
788
- field.innerHTML = '<label>Name</label><fig-input-text></fig-input-text>';
789
- await test.waitFrames();
790
- const label = field.querySelector('label');
791
- const input = field.querySelector('fig-input-text');
792
- assertEqual(label.getAttribute('for'), input.getAttribute('id'), 'Label should be linked to input');
793
- });
794
- });
795
-
796
- // ============================================
797
- // FIG-TOAST TESTS
798
- // ============================================
799
- test.describe('fig-toast', () => {
800
- test.it('should have duration attribute', async () => {
801
- const toast = test.createElement('fig-toast', { duration: '3000' });
802
- toast.textContent = 'Test toast';
803
- await test.waitFrames();
804
- assertEqual(toast.getAttribute('duration'), '3000', 'Should have duration');
805
- });
806
- });
807
-
808
- // ============================================
809
- // FIG-FILL-PICKER TESTS
810
- // ============================================
811
- test.describe('fig-fill-picker', () => {
812
- test.it('should initialize with solid fill', async () => {
813
- const picker = test.createElement('fig-fill-picker', {
814
- value: '{"type":"solid","color":"#ff0000","opacity":100}'
815
- });
816
- await test.waitFrames();
817
- const value = picker.value;
818
- assertEqual(value.type, 'solid', 'Should be solid type');
819
- });
820
-
821
- test.it('should support mode attribute', async () => {
822
- const picker = test.createElement('fig-fill-picker', { mode: 'solid' });
823
- await test.waitFrames();
824
- assertEqual(picker.getAttribute('mode'), 'solid', 'Should have mode attribute');
825
- });
826
- });
827
-
828
- // Run tests on button click
829
- document.getElementById('run-tests').addEventListener('click', async (e) => {
830
- e.target.disabled = true;
831
- e.target.textContent = 'Running...';
832
- await test.run();
833
- e.target.disabled = false;
834
- e.target.textContent = 'Run Tests';
835
- });
836
-
837
- // Auto-run on load
838
- window.addEventListener('load', () => {
839
- setTimeout(() => test.run(), 100);
840
- });
841
- </script>
842
- </body>
843
- </html>