@rakeyshgidwani/roger-ui-bank-theme-stan-design 0.1.6 โ†’ 0.1.7

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/CHANGELOG.md +1 -1
  2. package/dist/index.d.ts +7 -0
  3. package/dist/index.esm.js +18 -1
  4. package/dist/index.js +18 -1
  5. package/dist/setupTests.d.ts +124 -0
  6. package/dist/setupTests.esm.js +122 -0
  7. package/dist/setupTests.js +122 -0
  8. package/dist/styles.css +1 -1
  9. package/package.json +1 -1
  10. package/src/index.css +1046 -0
  11. package/src/index.ts +18 -0
  12. package/src/plugins/theme-css-generator.ts +354 -0
  13. package/src/setupTests.ts +124 -0
  14. package/src/stories/README.md +39 -0
  15. package/src/stories/components/ThemeDebugger.tsx +143 -0
  16. package/src/stories/index.ts +29 -0
  17. package/src/stories/storybook-theme-imports.css +51 -0
  18. package/src/styles/base/fonts.css +30 -0
  19. package/src/styles/base/generated-theme-variables.css +573 -0
  20. package/src/styles/base/index.css +7 -0
  21. package/src/styles/base/reset.css +48 -0
  22. package/src/styles/base/theme.css +1068 -0
  23. package/src/styles/base/typography.css +68 -0
  24. package/src/styles/base/variables.css +5 -0
  25. package/src/styles/components/CLAUDE.md +62 -0
  26. package/src/styles/components/base/badge.css +428 -0
  27. package/src/styles/components/base/button.css +774 -0
  28. package/src/styles/components/base/card.css +601 -0
  29. package/src/styles/components/base/checkbox.css +442 -0
  30. package/src/styles/components/base/index.css +9 -0
  31. package/src/styles/components/base/input.css +887 -0
  32. package/src/styles/components/base/label.css +296 -0
  33. package/src/styles/components/data-display/chart.css +353 -0
  34. package/src/styles/components/data-display/data-grid.css +619 -0
  35. package/src/styles/components/data-display/index.css +9 -0
  36. package/src/styles/components/data-display/list.css +560 -0
  37. package/src/styles/components/data-display/table.css +498 -0
  38. package/src/styles/components/data-display/timeline.css +764 -0
  39. package/src/styles/components/data-display/tree.css +881 -0
  40. package/src/styles/components/feedback/alert.css +358 -0
  41. package/src/styles/components/feedback/index.css +7 -0
  42. package/src/styles/components/feedback/progress.css +435 -0
  43. package/src/styles/components/feedback/skeleton.css +337 -0
  44. package/src/styles/components/feedback/toast.css +564 -0
  45. package/src/styles/components/index.css +17 -0
  46. package/src/styles/components/navigation/breadcrumb.css +465 -0
  47. package/src/styles/components/navigation/index.css +9 -0
  48. package/src/styles/components/navigation/menu.css +572 -0
  49. package/src/styles/components/navigation/pagination.css +635 -0
  50. package/src/styles/components/navigation/sidebar.css +807 -0
  51. package/src/styles/components/navigation/stepper.css +519 -0
  52. package/src/styles/components/navigation/tabs.css +404 -0
  53. package/src/styles/components/overlay/backdrop.css +243 -0
  54. package/src/styles/components/overlay/index.css +8 -0
  55. package/src/styles/components/overlay/modal.css +482 -0
  56. package/src/styles/components/overlay/popover.css +607 -0
  57. package/src/styles/components/overlay/portal.css +213 -0
  58. package/src/styles/components/overlay/tooltip.css +488 -0
  59. package/src/styles/generated-theme-variables.css +573 -0
  60. package/src/styles/index.css +5 -0
  61. package/src/styles/layers/index.css +54 -0
  62. package/src/styles/layers/overrides.css +108 -0
  63. package/src/styles/layers/validation.css +159 -0
  64. package/src/styles/layers/validation.js +310 -0
  65. package/src/styles/themes/default.css +450 -0
  66. package/src/styles/themes/enterprise.css +370 -0
  67. package/src/styles/themes/harvey.css +436 -0
  68. package/src/styles/themes/index.css +4 -0
  69. package/src/styles/themes/stan-design.css +572 -0
  70. package/src/styles/utilities/advanced-transition-system.css +467 -0
  71. package/src/styles/utilities/battery-conscious-animations.css +289 -0
  72. package/src/styles/utilities/enterprise-mobile-experience.css +817 -0
  73. package/src/styles/utilities/hardware-acceleration.css +121 -0
  74. package/src/styles/utilities/index.css +20 -0
  75. package/src/styles/utilities/mobile-skeleton-loading.css +596 -0
  76. package/src/styles/utilities/semantic-input-system.css +451 -0
  77. package/src/styles/utilities/touch-friendly-interface.css +247 -0
  78. package/src/styles/utilities/touch-optimization.css +165 -0
  79. package/src/test-utils/index.ts +7 -0
  80. package/src/test-utils/theme-testing.tsx +219 -0
  81. package/src/testing/test-automation.ts +627 -0
  82. package/src/testing/test-utils.tsx +367 -0
@@ -0,0 +1,627 @@
1
+ /**
2
+ * Test Automation Scripts
3
+ * Automated testing utilities and scripts for running comprehensive test suites
4
+ */
5
+
6
+ import { spawn, exec } from 'child_process';
7
+ import { promisify } from 'util';
8
+ import fs from 'fs';
9
+ import path from 'path';
10
+
11
+ const execAsync = promisify(exec);
12
+
13
+ export interface TestConfig {
14
+ testTypes: string[];
15
+ browsers?: string[];
16
+ themes?: string[];
17
+ viewports?: string[];
18
+ coverage?: boolean;
19
+ parallel?: boolean;
20
+ timeout?: number;
21
+ }
22
+
23
+ export interface TestResult {
24
+ testType: string;
25
+ passed: number;
26
+ failed: number;
27
+ skipped: number;
28
+ duration: number;
29
+ coverage?: number;
30
+ }
31
+
32
+ export interface TestReport {
33
+ timestamp: string;
34
+ totalTests: number;
35
+ totalPassed: number;
36
+ totalFailed: number;
37
+ totalSkipped: number;
38
+ totalDuration: number;
39
+ results: TestResult[];
40
+ coverageReport?: any;
41
+ }
42
+
43
+ /**
44
+ * Main test automation class
45
+ */
46
+ export class TestAutomation {
47
+ private config: TestConfig;
48
+ private results: TestResult[] = [];
49
+
50
+ constructor(config: TestConfig) {
51
+ this.config = {
52
+ timeout: 30000,
53
+ parallel: true,
54
+ coverage: true,
55
+ ...config,
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Run all configured tests
61
+ */
62
+ async runAllTests(): Promise<TestReport> {
63
+ console.log('๐Ÿš€ Starting comprehensive test suite...');
64
+ const startTime = Date.now();
65
+
66
+ try {
67
+ // Run tests based on configuration
68
+ if (this.config.testTypes.includes('unit')) {
69
+ await this.runUnitTests();
70
+ }
71
+
72
+ if (this.config.testTypes.includes('visual-regression')) {
73
+ await this.runVisualRegressionTests();
74
+ }
75
+
76
+ if (this.config.testTypes.includes('theme-switching')) {
77
+ await this.runThemeSwitchingTests();
78
+ }
79
+
80
+ if (this.config.testTypes.includes('state-testing')) {
81
+ await this.runStateTests();
82
+ }
83
+
84
+ if (this.config.testTypes.includes('responsive')) {
85
+ await this.runResponsiveTests();
86
+ }
87
+
88
+ if (this.config.testTypes.includes('cross-browser')) {
89
+ await this.runCrossBrowserTests();
90
+ }
91
+
92
+ if (this.config.testTypes.includes('accessibility')) {
93
+ await this.runAccessibilityTests();
94
+ }
95
+
96
+ if (this.config.testTypes.includes('performance')) {
97
+ await this.runPerformanceTests();
98
+ }
99
+
100
+ if (this.config.testTypes.includes('e2e')) {
101
+ await this.runE2ETests();
102
+ }
103
+
104
+ const endTime = Date.now();
105
+ const totalDuration = endTime - startTime;
106
+
107
+ const report = this.generateReport(totalDuration);
108
+ await this.saveReport(report);
109
+
110
+ console.log('โœ… Test suite completed successfully!');
111
+ this.printSummary(report);
112
+
113
+ return report;
114
+ } catch (error) {
115
+ console.error('โŒ Test suite failed:', error);
116
+ throw error;
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Run unit tests
122
+ */
123
+ private async runUnitTests(): Promise<void> {
124
+ console.log('๐Ÿงช Running unit tests...');
125
+ const startTime = Date.now();
126
+
127
+ try {
128
+ const command = this.config.coverage
129
+ ? 'npm run test:coverage'
130
+ : 'npm test';
131
+
132
+ const { stdout } = await execAsync(command, {
133
+ timeout: this.config.timeout,
134
+ });
135
+
136
+ const result = this.parseJestOutput(stdout);
137
+ result.testType = 'unit';
138
+ result.duration = Date.now() - startTime;
139
+
140
+ this.results.push(result);
141
+ console.log(`โœ… Unit tests completed: ${result.passed} passed, ${result.failed} failed`);
142
+ } catch (error) {
143
+ console.error('โŒ Unit tests failed:', error);
144
+ this.results.push({
145
+ testType: 'unit',
146
+ passed: 0,
147
+ failed: 1,
148
+ skipped: 0,
149
+ duration: Date.now() - startTime,
150
+ });
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Run visual regression tests
156
+ */
157
+ private async runVisualRegressionTests(): Promise<void> {
158
+ console.log('๐Ÿ“ธ Running visual regression tests...');
159
+ const startTime = Date.now();
160
+
161
+ try {
162
+ const command = 'npm test -- --testPathPatterns=visual-regression';
163
+ const { stdout } = await execAsync(command, {
164
+ timeout: this.config.timeout,
165
+ });
166
+
167
+ const result = this.parseJestOutput(stdout);
168
+ result.testType = 'visual-regression';
169
+ result.duration = Date.now() - startTime;
170
+
171
+ this.results.push(result);
172
+ console.log(`โœ… Visual regression tests completed: ${result.passed} passed, ${result.failed} failed`);
173
+ } catch (error) {
174
+ console.error('โŒ Visual regression tests failed:', error);
175
+ this.results.push({
176
+ testType: 'visual-regression',
177
+ passed: 0,
178
+ failed: 1,
179
+ skipped: 0,
180
+ duration: Date.now() - startTime,
181
+ });
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Run theme switching tests
187
+ */
188
+ private async runThemeSwitchingTests(): Promise<void> {
189
+ console.log('๐ŸŽจ Running theme switching tests...');
190
+ const startTime = Date.now();
191
+
192
+ try {
193
+ const command = 'npm test -- --testPathPatterns=theme-switching';
194
+ const { stdout } = await execAsync(command, {
195
+ timeout: this.config.timeout,
196
+ });
197
+
198
+ const result = this.parseJestOutput(stdout);
199
+ result.testType = 'theme-switching';
200
+ result.duration = Date.now() - startTime;
201
+
202
+ this.results.push(result);
203
+ console.log(`โœ… Theme switching tests completed: ${result.passed} passed, ${result.failed} failed`);
204
+ } catch (error) {
205
+ console.error('โŒ Theme switching tests failed:', error);
206
+ this.results.push({
207
+ testType: 'theme-switching',
208
+ passed: 0,
209
+ failed: 1,
210
+ skipped: 0,
211
+ duration: Date.now() - startTime,
212
+ });
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Run state tests
218
+ */
219
+ private async runStateTests(): Promise<void> {
220
+ console.log('๐Ÿ”„ Running state tests...');
221
+ const startTime = Date.now();
222
+
223
+ try {
224
+ const command = 'npm test -- --testPathPatterns=state-testing';
225
+ const { stdout } = await execAsync(command, {
226
+ timeout: this.config.timeout,
227
+ });
228
+
229
+ const result = this.parseJestOutput(stdout);
230
+ result.testType = 'state-testing';
231
+ result.duration = Date.now() - startTime;
232
+
233
+ this.results.push(result);
234
+ console.log(`โœ… State tests completed: ${result.passed} passed, ${result.failed} failed`);
235
+ } catch (error) {
236
+ console.error('โŒ State tests failed:', error);
237
+ this.results.push({
238
+ testType: 'state-testing',
239
+ passed: 0,
240
+ failed: 1,
241
+ skipped: 0,
242
+ duration: Date.now() - startTime,
243
+ });
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Run responsive tests
249
+ */
250
+ private async runResponsiveTests(): Promise<void> {
251
+ console.log('๐Ÿ“ฑ Running responsive tests...');
252
+ const startTime = Date.now();
253
+
254
+ try {
255
+ const command = 'npm test -- --testPathPatterns=responsive-testing';
256
+ const { stdout } = await execAsync(command, {
257
+ timeout: this.config.timeout,
258
+ });
259
+
260
+ const result = this.parseJestOutput(stdout);
261
+ result.testType = 'responsive';
262
+ result.duration = Date.now() - startTime;
263
+
264
+ this.results.push(result);
265
+ console.log(`โœ… Responsive tests completed: ${result.passed} passed, ${result.failed} failed`);
266
+ } catch (error) {
267
+ console.error('โŒ Responsive tests failed:', error);
268
+ this.results.push({
269
+ testType: 'responsive',
270
+ passed: 0,
271
+ failed: 1,
272
+ skipped: 0,
273
+ duration: Date.now() - startTime,
274
+ });
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Run cross-browser tests
280
+ */
281
+ private async runCrossBrowserTests(): Promise<void> {
282
+ console.log('๐ŸŒ Running cross-browser tests...');
283
+ const startTime = Date.now();
284
+
285
+ try {
286
+ const command = 'npm test -- --testPathPatterns=cross-browser';
287
+ const { stdout } = await execAsync(command, {
288
+ timeout: this.config.timeout,
289
+ });
290
+
291
+ const result = this.parseJestOutput(stdout);
292
+ result.testType = 'cross-browser';
293
+ result.duration = Date.now() - startTime;
294
+
295
+ this.results.push(result);
296
+ console.log(`โœ… Cross-browser tests completed: ${result.passed} passed, ${result.failed} failed`);
297
+ } catch (error) {
298
+ console.error('โŒ Cross-browser tests failed:', error);
299
+ this.results.push({
300
+ testType: 'cross-browser',
301
+ passed: 0,
302
+ failed: 1,
303
+ skipped: 0,
304
+ duration: Date.now() - startTime,
305
+ });
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Run accessibility tests
311
+ */
312
+ private async runAccessibilityTests(): Promise<void> {
313
+ console.log('โ™ฟ Running accessibility tests...');
314
+ const startTime = Date.now();
315
+
316
+ try {
317
+ const command = 'npm test -- --testPathPatterns=accessibility';
318
+ const { stdout } = await execAsync(command, {
319
+ timeout: this.config.timeout,
320
+ });
321
+
322
+ const result = this.parseJestOutput(stdout);
323
+ result.testType = 'accessibility';
324
+ result.duration = Date.now() - startTime;
325
+
326
+ this.results.push(result);
327
+ console.log(`โœ… Accessibility tests completed: ${result.passed} passed, ${result.failed} failed`);
328
+ } catch (error) {
329
+ console.error('โŒ Accessibility tests failed:', error);
330
+ this.results.push({
331
+ testType: 'accessibility',
332
+ passed: 0,
333
+ failed: 1,
334
+ skipped: 0,
335
+ duration: Date.now() - startTime,
336
+ });
337
+ }
338
+ }
339
+
340
+ /**
341
+ * Run performance tests
342
+ */
343
+ private async runPerformanceTests(): Promise<void> {
344
+ console.log('โšก Running performance tests...');
345
+ const startTime = Date.now();
346
+
347
+ try {
348
+ const command = 'npm test -- --testPathPatterns=performance';
349
+ const { stdout } = await execAsync(command, {
350
+ timeout: this.config.timeout,
351
+ });
352
+
353
+ const result = this.parseJestOutput(stdout);
354
+ result.testType = 'performance';
355
+ result.duration = Date.now() - startTime;
356
+
357
+ this.results.push(result);
358
+ console.log(`โœ… Performance tests completed: ${result.passed} passed, ${result.failed} failed`);
359
+ } catch (error) {
360
+ console.error('โŒ Performance tests failed:', error);
361
+ this.results.push({
362
+ testType: 'performance',
363
+ passed: 0,
364
+ failed: 1,
365
+ skipped: 0,
366
+ duration: Date.now() - startTime,
367
+ });
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Run E2E tests (placeholder for future implementation)
373
+ */
374
+ private async runE2ETests(): Promise<void> {
375
+ console.log('๐ŸŽญ Running E2E tests...');
376
+ const startTime = Date.now();
377
+
378
+ try {
379
+ // Placeholder for E2E tests (Playwright, Cypress, etc.)
380
+ console.log('โ„น๏ธ E2E tests not yet configured - skipping');
381
+
382
+ this.results.push({
383
+ testType: 'e2e',
384
+ passed: 0,
385
+ failed: 0,
386
+ skipped: 1,
387
+ duration: Date.now() - startTime,
388
+ });
389
+ } catch (error) {
390
+ console.error('โŒ E2E tests failed:', error);
391
+ this.results.push({
392
+ testType: 'e2e',
393
+ passed: 0,
394
+ failed: 1,
395
+ skipped: 0,
396
+ duration: Date.now() - startTime,
397
+ });
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Parse Jest output to extract test results
403
+ */
404
+ private parseJestOutput(output: string): TestResult {
405
+ const result: TestResult = {
406
+ testType: '',
407
+ passed: 0,
408
+ failed: 0,
409
+ skipped: 0,
410
+ duration: 0,
411
+ };
412
+
413
+ // Parse Jest output for test counts
414
+ const passedMatch = output.match(/(\d+) passed/);
415
+ const failedMatch = output.match(/(\d+) failed/);
416
+ const skippedMatch = output.match(/(\d+) skipped/);
417
+
418
+ if (passedMatch) result.passed = parseInt(passedMatch[1], 10);
419
+ if (failedMatch) result.failed = parseInt(failedMatch[1], 10);
420
+ if (skippedMatch) result.skipped = parseInt(skippedMatch[1], 10);
421
+
422
+ return result;
423
+ }
424
+
425
+ /**
426
+ * Generate comprehensive test report
427
+ */
428
+ private generateReport(totalDuration: number): TestReport {
429
+ const totalPassed = this.results.reduce((sum, r) => sum + r.passed, 0);
430
+ const totalFailed = this.results.reduce((sum, r) => sum + r.failed, 0);
431
+ const totalSkipped = this.results.reduce((sum, r) => sum + r.skipped, 0);
432
+ const totalTests = totalPassed + totalFailed + totalSkipped;
433
+
434
+ return {
435
+ timestamp: new Date().toISOString(),
436
+ totalTests,
437
+ totalPassed,
438
+ totalFailed,
439
+ totalSkipped,
440
+ totalDuration,
441
+ results: this.results,
442
+ };
443
+ }
444
+
445
+ /**
446
+ * Save test report to file
447
+ */
448
+ private async saveReport(report: TestReport): Promise<void> {
449
+ const reportsDir = path.join(process.cwd(), 'test-reports');
450
+
451
+ if (!fs.existsSync(reportsDir)) {
452
+ fs.mkdirSync(reportsDir, { recursive: true });
453
+ }
454
+
455
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
456
+ const reportPath = path.join(reportsDir, `test-report-${timestamp}.json`);
457
+
458
+ fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
459
+ console.log(`๐Ÿ“„ Test report saved to: ${reportPath}`);
460
+ }
461
+
462
+ /**
463
+ * Print test summary to console
464
+ */
465
+ private printSummary(report: TestReport): void {
466
+ console.log('\n๐Ÿ“Š Test Summary:');
467
+ console.log('================');
468
+ console.log(`Total Tests: ${report.totalTests}`);
469
+ console.log(`โœ… Passed: ${report.totalPassed}`);
470
+ console.log(`โŒ Failed: ${report.totalFailed}`);
471
+ console.log(`โญ๏ธ Skipped: ${report.totalSkipped}`);
472
+ console.log(`โฑ๏ธ Duration: ${(report.totalDuration / 1000).toFixed(2)}s`);
473
+ console.log(`๐Ÿ“ˆ Success Rate: ${((report.totalPassed / (report.totalPassed + report.totalFailed)) * 100).toFixed(1)}%`);
474
+
475
+ console.log('\n๐Ÿ“ Test Type Results:');
476
+ console.log('=====================');
477
+ report.results.forEach((result) => {
478
+ const icon = result.failed > 0 ? 'โŒ' : result.passed > 0 ? 'โœ…' : 'โญ๏ธ';
479
+ console.log(`${icon} ${result.testType}: ${result.passed} passed, ${result.failed} failed, ${result.skipped} skipped (${(result.duration / 1000).toFixed(2)}s)`);
480
+ });
481
+ }
482
+ }
483
+
484
+ /**
485
+ * Predefined test configurations
486
+ */
487
+ export const testConfigurations = {
488
+ quick: {
489
+ testTypes: ['unit', 'visual-regression'],
490
+ parallel: true,
491
+ timeout: 30000,
492
+ },
493
+ comprehensive: {
494
+ testTypes: [
495
+ 'unit',
496
+ 'visual-regression',
497
+ 'theme-switching',
498
+ 'state-testing',
499
+ 'responsive',
500
+ 'cross-browser',
501
+ 'accessibility',
502
+ 'performance',
503
+ ],
504
+ parallel: true,
505
+ coverage: true,
506
+ timeout: 60000,
507
+ },
508
+ ci: {
509
+ testTypes: [
510
+ 'unit',
511
+ 'visual-regression',
512
+ 'theme-switching',
513
+ 'state-testing',
514
+ 'responsive',
515
+ 'accessibility',
516
+ ],
517
+ parallel: true,
518
+ coverage: true,
519
+ timeout: 45000,
520
+ },
521
+ accessibility: {
522
+ testTypes: ['accessibility'],
523
+ timeout: 30000,
524
+ },
525
+ performance: {
526
+ testTypes: ['performance'],
527
+ timeout: 45000,
528
+ },
529
+ };
530
+
531
+ /**
532
+ * CLI utility functions
533
+ */
534
+ export class TestCLI {
535
+ static async runConfiguration(configName: keyof typeof testConfigurations): Promise<void> {
536
+ const config = testConfigurations[configName];
537
+ if (!config) {
538
+ throw new Error(`Unknown configuration: ${configName}`);
539
+ }
540
+
541
+ const automation = new TestAutomation(config);
542
+ await automation.runAllTests();
543
+ }
544
+
545
+ static async runCustom(customConfig: TestConfig): Promise<void> {
546
+ const automation = new TestAutomation(customConfig);
547
+ await automation.runAllTests();
548
+ }
549
+
550
+ static listConfigurations(): void {
551
+ console.log('Available test configurations:');
552
+ console.log('==============================');
553
+ Object.keys(testConfigurations).forEach((name) => {
554
+ const config = testConfigurations[name as keyof typeof testConfigurations];
555
+ console.log(`โ€ข ${name}: ${config.testTypes.join(', ')}`);
556
+ });
557
+ }
558
+ }
559
+
560
+ /**
561
+ * Utility functions for individual test operations
562
+ */
563
+ export const testUtils = {
564
+ /**
565
+ * Run tests with coverage
566
+ */
567
+ async runWithCoverage(): Promise<void> {
568
+ console.log('๐Ÿ“Š Running tests with coverage...');
569
+ try {
570
+ await execAsync('npm run test:coverage');
571
+ console.log('โœ… Coverage report generated');
572
+ } catch (error) {
573
+ console.error('โŒ Coverage test failed:', error);
574
+ throw error;
575
+ }
576
+ },
577
+
578
+ /**
579
+ * Update visual regression snapshots
580
+ */
581
+ async updateSnapshots(): Promise<void> {
582
+ console.log('๐Ÿ“ธ Updating visual regression snapshots...');
583
+ try {
584
+ await execAsync('npm test -- --updateSnapshot');
585
+ console.log('โœ… Snapshots updated');
586
+ } catch (error) {
587
+ console.error('โŒ Snapshot update failed:', error);
588
+ throw error;
589
+ }
590
+ },
591
+
592
+ /**
593
+ * Run tests in watch mode
594
+ */
595
+ async runInWatchMode(): Promise<void> {
596
+ console.log('๐Ÿ‘€ Starting tests in watch mode...');
597
+ try {
598
+ const child = spawn('npm', ['test', '--', '--watch'], {
599
+ stdio: 'inherit',
600
+ });
601
+
602
+ child.on('close', (code) => {
603
+ console.log(`Watch mode exited with code ${code}`);
604
+ });
605
+ } catch (error) {
606
+ console.error('โŒ Watch mode failed:', error);
607
+ throw error;
608
+ }
609
+ },
610
+
611
+ /**
612
+ * Clean test artifacts
613
+ */
614
+ async cleanTestArtifacts(): Promise<void> {
615
+ console.log('๐Ÿงน Cleaning test artifacts...');
616
+ try {
617
+ await execAsync('rm -rf coverage test-reports __snapshots__');
618
+ console.log('โœ… Test artifacts cleaned');
619
+ } catch (error) {
620
+ console.error('โŒ Cleanup failed:', error);
621
+ throw error;
622
+ }
623
+ },
624
+ };
625
+
626
+ // Export main automation class as default
627
+ export default TestAutomation;