@hyperfixi/testing-framework 2.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 (37) hide show
  1. package/dist/assertions-BImuwZsB.d.mts +458 -0
  2. package/dist/assertions-BImuwZsB.d.ts +458 -0
  3. package/dist/assertions.d.mts +1 -0
  4. package/dist/assertions.d.ts +1 -0
  5. package/dist/assertions.js +625 -0
  6. package/dist/assertions.js.map +1 -0
  7. package/dist/assertions.mjs +621 -0
  8. package/dist/assertions.mjs.map +1 -0
  9. package/dist/index.d.mts +246 -0
  10. package/dist/index.d.ts +246 -0
  11. package/dist/index.js +1465 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/index.mjs +1435 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/package.json +121 -0
  16. package/src/assertions.test.ts +490 -0
  17. package/src/assertions.ts +779 -0
  18. package/src/index.test.ts +490 -0
  19. package/src/index.ts +601 -0
  20. package/src/jsdom.d.ts +7 -0
  21. package/src/multilingual/README.md +295 -0
  22. package/src/multilingual/bundle-builder.ts +345 -0
  23. package/src/multilingual/cli.ts +224 -0
  24. package/src/multilingual/index.ts +68 -0
  25. package/src/multilingual/orchestrator.ts +275 -0
  26. package/src/multilingual/pattern-loader.ts +176 -0
  27. package/src/multilingual/reporters/console-reporter.ts +294 -0
  28. package/src/multilingual/reporters/json-reporter.ts +99 -0
  29. package/src/multilingual/reporters/regression-reporter.ts +263 -0
  30. package/src/multilingual/tools/analyze-failures.ts +326 -0
  31. package/src/multilingual/types.ts +268 -0
  32. package/src/multilingual/validators/parse-diagnostics.ts +337 -0
  33. package/src/multilingual/validators/parse-validator.ts +193 -0
  34. package/src/multilingual/validators/size-validator.ts +187 -0
  35. package/src/runner.test.ts +813 -0
  36. package/src/runner.ts +639 -0
  37. package/src/types.ts +505 -0
@@ -0,0 +1,813 @@
1
+ /**
2
+ * Runner Tests
3
+ * Tests for CoreTestRunner, ParallelTestRunner, createTestRunner, and measurePerformance
4
+ */
5
+
6
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
7
+ import { CoreTestRunner, ParallelTestRunner, createTestRunner, measurePerformance } from './runner';
8
+ import type { TestSuite, TestCase, TestConfig, TestContext } from './types';
9
+
10
+ // Mock jsdom
11
+ vi.mock('jsdom', () => ({
12
+ JSDOM: class {
13
+ window = {
14
+ document: {
15
+ createElement: vi.fn(),
16
+ body: {},
17
+ },
18
+ navigator: {},
19
+ location: { href: 'http://localhost' },
20
+ HTMLElement: class {},
21
+ Event: class {},
22
+ CustomEvent: class {},
23
+ };
24
+ },
25
+ }));
26
+
27
+ // Mock playwright
28
+ vi.mock('playwright', () => ({
29
+ chromium: {
30
+ launch: vi.fn().mockResolvedValue({
31
+ newContext: vi.fn().mockResolvedValue({
32
+ newPage: vi.fn().mockResolvedValue({
33
+ on: vi.fn(),
34
+ screenshot: vi.fn().mockResolvedValue(undefined),
35
+ goto: vi.fn().mockResolvedValue(undefined),
36
+ }),
37
+ }),
38
+ close: vi.fn().mockResolvedValue(undefined),
39
+ }),
40
+ },
41
+ firefox: {
42
+ launch: vi.fn().mockResolvedValue({
43
+ newContext: vi.fn().mockResolvedValue({
44
+ newPage: vi.fn().mockResolvedValue({
45
+ on: vi.fn(),
46
+ }),
47
+ }),
48
+ close: vi.fn().mockResolvedValue(undefined),
49
+ }),
50
+ },
51
+ webkit: {
52
+ launch: vi.fn().mockResolvedValue({
53
+ newContext: vi.fn().mockResolvedValue({
54
+ newPage: vi.fn().mockResolvedValue({
55
+ on: vi.fn(),
56
+ }),
57
+ }),
58
+ close: vi.fn().mockResolvedValue(undefined),
59
+ }),
60
+ },
61
+ }));
62
+
63
+ // Mock puppeteer
64
+ vi.mock('puppeteer', () => ({
65
+ default: {
66
+ launch: vi.fn().mockResolvedValue({
67
+ newPage: vi.fn().mockResolvedValue({
68
+ setViewport: vi.fn().mockResolvedValue(undefined),
69
+ goto: vi.fn().mockResolvedValue(undefined),
70
+ on: vi.fn(),
71
+ }),
72
+ close: vi.fn().mockResolvedValue(undefined),
73
+ }),
74
+ },
75
+ }));
76
+
77
+ describe('CoreTestRunner', () => {
78
+ let runner: CoreTestRunner;
79
+
80
+ beforeEach(() => {
81
+ runner = new CoreTestRunner();
82
+ vi.clearAllMocks();
83
+ });
84
+
85
+ describe('constructor', () => {
86
+ it('should create a CoreTestRunner instance', () => {
87
+ expect(runner).toBeInstanceOf(CoreTestRunner);
88
+ });
89
+ });
90
+
91
+ describe('run', () => {
92
+ it('should run all test suites and return results', async () => {
93
+ const suite: TestSuite = {
94
+ name: 'Test Suite',
95
+ tests: [
96
+ { name: 'test 1', fn: async () => {}, tags: [], fixtures: {} },
97
+ { name: 'test 2', fn: async () => {}, tags: [], fixtures: {} },
98
+ ],
99
+ suites: [],
100
+ };
101
+
102
+ const config: TestConfig = {
103
+ environment: 'node',
104
+ browser: 'chromium',
105
+ timeout: 5000,
106
+ retries: 0,
107
+ parallel: false,
108
+ maxWorkers: 1,
109
+ headless: true,
110
+ viewport: { width: 1280, height: 720 },
111
+ setupFiles: [],
112
+ teardownFiles: [],
113
+ reporters: [],
114
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
115
+ fixtures: {},
116
+ };
117
+
118
+ const results = await runner.run([suite], config);
119
+
120
+ expect(results).toHaveLength(2);
121
+ expect(results[0].suite).toBe('Test Suite');
122
+ expect(results[0].test).toBe('test 1');
123
+ expect(results[1].test).toBe('test 2');
124
+ });
125
+
126
+ it('should handle empty suite list', async () => {
127
+ const config: TestConfig = {
128
+ environment: 'node',
129
+ browser: 'chromium',
130
+ timeout: 5000,
131
+ retries: 0,
132
+ parallel: false,
133
+ maxWorkers: 1,
134
+ headless: true,
135
+ viewport: { width: 1280, height: 720 },
136
+ setupFiles: [],
137
+ teardownFiles: [],
138
+ reporters: [],
139
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
140
+ fixtures: {},
141
+ };
142
+
143
+ const results = await runner.run([], config);
144
+ expect(results).toHaveLength(0);
145
+ });
146
+ });
147
+
148
+ describe('runSuite', () => {
149
+ it('should skip suite when marked as skip', async () => {
150
+ const suite: TestSuite = {
151
+ name: 'Skipped Suite',
152
+ tests: [{ name: 'test 1', fn: async () => {}, tags: [], fixtures: {} }],
153
+ suites: [],
154
+ skip: true,
155
+ };
156
+
157
+ const config: TestConfig = {
158
+ environment: 'node',
159
+ browser: 'chromium',
160
+ timeout: 5000,
161
+ retries: 0,
162
+ parallel: false,
163
+ maxWorkers: 1,
164
+ headless: true,
165
+ viewport: { width: 1280, height: 720 },
166
+ setupFiles: [],
167
+ teardownFiles: [],
168
+ reporters: [],
169
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
170
+ fixtures: {},
171
+ };
172
+
173
+ const results = await runner.runSuite(suite, config);
174
+
175
+ expect(results).toHaveLength(1);
176
+ expect(results[0].status).toBe('skipped');
177
+ });
178
+
179
+ it('should run nested suites', async () => {
180
+ const nestedSuite: TestSuite = {
181
+ name: 'Nested Suite',
182
+ tests: [{ name: 'nested test', fn: async () => {}, tags: [], fixtures: {} }],
183
+ suites: [],
184
+ };
185
+
186
+ const parentSuite: TestSuite = {
187
+ name: 'Parent Suite',
188
+ tests: [{ name: 'parent test', fn: async () => {}, tags: [], fixtures: {} }],
189
+ suites: [nestedSuite],
190
+ };
191
+
192
+ const config: TestConfig = {
193
+ environment: 'node',
194
+ browser: 'chromium',
195
+ timeout: 5000,
196
+ retries: 0,
197
+ parallel: false,
198
+ maxWorkers: 1,
199
+ headless: true,
200
+ viewport: { width: 1280, height: 720 },
201
+ setupFiles: [],
202
+ teardownFiles: [],
203
+ reporters: [],
204
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
205
+ fixtures: {},
206
+ };
207
+
208
+ const results = await runner.runSuite(parentSuite, config);
209
+
210
+ expect(results).toHaveLength(2);
211
+ expect(results[0].suite).toBe('Parent Suite');
212
+ expect(results[1].suite).toBe('Nested Suite');
213
+ });
214
+
215
+ it('should run setup and teardown functions', async () => {
216
+ const setupCalled = { value: false };
217
+ const teardownCalled = { value: false };
218
+
219
+ const suite: TestSuite = {
220
+ name: 'Suite with hooks',
221
+ tests: [{ name: 'test', fn: async () => {}, tags: [], fixtures: {} }],
222
+ suites: [],
223
+ setup: async () => {
224
+ setupCalled.value = true;
225
+ },
226
+ teardown: async () => {
227
+ teardownCalled.value = true;
228
+ },
229
+ };
230
+
231
+ const config: TestConfig = {
232
+ environment: 'node',
233
+ browser: 'chromium',
234
+ timeout: 5000,
235
+ retries: 0,
236
+ parallel: false,
237
+ maxWorkers: 1,
238
+ headless: true,
239
+ viewport: { width: 1280, height: 720 },
240
+ setupFiles: [],
241
+ teardownFiles: [],
242
+ reporters: [],
243
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
244
+ fixtures: {},
245
+ };
246
+
247
+ await runner.runSuite(suite, config);
248
+
249
+ expect(setupCalled.value).toBe(true);
250
+ expect(teardownCalled.value).toBe(true);
251
+ });
252
+
253
+ it('should handle setup errors', async () => {
254
+ const suite: TestSuite = {
255
+ name: 'Suite with failing setup',
256
+ tests: [{ name: 'test', fn: async () => {}, tags: [], fixtures: {} }],
257
+ suites: [],
258
+ setup: async () => {
259
+ throw new Error('Setup failed');
260
+ },
261
+ };
262
+
263
+ const config: TestConfig = {
264
+ environment: 'node',
265
+ browser: 'chromium',
266
+ timeout: 5000,
267
+ retries: 0,
268
+ parallel: false,
269
+ maxWorkers: 1,
270
+ headless: true,
271
+ viewport: { width: 1280, height: 720 },
272
+ setupFiles: [],
273
+ teardownFiles: [],
274
+ reporters: [],
275
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
276
+ fixtures: {},
277
+ };
278
+
279
+ const results = await runner.runSuite(suite, config);
280
+
281
+ expect(results).toHaveLength(1);
282
+ expect(results[0].status).toBe('failed');
283
+ expect(results[0].error?.message).toBe('Setup failed');
284
+ });
285
+ });
286
+
287
+ describe('runTest', () => {
288
+ it('should skip test when marked as skip', async () => {
289
+ const test: TestCase = {
290
+ name: 'skipped test',
291
+ fn: async () => {},
292
+ skip: true,
293
+ tags: [],
294
+ fixtures: {},
295
+ };
296
+
297
+ const context: TestContext = {
298
+ suite: 'Test Suite',
299
+ test: 'skipped test',
300
+ environment: 'node',
301
+ fixtures: {},
302
+ expect: {} as any,
303
+ assert: {} as any,
304
+ skip: () => {
305
+ throw new Error('SKIP_TEST');
306
+ },
307
+ fail: (msg: string) => {
308
+ throw new Error(msg);
309
+ },
310
+ timeout: () => {},
311
+ };
312
+
313
+ const result = await runner.runTest(test, context);
314
+
315
+ expect(result.status).toBe('skipped');
316
+ });
317
+
318
+ it('should mark test as passed on success', async () => {
319
+ const test: TestCase = {
320
+ name: 'passing test',
321
+ fn: async () => {},
322
+ tags: [],
323
+ fixtures: {},
324
+ };
325
+
326
+ const context: TestContext = {
327
+ suite: 'Test Suite',
328
+ test: 'passing test',
329
+ environment: 'node',
330
+ fixtures: {},
331
+ expect: {} as any,
332
+ assert: {} as any,
333
+ skip: () => {
334
+ throw new Error('SKIP_TEST');
335
+ },
336
+ fail: (msg: string) => {
337
+ throw new Error(msg);
338
+ },
339
+ timeout: () => {},
340
+ };
341
+
342
+ const result = await runner.runTest(test, context);
343
+
344
+ expect(result.status).toBe('passed');
345
+ expect(result.error).toBeUndefined();
346
+ });
347
+
348
+ it('should mark test as failed on error', async () => {
349
+ const test: TestCase = {
350
+ name: 'failing test',
351
+ fn: async () => {
352
+ throw new Error('Test failed');
353
+ },
354
+ tags: [],
355
+ fixtures: {},
356
+ };
357
+
358
+ const context: TestContext = {
359
+ suite: 'Test Suite',
360
+ test: 'failing test',
361
+ environment: 'node',
362
+ fixtures: {},
363
+ expect: {} as any,
364
+ assert: {} as any,
365
+ skip: () => {
366
+ throw new Error('SKIP_TEST');
367
+ },
368
+ fail: (msg: string) => {
369
+ throw new Error(msg);
370
+ },
371
+ timeout: () => {},
372
+ };
373
+
374
+ const result = await runner.runTest(test, context);
375
+
376
+ expect(result.status).toBe('failed');
377
+ expect(result.error?.message).toBe('Test failed');
378
+ });
379
+
380
+ it('should handle test timeout', async () => {
381
+ const test: TestCase = {
382
+ name: 'slow test',
383
+ fn: async () => {
384
+ await new Promise(r => setTimeout(r, 200));
385
+ },
386
+ timeout: 50,
387
+ tags: [],
388
+ fixtures: {},
389
+ };
390
+
391
+ const context: TestContext = {
392
+ suite: 'Test Suite',
393
+ test: 'slow test',
394
+ environment: 'node',
395
+ fixtures: {},
396
+ expect: {} as any,
397
+ assert: {} as any,
398
+ skip: () => {
399
+ throw new Error('SKIP_TEST');
400
+ },
401
+ fail: (msg: string) => {
402
+ throw new Error(msg);
403
+ },
404
+ timeout: () => {},
405
+ };
406
+
407
+ const result = await runner.runTest(test, context);
408
+
409
+ expect(result.status).toBe('failed');
410
+ expect(result.error?.message).toContain('timeout');
411
+ });
412
+
413
+ it('should record test duration', async () => {
414
+ const test: TestCase = {
415
+ name: 'timed test',
416
+ fn: async () => {
417
+ await new Promise(r => setTimeout(r, 50));
418
+ },
419
+ tags: [],
420
+ fixtures: {},
421
+ };
422
+
423
+ const context: TestContext = {
424
+ suite: 'Test Suite',
425
+ test: 'timed test',
426
+ environment: 'node',
427
+ fixtures: {},
428
+ expect: {} as any,
429
+ assert: {} as any,
430
+ skip: () => {
431
+ throw new Error('SKIP_TEST');
432
+ },
433
+ fail: (msg: string) => {
434
+ throw new Error(msg);
435
+ },
436
+ timeout: () => {},
437
+ };
438
+
439
+ const result = await runner.runTest(test, context);
440
+
441
+ expect(result.duration).toBeGreaterThanOrEqual(50);
442
+ });
443
+ });
444
+ });
445
+
446
+ describe('ParallelTestRunner', () => {
447
+ let runner: ParallelTestRunner;
448
+
449
+ beforeEach(() => {
450
+ runner = new ParallelTestRunner();
451
+ vi.clearAllMocks();
452
+ });
453
+
454
+ describe('constructor', () => {
455
+ it('should create a ParallelTestRunner instance', () => {
456
+ expect(runner).toBeInstanceOf(ParallelTestRunner);
457
+ expect(runner).toBeInstanceOf(CoreTestRunner);
458
+ });
459
+ });
460
+
461
+ describe('run', () => {
462
+ it('should fall back to sequential when parallel is false', async () => {
463
+ const suite: TestSuite = {
464
+ name: 'Suite',
465
+ tests: [{ name: 'test', fn: async () => {}, tags: [], fixtures: {} }],
466
+ suites: [],
467
+ };
468
+
469
+ const config: TestConfig = {
470
+ environment: 'node',
471
+ browser: 'chromium',
472
+ timeout: 5000,
473
+ retries: 0,
474
+ parallel: false,
475
+ maxWorkers: 1,
476
+ headless: true,
477
+ viewport: { width: 1280, height: 720 },
478
+ setupFiles: [],
479
+ teardownFiles: [],
480
+ reporters: [],
481
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
482
+ fixtures: {},
483
+ };
484
+
485
+ const results = await runner.run([suite], config);
486
+
487
+ expect(results).toHaveLength(1);
488
+ });
489
+
490
+ it('should fall back to sequential when maxWorkers is 1', async () => {
491
+ const suite: TestSuite = {
492
+ name: 'Suite',
493
+ tests: [{ name: 'test', fn: async () => {}, tags: [], fixtures: {} }],
494
+ suites: [],
495
+ };
496
+
497
+ const config: TestConfig = {
498
+ environment: 'node',
499
+ browser: 'chromium',
500
+ timeout: 5000,
501
+ retries: 0,
502
+ parallel: true,
503
+ maxWorkers: 1,
504
+ headless: true,
505
+ viewport: { width: 1280, height: 720 },
506
+ setupFiles: [],
507
+ teardownFiles: [],
508
+ reporters: [],
509
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
510
+ fixtures: {},
511
+ };
512
+
513
+ const results = await runner.run([suite], config);
514
+
515
+ expect(results).toHaveLength(1);
516
+ });
517
+
518
+ it('should run suites in parallel when enabled', async () => {
519
+ const suite1: TestSuite = {
520
+ name: 'Suite 1',
521
+ tests: [{ name: 'test 1', fn: async () => {}, tags: [], fixtures: {} }],
522
+ suites: [],
523
+ };
524
+
525
+ const suite2: TestSuite = {
526
+ name: 'Suite 2',
527
+ tests: [{ name: 'test 2', fn: async () => {}, tags: [], fixtures: {} }],
528
+ suites: [],
529
+ };
530
+
531
+ const config: TestConfig = {
532
+ environment: 'node',
533
+ browser: 'chromium',
534
+ timeout: 5000,
535
+ retries: 0,
536
+ parallel: true,
537
+ maxWorkers: 4,
538
+ headless: true,
539
+ viewport: { width: 1280, height: 720 },
540
+ setupFiles: [],
541
+ teardownFiles: [],
542
+ reporters: [],
543
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
544
+ fixtures: {},
545
+ };
546
+
547
+ const results = await runner.run([suite1, suite2], config);
548
+
549
+ expect(results).toHaveLength(2);
550
+ expect(results.map(r => r.suite)).toContain('Suite 1');
551
+ expect(results.map(r => r.suite)).toContain('Suite 2');
552
+ });
553
+ });
554
+ });
555
+
556
+ describe('createTestRunner', () => {
557
+ it('should return CoreTestRunner when parallel is disabled', () => {
558
+ const config: TestConfig = {
559
+ environment: 'node',
560
+ browser: 'chromium',
561
+ timeout: 5000,
562
+ retries: 0,
563
+ parallel: false,
564
+ maxWorkers: 1,
565
+ headless: true,
566
+ viewport: { width: 1280, height: 720 },
567
+ setupFiles: [],
568
+ teardownFiles: [],
569
+ reporters: [],
570
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
571
+ fixtures: {},
572
+ };
573
+
574
+ const runner = createTestRunner(config);
575
+
576
+ expect(runner).toBeInstanceOf(CoreTestRunner);
577
+ expect(runner).not.toBeInstanceOf(ParallelTestRunner);
578
+ });
579
+
580
+ it('should return CoreTestRunner when maxWorkers is 1', () => {
581
+ const config: TestConfig = {
582
+ environment: 'node',
583
+ browser: 'chromium',
584
+ timeout: 5000,
585
+ retries: 0,
586
+ parallel: true,
587
+ maxWorkers: 1,
588
+ headless: true,
589
+ viewport: { width: 1280, height: 720 },
590
+ setupFiles: [],
591
+ teardownFiles: [],
592
+ reporters: [],
593
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
594
+ fixtures: {},
595
+ };
596
+
597
+ const runner = createTestRunner(config);
598
+
599
+ expect(runner).toBeInstanceOf(CoreTestRunner);
600
+ });
601
+
602
+ it('should return ParallelTestRunner when parallel and maxWorkers > 1', () => {
603
+ const config: TestConfig = {
604
+ environment: 'node',
605
+ browser: 'chromium',
606
+ timeout: 5000,
607
+ retries: 0,
608
+ parallel: true,
609
+ maxWorkers: 4,
610
+ headless: true,
611
+ viewport: { width: 1280, height: 720 },
612
+ setupFiles: [],
613
+ teardownFiles: [],
614
+ reporters: [],
615
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
616
+ fixtures: {},
617
+ };
618
+
619
+ const runner = createTestRunner(config);
620
+
621
+ expect(runner).toBeInstanceOf(ParallelTestRunner);
622
+ });
623
+ });
624
+
625
+ describe('measurePerformance', () => {
626
+ it('should execute test function and return metrics', async () => {
627
+ let executed = false;
628
+ const mockPage = {
629
+ evaluate: vi
630
+ .fn()
631
+ .mockResolvedValueOnce(undefined) // First call to clear marks
632
+ .mockResolvedValueOnce({
633
+ loadTime: 100,
634
+ domContentLoaded: 50,
635
+ firstPaint: 25,
636
+ firstContentfulPaint: 30,
637
+ largestContentfulPaint: 0,
638
+ cumulativeLayoutShift: 0,
639
+ firstInputDelay: 0,
640
+ memoryUsage: {
641
+ usedJSHeapSize: 1000000,
642
+ totalJSHeapSize: 2000000,
643
+ jsHeapSizeLimit: 4000000,
644
+ },
645
+ }),
646
+ };
647
+
648
+ const testFn = async () => {
649
+ executed = true;
650
+ };
651
+
652
+ const metrics = await measurePerformance(mockPage, testFn);
653
+
654
+ expect(executed).toBe(true);
655
+ expect(metrics.loadTime).toBeDefined();
656
+ expect(metrics.domContentLoaded).toBeDefined();
657
+ expect(metrics.firstPaint).toBeDefined();
658
+ expect(metrics.firstContentfulPaint).toBeDefined();
659
+ expect(metrics.memoryUsage).toBeDefined();
660
+ });
661
+
662
+ it('should use elapsed time when loadTime is 0', async () => {
663
+ const mockPage = {
664
+ evaluate: vi
665
+ .fn()
666
+ .mockResolvedValueOnce(undefined)
667
+ .mockResolvedValueOnce({
668
+ loadTime: 0,
669
+ domContentLoaded: 0,
670
+ firstPaint: 0,
671
+ firstContentfulPaint: 0,
672
+ largestContentfulPaint: 0,
673
+ cumulativeLayoutShift: 0,
674
+ firstInputDelay: 0,
675
+ memoryUsage: {
676
+ usedJSHeapSize: 0,
677
+ totalJSHeapSize: 0,
678
+ jsHeapSizeLimit: 0,
679
+ },
680
+ }),
681
+ };
682
+
683
+ const testFn = async () => {
684
+ await new Promise(r => setTimeout(r, 50));
685
+ };
686
+
687
+ const metrics = await measurePerformance(mockPage, testFn);
688
+
689
+ expect(metrics.loadTime).toBeGreaterThanOrEqual(50);
690
+ });
691
+ });
692
+
693
+ describe('Environment Setup', () => {
694
+ let runner: CoreTestRunner;
695
+
696
+ beforeEach(() => {
697
+ runner = new CoreTestRunner();
698
+ vi.clearAllMocks();
699
+ });
700
+
701
+ it('should setup jsdom environment', async () => {
702
+ const suite: TestSuite = {
703
+ name: 'JSDOM Suite',
704
+ tests: [{ name: 'test', fn: async () => {}, tags: [], fixtures: {} }],
705
+ suites: [],
706
+ };
707
+
708
+ const config: TestConfig = {
709
+ environment: 'jsdom',
710
+ browser: 'chromium',
711
+ timeout: 5000,
712
+ retries: 0,
713
+ parallel: false,
714
+ maxWorkers: 1,
715
+ headless: true,
716
+ viewport: { width: 1280, height: 720 },
717
+ setupFiles: [],
718
+ teardownFiles: [],
719
+ reporters: [],
720
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
721
+ fixtures: {},
722
+ };
723
+
724
+ const results = await runner.run([suite], config);
725
+
726
+ expect(results).toHaveLength(1);
727
+ expect(results[0].status).toBe('passed');
728
+ });
729
+
730
+ it('should setup browser environment with playwright', async () => {
731
+ const suite: TestSuite = {
732
+ name: 'Browser Suite',
733
+ tests: [{ name: 'test', fn: async () => {}, tags: [], fixtures: {} }],
734
+ suites: [],
735
+ };
736
+
737
+ const config: TestConfig = {
738
+ environment: 'browser',
739
+ browser: 'chromium',
740
+ timeout: 5000,
741
+ retries: 0,
742
+ parallel: false,
743
+ maxWorkers: 1,
744
+ headless: true,
745
+ viewport: { width: 1280, height: 720 },
746
+ setupFiles: [],
747
+ teardownFiles: [],
748
+ reporters: [],
749
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
750
+ fixtures: {},
751
+ };
752
+
753
+ const results = await runner.run([suite], config);
754
+
755
+ expect(results).toHaveLength(1);
756
+ });
757
+
758
+ it('should support firefox browser', async () => {
759
+ const suite: TestSuite = {
760
+ name: 'Firefox Suite',
761
+ tests: [{ name: 'test', fn: async () => {}, tags: [], fixtures: {} }],
762
+ suites: [],
763
+ };
764
+
765
+ const config: TestConfig = {
766
+ environment: 'browser',
767
+ browser: 'firefox',
768
+ timeout: 5000,
769
+ retries: 0,
770
+ parallel: false,
771
+ maxWorkers: 1,
772
+ headless: true,
773
+ viewport: { width: 1280, height: 720 },
774
+ setupFiles: [],
775
+ teardownFiles: [],
776
+ reporters: [],
777
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
778
+ fixtures: {},
779
+ };
780
+
781
+ const results = await runner.run([suite], config);
782
+
783
+ expect(results).toHaveLength(1);
784
+ });
785
+
786
+ it('should support webkit browser', async () => {
787
+ const suite: TestSuite = {
788
+ name: 'Webkit Suite',
789
+ tests: [{ name: 'test', fn: async () => {}, tags: [], fixtures: {} }],
790
+ suites: [],
791
+ };
792
+
793
+ const config: TestConfig = {
794
+ environment: 'browser',
795
+ browser: 'webkit',
796
+ timeout: 5000,
797
+ retries: 0,
798
+ parallel: false,
799
+ maxWorkers: 1,
800
+ headless: true,
801
+ viewport: { width: 1280, height: 720 },
802
+ setupFiles: [],
803
+ teardownFiles: [],
804
+ reporters: [],
805
+ coverage: { enabled: false, threshold: 80, include: [], exclude: [] },
806
+ fixtures: {},
807
+ };
808
+
809
+ const results = await runner.run([suite], config);
810
+
811
+ expect(results).toHaveLength(1);
812
+ });
813
+ });