ai-progress-controls 0.1.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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +823 -0
  3. package/dist/ai-progress-controls.es.js +7191 -0
  4. package/dist/ai-progress-controls.es.js.map +1 -0
  5. package/dist/ai-progress-controls.umd.js +2 -0
  6. package/dist/ai-progress-controls.umd.js.map +1 -0
  7. package/dist/index.d.ts +2212 -0
  8. package/package.json +105 -0
  9. package/src/__tests__/setup.ts +93 -0
  10. package/src/core/base/AIControl.ts +230 -0
  11. package/src/core/base/index.ts +3 -0
  12. package/src/core/base/types.ts +77 -0
  13. package/src/core/base/utils.ts +168 -0
  14. package/src/core/batch-progress/BatchProgress.test.ts +458 -0
  15. package/src/core/batch-progress/BatchProgress.ts +760 -0
  16. package/src/core/batch-progress/index.ts +14 -0
  17. package/src/core/batch-progress/styles.ts +480 -0
  18. package/src/core/batch-progress/types.ts +169 -0
  19. package/src/core/model-loader/ModelLoader.test.ts +311 -0
  20. package/src/core/model-loader/ModelLoader.ts +673 -0
  21. package/src/core/model-loader/index.ts +2 -0
  22. package/src/core/model-loader/styles.ts +496 -0
  23. package/src/core/model-loader/types.ts +127 -0
  24. package/src/core/parameter-panel/ParameterPanel.test.ts +856 -0
  25. package/src/core/parameter-panel/ParameterPanel.ts +877 -0
  26. package/src/core/parameter-panel/index.ts +14 -0
  27. package/src/core/parameter-panel/styles.ts +323 -0
  28. package/src/core/parameter-panel/types.ts +278 -0
  29. package/src/core/parameter-slider/ParameterSlider.test.ts +299 -0
  30. package/src/core/parameter-slider/ParameterSlider.ts +653 -0
  31. package/src/core/parameter-slider/index.ts +8 -0
  32. package/src/core/parameter-slider/styles.ts +493 -0
  33. package/src/core/parameter-slider/types.ts +107 -0
  34. package/src/core/queue-progress/QueueProgress.test.ts +344 -0
  35. package/src/core/queue-progress/QueueProgress.ts +563 -0
  36. package/src/core/queue-progress/index.ts +5 -0
  37. package/src/core/queue-progress/styles.ts +469 -0
  38. package/src/core/queue-progress/types.ts +130 -0
  39. package/src/core/retry-progress/RetryProgress.test.ts +397 -0
  40. package/src/core/retry-progress/RetryProgress.ts +957 -0
  41. package/src/core/retry-progress/index.ts +6 -0
  42. package/src/core/retry-progress/styles.ts +530 -0
  43. package/src/core/retry-progress/types.ts +176 -0
  44. package/src/core/stream-progress/StreamProgress.test.ts +531 -0
  45. package/src/core/stream-progress/StreamProgress.ts +517 -0
  46. package/src/core/stream-progress/index.ts +2 -0
  47. package/src/core/stream-progress/styles.ts +349 -0
  48. package/src/core/stream-progress/types.ts +82 -0
  49. package/src/index.ts +19 -0
@@ -0,0 +1,458 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { BatchProgress } from './BatchProgress';
3
+ import { waitForElement, waitForNextTick } from '../../__tests__/setup';
4
+
5
+ describe('BatchProgress Component', () => {
6
+ let progress: BatchProgress;
7
+
8
+ beforeEach(() => {
9
+ progress = new BatchProgress();
10
+ document.body.appendChild(progress);
11
+ });
12
+
13
+ afterEach(() => {
14
+ if (progress.parentNode) {
15
+ progress.remove();
16
+ }
17
+ });
18
+
19
+ describe('Constructor & Configuration', () => {
20
+ it('should create instance with default config', () => {
21
+ expect(progress).toBeInstanceOf(BatchProgress);
22
+ expect(progress.tagName.toLowerCase()).toBe('batch-progress');
23
+ });
24
+
25
+ it('should accept custom configuration', () => {
26
+ const customProgress = new BatchProgress({
27
+ totalItems: 100,
28
+ showRate: true,
29
+ showETA: true,
30
+ });
31
+
32
+ expect(customProgress['config'].totalItems).toBe(100);
33
+ expect(customProgress['config'].showRate).toBe(true);
34
+ customProgress.remove();
35
+ });
36
+
37
+ it('should have shadow root', async () => {
38
+ await waitForElement(progress);
39
+ expect(progress.shadowRoot).toBeTruthy();
40
+ });
41
+ });
42
+
43
+ describe('State Management', () => {
44
+ it('should initialize with idle state', () => {
45
+ expect(progress['state'].status).toBe('idle');
46
+ expect(progress['state'].items.size).toBe(0);
47
+ });
48
+
49
+ it('should track batch items', () => {
50
+ progress.start();
51
+ progress.addItem('item1', 'Task 1');
52
+
53
+ expect(progress['state'].items.size).toBe(1);
54
+ });
55
+
56
+ it('should track completed items', () => {
57
+ progress.start();
58
+ progress.addItem('item1', 'Task 1');
59
+ progress.completeItem('item1');
60
+
61
+ expect(progress['state'].completedCount).toBe(1);
62
+ });
63
+ });
64
+
65
+ describe('Methods', () => {
66
+ describe('start()', () => {
67
+ it('should start batch processing', () => {
68
+ progress.start();
69
+
70
+ expect(progress['state'].status).toBe('processing');
71
+ expect(progress['state'].totalItems).toBeGreaterThanOrEqual(0);
72
+ });
73
+
74
+ it('should emit batchstart event', async () => {
75
+ const spy = vi.fn();
76
+ progress.addEventListener('batchstart', spy);
77
+
78
+ progress.start();
79
+ await waitForNextTick();
80
+
81
+ expect(spy).toHaveBeenCalled();
82
+ });
83
+ });
84
+
85
+ describe('addItem()', () => {
86
+ beforeEach(() => {
87
+ progress.start();
88
+ });
89
+
90
+ it('should add item to batch', () => {
91
+ progress.addItem('item1', 'Task 1');
92
+
93
+ expect(progress['state'].items.size).toBe(1);
94
+ expect(progress['state'].items.get('item1')?.id).toBe('item1');
95
+ });
96
+
97
+ it('should not emit itemupdate on add', async () => {
98
+ const spy = vi.fn();
99
+ progress.addEventListener('itemupdate', spy);
100
+
101
+ progress.addItem('item1', 'Task 1');
102
+ await waitForNextTick();
103
+
104
+ // addItem doesn't trigger itemupdate, only updateItem does
105
+ expect(spy).not.toHaveBeenCalled();
106
+ });
107
+
108
+ it('should handle multiple items', () => {
109
+ progress.addItem('item1', 'Task 1');
110
+ progress.addItem('item2', 'Task 2');
111
+ progress.addItem('item3', 'Task 3');
112
+
113
+ expect(progress['state'].items.size).toBe(3);
114
+ });
115
+ });
116
+
117
+ describe('updateItem()', () => {
118
+ beforeEach(() => {
119
+ progress.start();
120
+ progress.addItem('item1', 'Task 1');
121
+ });
122
+
123
+ it('should update item progress', () => {
124
+ progress.updateItem({ itemId: 'item1', progress: 50 });
125
+
126
+ const item = progress['state'].items.get('item1');
127
+ expect(item?.progress).toBe(50);
128
+ });
129
+
130
+ it('should emit itemupdate event', async () => {
131
+ const spy = vi.fn();
132
+ progress.addEventListener('itemupdate', spy);
133
+
134
+ progress.updateItem({ itemId: 'item1', progress: 75 });
135
+ await waitForNextTick();
136
+
137
+ expect(spy).toHaveBeenCalled();
138
+ });
139
+ });
140
+
141
+ describe('completeItem()', () => {
142
+ beforeEach(() => {
143
+ progress.start();
144
+ progress.addItem('item1', 'Task 1');
145
+ });
146
+
147
+ it('should mark item as complete', () => {
148
+ progress.completeItem('item1');
149
+
150
+ const item = progress['state'].items.get('item1');
151
+ expect(item?.status).toBe('completed');
152
+ expect(progress['state'].completedCount).toBe(1);
153
+ });
154
+
155
+ it('should emit itemcomplete event', async () => {
156
+ const spy = vi.fn();
157
+ progress.addEventListener('itemcomplete', spy);
158
+
159
+ progress.completeItem('item1');
160
+ await waitForNextTick();
161
+
162
+ expect(spy).toHaveBeenCalled();
163
+ });
164
+ });
165
+
166
+ describe('failItem()', () => {
167
+ beforeEach(() => {
168
+ progress.start();
169
+ progress.addItem('item1', 'Task 1');
170
+ });
171
+
172
+ it('should mark item as failed', () => {
173
+ progress.failItem('item1', 'Error occurred');
174
+
175
+ const item = progress['state'].items.get('item1');
176
+ expect(item?.status).toBe('failed');
177
+ expect(progress['state'].failedCount).toBe(1);
178
+ });
179
+
180
+ it('should emit itemfail event', async () => {
181
+ const spy = vi.fn();
182
+ progress.addEventListener('itemfailed', spy);
183
+
184
+ progress.failItem('item1', 'Test error');
185
+ await waitForNextTick();
186
+
187
+ expect(spy).toHaveBeenCalled();
188
+ });
189
+ });
190
+
191
+ describe('complete()', () => {
192
+ beforeEach(() => {
193
+ progress.start();
194
+ progress.addItem('item1', 'Task 1');
195
+ progress.addItem('item2', 'Task 2');
196
+ progress.completeItem('item1');
197
+ progress.completeItem('item2');
198
+ });
199
+
200
+ it('should complete batch', () => {
201
+ progress.complete();
202
+ expect(progress['state'].status).toBe('completed');
203
+ });
204
+
205
+ it('should emit batchcomplete event', async () => {
206
+ const spy = vi.fn();
207
+ progress.addEventListener('batchcomplete', spy);
208
+
209
+ progress.complete();
210
+ await waitForNextTick();
211
+
212
+ expect(spy).toHaveBeenCalled();
213
+ });
214
+ });
215
+
216
+ describe('cancel()', () => {
217
+ beforeEach(() => {
218
+ progress.start();
219
+ });
220
+
221
+ it('should cancel batch', () => {
222
+ progress.cancel();
223
+ expect(progress['state'].status).toBe('cancelled');
224
+ });
225
+
226
+ it('should emit batchcancel event', async () => {
227
+ const spy = vi.fn();
228
+ progress.addEventListener('batchcancel', spy);
229
+
230
+ progress.cancel();
231
+ await waitForNextTick();
232
+
233
+ expect(spy).toHaveBeenCalled();
234
+ });
235
+ });
236
+
237
+ describe('reset()', () => {
238
+ it('should reset to initial state', () => {
239
+ progress.start();
240
+ progress.addItem('item1', 'Task 1');
241
+ progress.completeItem('item1');
242
+ progress.reset();
243
+
244
+ expect(progress['state'].status).toBe('idle');
245
+ expect(progress['state'].items.size).toBe(0);
246
+ expect(progress['state'].completedCount).toBe(0);
247
+ });
248
+ });
249
+
250
+ describe('getOverallProgress()', () => {
251
+ it('should calculate overall progress', () => {
252
+ progress.start();
253
+ progress.addItem('item1', 'Task 1');
254
+ progress.addItem('item2', 'Task 2');
255
+ progress.completeItem('item1');
256
+
257
+ const overallProgress = progress.getOverallProgress();
258
+ expect(overallProgress).toBeGreaterThan(0);
259
+ expect(overallProgress).toBeLessThanOrEqual(100);
260
+ });
261
+
262
+ it('should return 0 for no items', () => {
263
+ progress.start();
264
+ expect(progress.getOverallProgress()).toBe(0);
265
+ });
266
+
267
+ it('should return 100 when all complete', () => {
268
+ progress.start();
269
+ progress.addItem('item1', 'Task 1');
270
+ progress.addItem('item2', 'Task 2');
271
+ progress.completeItem('item1');
272
+ progress.completeItem('item2');
273
+
274
+ expect(progress.getOverallProgress()).toBe(100);
275
+ });
276
+ });
277
+
278
+ describe('getRate()', () => {
279
+ it('should calculate processing rate', () => {
280
+ progress.start();
281
+ progress.addItem('item1', 'Task 1');
282
+ progress.completeItem('item1');
283
+
284
+ const rate = progress.getRate();
285
+ expect(rate).toBeGreaterThanOrEqual(0);
286
+ });
287
+ });
288
+
289
+ describe('getStats()', () => {
290
+ it('should return batch statistics', () => {
291
+ progress.start();
292
+ progress.addItem('item1', 'Task 1');
293
+ progress.addItem('item2', 'Task 2');
294
+ progress.completeItem('item1');
295
+ progress.failItem('item2', 'Error');
296
+
297
+ const stats = progress.getStats();
298
+ expect(stats.total).toBe(2); // Actual added items
299
+ expect(stats.completed).toBe(2); // completedCount includes both success and failed
300
+ expect(stats.failed).toBe(1);
301
+ });
302
+ });
303
+ });
304
+
305
+ describe('Properties', () => {
306
+ it('should get/set disabled state', () => {
307
+ expect(progress.disabled).toBe(false);
308
+
309
+ progress.disabled = true;
310
+ expect(progress.disabled).toBe(true);
311
+ });
312
+ });
313
+
314
+ describe('Rendering', () => {
315
+ it('should render in shadow DOM', async () => {
316
+ await waitForElement(progress);
317
+ expect(progress.shadowRoot?.querySelector('.container')).toBeTruthy();
318
+ });
319
+
320
+ it('should update display on item changes', async () => {
321
+ progress.start();
322
+ await waitForElement(progress);
323
+
324
+ progress.addItem('item1', 'Task 1');
325
+ await waitForNextTick();
326
+
327
+ const container = progress.shadowRoot?.querySelector('.container');
328
+ expect(container).toBeTruthy();
329
+ });
330
+ });
331
+
332
+ describe('Accessibility', () => {
333
+ it('should have proper ARIA role', async () => {
334
+ await waitForElement(progress);
335
+ const role = progress.getAttribute('role');
336
+ expect(role).toBe('region');
337
+ });
338
+ });
339
+
340
+ describe('Edge Cases', () => {
341
+ it('should handle zero total items', () => {
342
+ progress.start();
343
+ expect(progress['state'].totalItems).toBeGreaterThanOrEqual(0);
344
+ });
345
+
346
+ it('should handle duplicate item IDs', () => {
347
+ progress.start();
348
+ progress.addItem('item1', 'Task 1');
349
+ progress.addItem('item1', 'Task 1 Duplicate');
350
+
351
+ // Map overwrites duplicate keys
352
+ expect(progress['state'].items.has('item1')).toBe(true);
353
+ expect(progress['state'].items.size).toBe(1);
354
+ });
355
+
356
+ it('should handle rapid item operations', async () => {
357
+ progress.start();
358
+
359
+ for (let i = 0; i < 10; i++) {
360
+ progress.addItem(`item${i}`, `Task ${i}`);
361
+ await waitForNextTick();
362
+ progress.updateItem({ itemId: `item${i}`, progress: 50 });
363
+ await waitForNextTick();
364
+ progress.completeItem(`item${i}`);
365
+ await waitForNextTick();
366
+ }
367
+
368
+ expect(progress['state'].completedCount).toBe(10);
369
+ });
370
+ });
371
+
372
+ describe('Size Variants', () => {
373
+ it('should default to default size', () => {
374
+ expect(progress['config'].size).toBe('default');
375
+ });
376
+
377
+ it('should accept size in constructor', () => {
378
+ const compact = new BatchProgress({ size: 'compact' });
379
+ expect(compact['config'].size).toBe('compact');
380
+ compact.remove();
381
+
382
+ const large = new BatchProgress({ size: 'large' });
383
+ expect(large['config'].size).toBe('large');
384
+ large.remove();
385
+ });
386
+
387
+ it('should update size via attribute', async () => {
388
+ await waitForElement(progress);
389
+
390
+ progress.setAttribute('size', 'compact');
391
+ await waitForNextTick();
392
+ expect(progress['config'].size).toBe('compact');
393
+ expect(progress.getAttribute('size')).toBe('compact');
394
+
395
+ progress.setAttribute('size', 'large');
396
+ await waitForNextTick();
397
+ expect(progress['config'].size).toBe('large');
398
+ expect(progress.getAttribute('size')).toBe('large');
399
+ });
400
+
401
+ it('should apply size attribute to host element', async () => {
402
+ await waitForElement(progress);
403
+
404
+ progress.setAttribute('size', 'compact');
405
+ await waitForNextTick();
406
+ expect(progress.hasAttribute('size')).toBe(true);
407
+ expect(progress.getAttribute('size')).toBe('compact');
408
+ });
409
+ });
410
+
411
+ describe('Visual Variants', () => {
412
+ it('should apply default variant by default', () => {
413
+ expect(progress['config'].variant).toBe('default');
414
+ });
415
+
416
+ it('should apply minimal variant', () => {
417
+ const minimal = new BatchProgress({ variant: 'minimal' });
418
+ expect(minimal['config'].variant).toBe('minimal');
419
+ minimal.remove();
420
+ });
421
+
422
+ it('should apply gradient variant', () => {
423
+ const gradient = new BatchProgress({ variant: 'gradient' });
424
+ expect(gradient['config'].variant).toBe('gradient');
425
+ gradient.remove();
426
+ });
427
+
428
+ it('should apply glassmorphic variant', () => {
429
+ const glassmorphic = new BatchProgress({ variant: 'glassmorphic' });
430
+ expect(glassmorphic['config'].variant).toBe('glassmorphic');
431
+ glassmorphic.remove();
432
+ });
433
+ });
434
+
435
+ describe('Animation Effects', () => {
436
+ it('should apply none animation by default', () => {
437
+ expect(progress['config'].animation).toBe('none');
438
+ });
439
+
440
+ it('should apply striped animation', () => {
441
+ const striped = new BatchProgress({ animation: 'striped' });
442
+ expect(striped['config'].animation).toBe('striped');
443
+ striped.remove();
444
+ });
445
+
446
+ it('should apply pulse animation', () => {
447
+ const pulse = new BatchProgress({ animation: 'pulse' });
448
+ expect(pulse['config'].animation).toBe('pulse');
449
+ pulse.remove();
450
+ });
451
+
452
+ it('should apply glow animation', () => {
453
+ const glow = new BatchProgress({ animation: 'glow' });
454
+ expect(glow['config'].animation).toBe('glow');
455
+ glow.remove();
456
+ });
457
+ });
458
+ });