@testdino/playwright 1.0.4 → 1.0.6

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.
@@ -1,14 +1,63 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
- import chalk from 'chalk';
4
3
  import { readFileSync, existsSync, statSync, writeFileSync, unlinkSync } from 'fs';
5
4
  import { dirname, join } from 'path';
6
5
  import { fileURLToPath } from 'url';
7
6
  import jiti from 'jiti';
8
7
  import { randomUUID } from 'crypto';
9
8
  import { tmpdir } from 'os';
9
+ import chalk from 'chalk';
10
10
  import { execa } from 'execa';
11
11
 
12
+ // src/cli/errors.ts
13
+ var TestDinoError = class extends Error {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "TestDinoError";
17
+ Error.captureStackTrace(this, this.constructor);
18
+ }
19
+ };
20
+ var TokenMissingError = class extends TestDinoError {
21
+ constructor() {
22
+ const message = `Token is required to run tests with TestDino
23
+
24
+ Provide token via:
25
+ \u2022 CLI flag: npx tdpw test --token <your-token>
26
+ \u2022 Environment: export TESTDINO_TOKEN=<your-token>
27
+ \u2022 Config file: Create testdino.config.ts with token
28
+
29
+ Get your token at: https://testdino.com/settings`;
30
+ super(message);
31
+ this.name = "TokenMissingError";
32
+ }
33
+ };
34
+ var ConfigSyntaxError = class extends TestDinoError {
35
+ constructor(configPath, originalError) {
36
+ const message = `Failed to load ${configPath}
37
+
38
+ ${originalError.message}
39
+
40
+ Fix the syntax error and try again.`;
41
+ super(message);
42
+ this.name = "ConfigSyntaxError";
43
+ }
44
+ };
45
+ var InvalidServerUrlError = class extends TestDinoError {
46
+ constructor(url) {
47
+ const message = `Invalid server URL: ${url}
48
+
49
+ Server URL must be a valid HTTP or HTTPS URL.
50
+
51
+ Examples:
52
+ \u2022 https://api.testdino.com
53
+ \u2022 https://api-v0.testdino.com
54
+ \u2022 https://global.testdino.com`;
55
+ super(message);
56
+ this.name = "InvalidServerUrlError";
57
+ }
58
+ };
59
+
60
+ // src/cli/config-loader.ts
12
61
  var CONFIG_FILENAMES = ["testdino.config.ts", "testdino.config.js"];
13
62
  var ConfigLoader = class {
14
63
  cwd;
@@ -27,10 +76,7 @@ var ConfigLoader = class {
27
76
  const config = this.loadConfigFile(configPath);
28
77
  return { config, configPath };
29
78
  } catch (error) {
30
- throw new Error(
31
- `Failed to load config file: ${configPath}
32
- ${error instanceof Error ? error.message : String(error)}`
33
- );
79
+ throw new ConfigSyntaxError(configPath, error instanceof Error ? error : new Error(String(error)));
34
80
  }
35
81
  }
36
82
  /**
@@ -82,8 +128,7 @@ ${error instanceof Error ? error.message : String(error)}`
82
128
  const resolvedPath = typeof resolved === "string" ? resolved : fileURLToPath(resolved);
83
129
  loaded = jitiLoader(resolvedPath);
84
130
  } catch (error) {
85
- throw new Error(`Syntax error in config file:
86
- ${error instanceof Error ? error.message : String(error)}`);
131
+ throw new Error(`Syntax error: ${error instanceof Error ? error.message : String(error)}`);
87
132
  }
88
133
  let config;
89
134
  if (loaded && typeof loaded === "object" && "__esModule" in loaded) {
@@ -100,8 +145,7 @@ ${error instanceof Error ? error.message : String(error)}`);
100
145
  try {
101
146
  config = config();
102
147
  } catch (error) {
103
- throw new Error(`Error executing config function:
104
- ${error instanceof Error ? error.message : String(error)}`);
148
+ throw new Error(`Error executing config function: ${error instanceof Error ? error.message : String(error)}`);
105
149
  }
106
150
  if (config instanceof Promise) {
107
151
  throw new Error("Async config functions are not supported");
@@ -139,10 +183,7 @@ var ConfigDetector = class {
139
183
  configPath
140
184
  };
141
185
  } catch (error) {
142
- throw new Error(
143
- `Failed to load Playwright config: ${configPath}
144
- ${error instanceof Error ? error.message : String(error)}`
145
- );
186
+ throw new ConfigSyntaxError(configPath, error instanceof Error ? error : new Error(String(error)));
146
187
  }
147
188
  }
148
189
  /**
@@ -175,8 +216,7 @@ ${error instanceof Error ? error.message : String(error)}`
175
216
  const resolvedPath = typeof resolved === "string" ? resolved : fileURLToPath(resolved);
176
217
  loaded = jitiLoader(resolvedPath);
177
218
  } catch (error) {
178
- throw new Error(`Syntax error in Playwright config:
179
- ${error instanceof Error ? error.message : String(error)}`);
219
+ throw new Error(`Syntax error: ${error instanceof Error ? error.message : String(error)}`);
180
220
  }
181
221
  let config;
182
222
  if (loaded && typeof loaded === "object" && "__esModule" in loaded) {
@@ -190,10 +230,7 @@ ${error instanceof Error ? error.message : String(error)}`);
190
230
  try {
191
231
  config = config();
192
232
  } catch (error) {
193
- throw new Error(
194
- `Error executing Playwright config function:
195
- ${error instanceof Error ? error.message : String(error)}`
196
- );
233
+ throw new Error(`Error executing config function: ${error instanceof Error ? error.message : String(error)}`);
197
234
  }
198
235
  }
199
236
  if (!config || typeof config !== "object") {
@@ -272,12 +309,6 @@ ${error instanceof Error ? error.message : String(error)}`
272
309
  return config;
273
310
  }
274
311
  };
275
- var ConfigValidationError = class extends Error {
276
- constructor(message) {
277
- super(message);
278
- this.name = "ConfigValidationError";
279
- }
280
- };
281
312
  var ConfigMerger = class _ConfigMerger {
282
313
  static DEFAULT_SERVER_URL = "https://api.testdino.com";
283
314
  /**
@@ -321,27 +352,14 @@ var ConfigMerger = class _ConfigMerger {
321
352
  * Validate merged configuration
322
353
  */
323
354
  validate(config) {
324
- const errors = [];
325
355
  if (!config.token || typeof config.token !== "string" || config.token.trim().length === 0) {
326
- errors.push("Token is required and must be a non-empty string");
356
+ throw new TokenMissingError();
327
357
  }
328
358
  if (config.serverUrl) {
329
- if (typeof config.serverUrl !== "string") {
330
- errors.push("Server URL must be a string");
331
- } else if (!this.isValidUrl(config.serverUrl)) {
332
- errors.push("Server URL must be a valid HTTP/HTTPS URL");
359
+ if (typeof config.serverUrl !== "string" || !this.isValidUrl(config.serverUrl)) {
360
+ throw new InvalidServerUrlError(config.serverUrl);
333
361
  }
334
362
  }
335
- if (config.ciRunId && typeof config.ciRunId !== "string") {
336
- errors.push("CI run ID must be a string");
337
- }
338
- if (config.debug !== void 0 && typeof config.debug !== "boolean") {
339
- errors.push("Debug flag must be a boolean");
340
- }
341
- if (errors.length > 0) {
342
- throw new ConfigValidationError(`Configuration validation failed:
343
- ${errors.map((e) => ` - ${e}`).join("\n")}`);
344
- }
345
363
  }
346
364
  /**
347
365
  * Check if string is a valid URL
@@ -436,6 +454,114 @@ var ArgFilter = class {
436
454
  return [...TESTDINO_FLAGS];
437
455
  }
438
456
  };
457
+
458
+ // src/utils/index.ts
459
+ function isDebugEnabled() {
460
+ return process.env.TESTDINO_DEBUG === "true" || process.env.TESTDINO_DEBUG === "1" || process.env.DEBUG === "true";
461
+ }
462
+
463
+ // src/cli/logger.ts
464
+ var Logger = class {
465
+ debugEnabled;
466
+ constructor(debugEnabled = false) {
467
+ this.debugEnabled = debugEnabled;
468
+ }
469
+ /**
470
+ * Enable or disable debug logging
471
+ */
472
+ setDebug(enabled) {
473
+ this.debugEnabled = enabled;
474
+ }
475
+ /**
476
+ * Log error message
477
+ */
478
+ error(message, error) {
479
+ console.error(chalk.red(`
480
+ \u2716 Error: ${message}`));
481
+ if (error && this.debugEnabled) {
482
+ console.error(chalk.dim("\nStack trace:"));
483
+ console.error(chalk.dim(error.stack || error.message));
484
+ }
485
+ }
486
+ /**
487
+ * Log warning message
488
+ */
489
+ warn(message) {
490
+ console.warn(chalk.yellow(`\u26A0\uFE0F Warning: ${message}`));
491
+ }
492
+ /**
493
+ * Log info message
494
+ */
495
+ info(message) {
496
+ console.log(chalk.blue(`\u2139\uFE0F ${message}`));
497
+ }
498
+ /**
499
+ * Log success message
500
+ */
501
+ success(message) {
502
+ console.log(chalk.green(`\u2713 ${message}`));
503
+ }
504
+ /**
505
+ * Log debug message (only if debug is enabled)
506
+ */
507
+ debug(message) {
508
+ if (this.debugEnabled) {
509
+ console.log(chalk.dim(`[DEBUG] ${message}`));
510
+ }
511
+ }
512
+ /**
513
+ * Log a blank line
514
+ */
515
+ newline() {
516
+ console.log();
517
+ }
518
+ /**
519
+ * Log a section header
520
+ */
521
+ section(title) {
522
+ console.log(chalk.bold.cyan(`
523
+ ${title}`));
524
+ }
525
+ /**
526
+ * Log a list item
527
+ */
528
+ listItem(text) {
529
+ console.log(` \u2022 ${text}`);
530
+ }
531
+ /**
532
+ * Log code/command
533
+ */
534
+ code(text) {
535
+ console.log(chalk.gray(` ${text}`));
536
+ }
537
+ /**
538
+ * Format error for display
539
+ */
540
+ formatError(error) {
541
+ if (this.debugEnabled && error.stack) {
542
+ return error.stack;
543
+ }
544
+ return error.message;
545
+ }
546
+ /**
547
+ * Log TestDino CLI banner
548
+ */
549
+ banner(version) {
550
+ console.log(
551
+ chalk.cyan(`
552
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
553
+ \u2551 \u2551
554
+ \u2551 ${chalk.bold("TestDino Playwright")} v${version.padEnd(36)}\u2551
555
+ \u2551 ${chalk.dim("https://testdino.com")} \u2551
556
+ \u2551 \u2551
557
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
558
+ `)
559
+ );
560
+ }
561
+ };
562
+ new Logger(isDebugEnabled());
563
+
564
+ // src/cli/temp-config.ts
439
565
  var TempConfigManager = class {
440
566
  tempFiles = /* @__PURE__ */ new Set();
441
567
  cleanupHandlersRegistered = false;
@@ -444,6 +570,10 @@ var TempConfigManager = class {
444
570
  sigtermHandler;
445
571
  uncaughtExceptionHandler;
446
572
  unhandledRejectionHandler;
573
+ logger;
574
+ constructor(logger3) {
575
+ this.logger = logger3 ?? new Logger();
576
+ }
447
577
  /**
448
578
  * Create a temporary config file with the merged configuration
449
579
  * @param config - Merged configuration to write
@@ -481,7 +611,7 @@ ${error instanceof Error ? error.message : String(error)}`
481
611
  }
482
612
  this.tempFiles.delete(tempPath);
483
613
  } catch {
484
- console.warn(`\u26A0\uFE0F TestDino: Failed to cleanup temp file: ${tempPath}`);
614
+ this.logger.warn(`Failed to cleanup temp file: ${tempPath}`);
485
615
  }
486
616
  }
487
617
  /**
@@ -540,13 +670,13 @@ ${error instanceof Error ? error.message : String(error)}`
540
670
  };
541
671
  process.on("SIGTERM", this.sigtermHandler);
542
672
  this.uncaughtExceptionHandler = (error) => {
543
- console.error("Uncaught exception:", error);
673
+ this.logger.error("Uncaught exception", error);
544
674
  this.cleanupAll();
545
675
  process.exit(1);
546
676
  };
547
677
  process.on("uncaughtException", this.uncaughtExceptionHandler);
548
678
  this.unhandledRejectionHandler = (reason) => {
549
- console.error("Unhandled rejection:", reason);
679
+ this.logger.error("Unhandled rejection", reason instanceof Error ? reason : void 0);
550
680
  this.cleanupAll();
551
681
  process.exit(1);
552
682
  };
@@ -560,6 +690,10 @@ ${error instanceof Error ? error.message : String(error)}`
560
690
  }
561
691
  };
562
692
  var PlaywrightSpawner = class {
693
+ logger;
694
+ constructor(logger3) {
695
+ this.logger = logger3 ?? new Logger();
696
+ }
563
697
  /**
564
698
  * Spawn Playwright test process
565
699
  * @param options - Spawn options
@@ -600,33 +734,30 @@ var PlaywrightSpawner = class {
600
734
  handleSpawnError(error) {
601
735
  const execaError = error;
602
736
  if (execaError.code === "ENOENT") {
603
- console.error("\u274C TestDino: Failed to spawn Playwright");
604
- console.error("");
605
- console.error("Playwright is not installed or npx is not available.");
606
- console.error("");
607
- console.error("To install Playwright:");
608
- console.error(" npm install -D @playwright/test");
609
- console.error(" npx playwright install");
610
- console.error("");
737
+ this.logger.error("Failed to spawn Playwright");
738
+ this.logger.newline();
739
+ this.logger.info("Playwright is not installed or npx is not available.");
740
+ this.logger.newline();
741
+ this.logger.section("To install Playwright:");
742
+ this.logger.code("npm install -D @playwright/test");
743
+ this.logger.code("npx playwright install");
611
744
  return {
612
745
  exitCode: 1,
613
746
  success: false
614
747
  };
615
748
  }
616
749
  if (execaError.code === "EACCES") {
617
- console.error("\u274C TestDino: Permission denied when trying to spawn Playwright");
618
- console.error("");
619
- console.error("Please check file permissions and try again.");
620
- console.error("");
750
+ this.logger.error("Permission denied when trying to spawn Playwright");
751
+ this.logger.newline();
752
+ this.logger.info("Please check file permissions and try again.");
621
753
  return {
622
754
  exitCode: 1,
623
755
  success: false
624
756
  };
625
757
  }
626
- console.error("\u274C TestDino: Failed to spawn Playwright");
627
- console.error("");
628
- console.error("Error:", execaError.message || String(error));
629
- console.error("");
758
+ this.logger.error("Failed to spawn Playwright");
759
+ this.logger.newline();
760
+ this.logger.info(`Error: ${execaError.message || String(error)}`);
630
761
  return {
631
762
  exitCode: 1,
632
763
  success: false
@@ -642,13 +773,13 @@ var TestCommand = class {
642
773
  argFilter;
643
774
  tempConfigManager;
644
775
  playwrightSpawner;
645
- constructor(configLoader, configDetector, configMerger, argFilter, tempConfigManager, playwrightSpawner) {
776
+ constructor(configLoader, configDetector, configMerger, argFilter, tempConfigManager, playwrightSpawner, logger3) {
646
777
  this.configLoader = configLoader || new ConfigLoader();
647
778
  this.configDetector = configDetector || new ConfigDetector();
648
779
  this.configMerger = configMerger || new ConfigMerger();
649
780
  this.argFilter = argFilter || new ArgFilter();
650
- this.tempConfigManager = tempConfigManager || new TempConfigManager();
651
- this.playwrightSpawner = playwrightSpawner || new PlaywrightSpawner();
781
+ this.tempConfigManager = tempConfigManager || new TempConfigManager(logger3);
782
+ this.playwrightSpawner = playwrightSpawner || new PlaywrightSpawner(logger3);
652
783
  }
653
784
  /**
654
785
  * Execute the test command
@@ -697,32 +828,26 @@ function getVersion() {
697
828
  return "0.0.0";
698
829
  }
699
830
  }
700
- function printBanner() {
701
- const version = getVersion();
702
- console.log(
703
- chalk.cyan(`
704
- \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
705
- \u2551 \u2551
706
- \u2551 ${chalk.bold("TestDino Playwright")} v${version.padEnd(36)}\u2551
707
- \u2551 ${chalk.dim("https://testdino.com")} \u2551
708
- \u2551 \u2551
709
- \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
710
- `)
711
- );
712
- }
831
+ var logger2 = new Logger(isDebugEnabled());
713
832
  function buildProgram() {
714
833
  const program = new Command().name("tdpw").description("Run Playwright tests with TestDino reporting").version(getVersion(), "-v, --version", "Output the current version").helpOption("-h, --help", "Display help for command");
715
834
  program.command("test").description("Run Playwright tests with TestDino reporter").option("-t, --token <token>", "TestDino authentication token").option("--ci-run-id <id>", "CI run ID for grouping test runs").option("--server-url <url>", "TestDino server URL").option("--debug", "Enable debug logging").option("--no-artifacts", "Disable artifact uploads (screenshots, videos, traces)").allowUnknownOption().allowExcessArguments().action(async (options, command) => {
835
+ if (options.debug) {
836
+ logger2.setDebug(true);
837
+ }
716
838
  try {
717
839
  const args = command.args || [];
718
840
  const testCommand = new TestCommand();
719
841
  const result = await testCommand.execute(options, args);
720
842
  process.exit(result.exitCode);
721
843
  } catch (error) {
722
- console.error(chalk.red("\n\u2716 Unexpected error:"), error instanceof Error ? error.message : String(error));
723
- if (options.debug) {
724
- console.error(chalk.dim("\nStack trace:"));
725
- console.error(error);
844
+ if (error instanceof TestDinoError) {
845
+ logger2.error(error.message);
846
+ } else {
847
+ logger2.error(
848
+ error instanceof Error ? error.message : String(error),
849
+ error instanceof Error ? error : void 0
850
+ );
726
851
  }
727
852
  process.exit(1);
728
853
  }
@@ -731,20 +856,20 @@ function buildProgram() {
731
856
  }
732
857
  async function main() {
733
858
  try {
734
- printBanner();
859
+ logger2.banner(getVersion());
735
860
  const program = buildProgram();
736
861
  await program.parseAsync(process.argv);
737
862
  } catch (error) {
738
- console.error(chalk.red("\n\u2716 Error:"), error instanceof Error ? error.message : String(error));
739
- if (process.env.DEBUG || process.env.TESTDINO_DEBUG) {
740
- console.error(chalk.dim("\nStack trace:"));
741
- console.error(error);
863
+ if (error instanceof TestDinoError) {
864
+ logger2.error(error.message);
865
+ } else {
866
+ logger2.error(error instanceof Error ? error.message : String(error), error instanceof Error ? error : void 0);
742
867
  }
743
868
  process.exit(1);
744
869
  }
745
870
  }
746
871
  main().catch((error) => {
747
- console.error(chalk.red("Unexpected error:"), error);
872
+ logger2.error("Unexpected error", error instanceof Error ? error : void 0);
748
873
  process.exit(1);
749
874
  });
750
875
  //# sourceMappingURL=index.mjs.map