@testsmith/testblocks 0.8.3 → 0.8.5

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 (2) hide show
  1. package/dist/cli/index.js +126 -64
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -177,7 +177,7 @@ program
177
177
  .argument('<patterns...>', 'Test file patterns (glob supported)')
178
178
  .option('-H, --headed', 'Run tests in headed mode (show browser)', false)
179
179
  .option('-t, --timeout <ms>', 'Test timeout in milliseconds', '30000')
180
- .option('-r, --reporter <type>', 'Reporter type: console, json, junit, html', 'console')
180
+ .option('-r, --reporter <types>', 'Reporter types (comma-separated): console, json, junit, html', 'console')
181
181
  .option('-o, --output <dir>', 'Output directory for reports', './testblocks-results')
182
182
  .option('-b, --base-url <url>', 'Base URL for relative URLs')
183
183
  .option('-v, --var <vars...>', 'Variables in key=value format')
@@ -275,57 +275,65 @@ program
275
275
  variables,
276
276
  procedures: globalProcedures,
277
277
  };
278
- // Create reporter
279
- const reporter = createReporter(options.reporter, options.output);
278
+ // Create reporters (supports multiple, comma-separated)
279
+ const reporters = createReporters(options.reporter, options.output);
280
280
  // Run tests
281
281
  const allResults = [];
282
282
  let hasFailures = false;
283
- for (const file of files) {
284
- // Skip _hooks.testblocks.json files - these are folder hooks, not test files
285
- const basename = path.basename(file);
286
- if (basename === '_hooks.testblocks.json') {
287
- continue;
288
- }
289
- console.log(`Running: ${basename}`);
290
- const content = fs.readFileSync(file, 'utf-8');
291
- let testFile = JSON.parse(content);
292
- // Skip files that have no tests array (e.g., hooks-only files)
293
- if (!testFile.tests || !Array.isArray(testFile.tests)) {
294
- console.log(' (no tests in file)\n');
295
- continue;
296
- }
297
- // Load and merge folder hooks
298
- const globalsDir = fs.existsSync(globalsPath) ? path.dirname(globalsPath) : null;
299
- const folderHooks = loadFolderHooks(file, globalsDir);
300
- if (folderHooks.length > 0) {
301
- testFile = mergeFolderHooksIntoTestFile(testFile, folderHooks);
302
- }
303
- // Apply filter if specified
304
- if (options.filter) {
305
- const filterRegex = new RegExp(options.filter, 'i');
306
- testFile.tests = testFile.tests.filter(t => filterRegex.test(t.name));
307
- }
308
- if (testFile.tests.length === 0) {
309
- console.log(' (no tests match filter)\n');
310
- continue;
311
- }
312
- const executor = new executor_1.TestExecutor(executorOptions);
313
- const results = await executor.runTestFile(testFile);
314
- allResults.push({ file, results });
315
- // Report results
316
- reporter.onTestFileComplete(file, testFile, results);
317
- // Check for failures
318
- const failed = results.some(r => r.status !== 'passed');
319
- if (failed) {
320
- hasFailures = true;
321
- if (options.failFast) {
322
- console.log('\nStopping due to --fail-fast\n');
323
- break;
283
+ try {
284
+ for (const file of files) {
285
+ // Skip _hooks.testblocks.json files - these are folder hooks, not test files
286
+ const basename = path.basename(file);
287
+ if (basename === '_hooks.testblocks.json') {
288
+ continue;
289
+ }
290
+ console.log(`Running: ${basename}`);
291
+ const content = fs.readFileSync(file, 'utf-8');
292
+ let testFile = JSON.parse(content);
293
+ // Skip files that have no tests array (e.g., hooks-only files)
294
+ if (!testFile.tests || !Array.isArray(testFile.tests)) {
295
+ console.log(' (no tests in file)\n');
296
+ continue;
297
+ }
298
+ // Load and merge folder hooks
299
+ const globalsDir = fs.existsSync(globalsPath) ? path.dirname(globalsPath) : null;
300
+ const folderHooks = loadFolderHooks(file, globalsDir);
301
+ if (folderHooks.length > 0) {
302
+ testFile = mergeFolderHooksIntoTestFile(testFile, folderHooks);
303
+ }
304
+ // Apply filter if specified
305
+ if (options.filter) {
306
+ const filterRegex = new RegExp(options.filter, 'i');
307
+ testFile.tests = testFile.tests.filter(t => filterRegex.test(t.name));
308
+ }
309
+ if (testFile.tests.length === 0) {
310
+ console.log(' (no tests match filter)\n');
311
+ continue;
312
+ }
313
+ const executor = new executor_1.TestExecutor(executorOptions);
314
+ const results = await executor.runTestFile(testFile);
315
+ allResults.push({ file, results });
316
+ // Report results to all reporters
317
+ reporters.forEach(r => r.onTestFileComplete(file, testFile, results));
318
+ // Check for failures
319
+ const failed = results.some(r => r.status !== 'passed');
320
+ if (failed) {
321
+ hasFailures = true;
322
+ if (options.failFast) {
323
+ console.log('\nStopping due to --fail-fast\n');
324
+ break;
325
+ }
324
326
  }
325
327
  }
326
328
  }
327
- // Generate final report
328
- reporter.onComplete(allResults);
329
+ catch (error) {
330
+ console.error('Error during test execution:', error.message);
331
+ hasFailures = true;
332
+ }
333
+ finally {
334
+ // Always generate final reports, even on errors
335
+ reporters.forEach(r => r.onComplete(allResults));
336
+ }
329
337
  // Exit with appropriate code
330
338
  process.exit(hasFailures ? 1 : 0);
331
339
  }
@@ -432,9 +440,10 @@ program
432
440
  'test:headed': 'testblocks run tests/**/*.testblocks.json --headed',
433
441
  'test:html': 'testblocks run tests/**/*.testblocks.json -r html -o reports',
434
442
  'test:junit': 'testblocks run tests/**/*.testblocks.json -r junit -o reports',
443
+ 'test:ci': 'testblocks run tests/**/*.testblocks.json -r console,html,junit -o reports',
435
444
  },
436
445
  devDependencies: {
437
- '@testsmith/testblocks': '^0.8.3',
446
+ '@testsmith/testblocks': '^0.8.5',
438
447
  },
439
448
  };
440
449
  fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2));
@@ -574,18 +583,34 @@ jobs:
574
583
  run: npm ci
575
584
 
576
585
  - name: Install Playwright browsers
577
- run: npx playwright install --with-deps chromium
586
+ run: npx testblocks install-browsers
578
587
 
579
588
  - name: Run tests
580
- run: npm test
589
+ run: npm run test:ci
590
+
591
+ - name: Upload HTML report
592
+ uses: actions/upload-artifact@v4
593
+ if: always()
594
+ with:
595
+ name: html-report
596
+ path: reports/*.html
597
+ retention-days: 30
581
598
 
582
- - name: Upload test reports
599
+ - name: Upload JUnit report
583
600
  uses: actions/upload-artifact@v4
584
601
  if: always()
585
602
  with:
586
- name: test-reports
587
- path: reports/
603
+ name: junit-report
604
+ path: reports/*.xml
588
605
  retention-days: 30
606
+
607
+ - name: Publish Test Results
608
+ uses: dorny/test-reporter@v1
609
+ if: always()
610
+ with:
611
+ name: Test Results
612
+ path: reports/*.xml
613
+ reporter: java-junit
589
614
  `;
590
615
  fs.writeFileSync(workflowPath, workflow);
591
616
  console.log(' Created: .github/workflows/testblocks.yml');
@@ -594,10 +619,30 @@ jobs:
594
619
  console.log('Next steps:');
595
620
  console.log(' 1. cd ' + (directory === '.' ? '' : directory));
596
621
  console.log(' 2. npm install');
597
- console.log(' 3. npm test\n');
622
+ console.log(' 3. npx testblocks install-browsers');
623
+ console.log(' 4. npm test\n');
598
624
  console.log('To open the visual test editor:');
599
625
  console.log(' testblocks serve\n');
600
626
  });
627
+ program
628
+ .command('install-browsers')
629
+ .description('Install Playwright browsers for running web tests')
630
+ .option('--browser <browser>', 'Browser to install (chromium, firefox, webkit, all)', 'chromium')
631
+ .action(async (options) => {
632
+ const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
633
+ const browser = options.browser === 'all' ? '' : options.browser;
634
+ console.log(`Installing Playwright browser${browser ? `: ${browser}` : 's'}...`);
635
+ try {
636
+ // Use execSync to run playwright install with inherited stdio
637
+ const command = `npx playwright install${browser ? ` ${browser}` : ''} --with-deps`;
638
+ execSync(command, { stdio: 'inherit' });
639
+ console.log('\n✓ Browsers installed successfully!');
640
+ }
641
+ catch (error) {
642
+ console.error('\nFailed to install browsers:', error instanceof Error ? error.message : error);
643
+ process.exit(1);
644
+ }
645
+ });
601
646
  program
602
647
  .command('list')
603
648
  .description('List tests in test files')
@@ -655,18 +700,35 @@ program
655
700
  open: options.open,
656
701
  });
657
702
  });
658
- function createReporter(type, outputDir) {
659
- switch (type) {
660
- case 'json':
661
- return new reporters_1.JSONReporter(outputDir);
662
- case 'junit':
663
- return new reporters_1.JUnitReporter(outputDir);
664
- case 'html':
665
- return new reporters_1.HTMLReporter(outputDir);
666
- case 'console':
667
- default:
668
- return new reporters_1.ConsoleReporter();
703
+ function createReporters(types, outputDir) {
704
+ const reporterTypes = types.split(',').map(t => t.trim().toLowerCase());
705
+ const reporters = [];
706
+ for (const type of reporterTypes) {
707
+ switch (type) {
708
+ case 'json':
709
+ reporters.push(new reporters_1.JSONReporter(outputDir));
710
+ break;
711
+ case 'junit':
712
+ reporters.push(new reporters_1.JUnitReporter(outputDir));
713
+ break;
714
+ case 'html':
715
+ reporters.push(new reporters_1.HTMLReporter(outputDir));
716
+ break;
717
+ case 'console':
718
+ reporters.push(new reporters_1.ConsoleReporter());
719
+ break;
720
+ default:
721
+ console.warn(`Unknown reporter type: ${type}, using console`);
722
+ if (!reporters.some(r => r instanceof reporters_1.ConsoleReporter)) {
723
+ reporters.push(new reporters_1.ConsoleReporter());
724
+ }
725
+ }
726
+ }
727
+ // Always include console reporter if not already included
728
+ if (!reporters.some(r => r instanceof reporters_1.ConsoleReporter)) {
729
+ reporters.unshift(new reporters_1.ConsoleReporter());
669
730
  }
731
+ return reporters;
670
732
  }
671
733
  function validateTestFile(testFile) {
672
734
  const errors = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testsmith/testblocks",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "Visual test automation tool with Blockly - API and Playwright testing",
5
5
  "author": "Roy de Kleijn",
6
6
  "license": "MIT",