@testdino/playwright 1.0.1

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.
@@ -0,0 +1,759 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var commander = require('commander');
5
+ var chalk = require('chalk');
6
+ var fs = require('fs');
7
+ var path = require('path');
8
+ var url = require('url');
9
+ var jiti = require('jiti');
10
+ var crypto = require('crypto');
11
+ var os = require('os');
12
+ var execa = require('execa');
13
+
14
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
15
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
+
17
+ var chalk__default = /*#__PURE__*/_interopDefault(chalk);
18
+ var jiti__default = /*#__PURE__*/_interopDefault(jiti);
19
+
20
+ var CONFIG_FILENAMES = ["testdino.config.ts", "testdino.config.js"];
21
+ var ConfigLoader = class {
22
+ cwd;
23
+ constructor(cwd = process.cwd()) {
24
+ this.cwd = cwd;
25
+ }
26
+ /**
27
+ * Load config file from current directory or parent directories
28
+ */
29
+ async load() {
30
+ const configPath = this.findConfigFile();
31
+ if (!configPath) {
32
+ return { config: {} };
33
+ }
34
+ try {
35
+ const config = this.loadConfigFile(configPath);
36
+ return { config, configPath };
37
+ } catch (error) {
38
+ throw new Error(
39
+ `Failed to load config file: ${configPath}
40
+ ${error instanceof Error ? error.message : String(error)}`
41
+ );
42
+ }
43
+ }
44
+ /**
45
+ * Find config file by searching up directory tree
46
+ * Stops at .git directory
47
+ */
48
+ findConfigFile() {
49
+ let currentDir = this.cwd;
50
+ while (true) {
51
+ for (const filename of CONFIG_FILENAMES) {
52
+ const configPath = path.join(currentDir, filename);
53
+ if (fs.existsSync(configPath)) {
54
+ return configPath;
55
+ }
56
+ }
57
+ const gitDir = path.join(currentDir, ".git");
58
+ if (fs.existsSync(gitDir) && fs.statSync(gitDir).isDirectory()) {
59
+ for (const filename of CONFIG_FILENAMES) {
60
+ const configPath = path.join(currentDir, filename);
61
+ if (fs.existsSync(configPath)) {
62
+ return configPath;
63
+ }
64
+ }
65
+ break;
66
+ }
67
+ const parentDir = path.dirname(currentDir);
68
+ if (parentDir === currentDir) {
69
+ break;
70
+ }
71
+ currentDir = parentDir;
72
+ }
73
+ return void 0;
74
+ }
75
+ /**
76
+ * Load and parse config file using jiti
77
+ */
78
+ loadConfigFile(configPath) {
79
+ const jitiLoader = jiti__default.default(path.dirname(configPath), {
80
+ interopDefault: true,
81
+ cache: false,
82
+ extensions: [".ts", ".js"]
83
+ });
84
+ let loaded;
85
+ try {
86
+ const resolved = jitiLoader.esmResolve(configPath, { try: true });
87
+ if (!resolved) {
88
+ throw new Error(`Could not resolve config file: ${configPath}`);
89
+ }
90
+ const resolvedPath = typeof resolved === "string" ? resolved : url.fileURLToPath(resolved);
91
+ loaded = jitiLoader(resolvedPath);
92
+ } catch (error) {
93
+ throw new Error(`Syntax error in config file:
94
+ ${error instanceof Error ? error.message : String(error)}`);
95
+ }
96
+ let config;
97
+ if (loaded && typeof loaded === "object" && "__esModule" in loaded) {
98
+ config = loaded.default;
99
+ } else if (loaded && typeof loaded === "object" && "default" in loaded) {
100
+ config = loaded.default;
101
+ } else {
102
+ config = loaded;
103
+ }
104
+ if (config === null || config === void 0) {
105
+ return {};
106
+ }
107
+ if (typeof config === "function") {
108
+ try {
109
+ config = config();
110
+ } catch (error) {
111
+ throw new Error(`Error executing config function:
112
+ ${error instanceof Error ? error.message : String(error)}`);
113
+ }
114
+ if (config instanceof Promise) {
115
+ throw new Error("Async config functions are not supported");
116
+ }
117
+ if (config === null || config === void 0) {
118
+ return {};
119
+ }
120
+ }
121
+ if (config && typeof config !== "object") {
122
+ throw new Error("Config must be an object");
123
+ }
124
+ return config ?? {};
125
+ }
126
+ };
127
+ var PLAYWRIGHT_CONFIG_FILENAMES = ["playwright.config.ts", "playwright.config.js"];
128
+ var TESTDINO_REPORTER_NAMES = ["@testdino/playwright", "testdino-playwright", "TestdinoReporter"];
129
+ var ConfigDetector = class {
130
+ cwd;
131
+ constructor(cwd = process.cwd()) {
132
+ this.cwd = cwd;
133
+ }
134
+ /**
135
+ * Detect TestdinoReporter in Playwright config
136
+ */
137
+ async detect() {
138
+ const configPath = this.findPlaywrightConfig();
139
+ if (!configPath) {
140
+ return { hasReporter: false };
141
+ }
142
+ try {
143
+ const config = this.loadPlaywrightConfig(configPath);
144
+ const result = this.extractTestdinoReporter(config);
145
+ return {
146
+ ...result,
147
+ configPath
148
+ };
149
+ } catch (error) {
150
+ throw new Error(
151
+ `Failed to load Playwright config: ${configPath}
152
+ ${error instanceof Error ? error.message : String(error)}`
153
+ );
154
+ }
155
+ }
156
+ /**
157
+ * Find playwright.config.[ts|js] in current directory
158
+ */
159
+ findPlaywrightConfig() {
160
+ for (const filename of PLAYWRIGHT_CONFIG_FILENAMES) {
161
+ const configPath = path.join(this.cwd, filename);
162
+ if (fs.existsSync(configPath)) {
163
+ return configPath;
164
+ }
165
+ }
166
+ return void 0;
167
+ }
168
+ /**
169
+ * Load and parse Playwright config using jiti
170
+ */
171
+ loadPlaywrightConfig(configPath) {
172
+ const jitiLoader = jiti__default.default(path.dirname(configPath), {
173
+ interopDefault: true,
174
+ cache: false,
175
+ extensions: [".ts", ".js"]
176
+ });
177
+ let loaded;
178
+ try {
179
+ const resolved = jitiLoader.esmResolve(configPath, { try: true });
180
+ if (!resolved) {
181
+ throw new Error(`Could not resolve Playwright config: ${configPath}`);
182
+ }
183
+ const resolvedPath = typeof resolved === "string" ? resolved : url.fileURLToPath(resolved);
184
+ loaded = jitiLoader(resolvedPath);
185
+ } catch (error) {
186
+ throw new Error(`Syntax error in Playwright config:
187
+ ${error instanceof Error ? error.message : String(error)}`);
188
+ }
189
+ let config;
190
+ if (loaded && typeof loaded === "object" && "__esModule" in loaded) {
191
+ config = loaded.default;
192
+ } else if (loaded && typeof loaded === "object" && "default" in loaded) {
193
+ config = loaded.default;
194
+ } else {
195
+ config = loaded;
196
+ }
197
+ if (typeof config === "function") {
198
+ try {
199
+ config = config();
200
+ } catch (error) {
201
+ throw new Error(
202
+ `Error executing Playwright config function:
203
+ ${error instanceof Error ? error.message : String(error)}`
204
+ );
205
+ }
206
+ }
207
+ if (!config || typeof config !== "object") {
208
+ throw new Error("Playwright config must be an object");
209
+ }
210
+ return config;
211
+ }
212
+ /**
213
+ * Extract TestdinoReporter configuration from Playwright config
214
+ */
215
+ extractTestdinoReporter(config) {
216
+ const { reporter } = config;
217
+ if (!reporter) {
218
+ return { hasReporter: false };
219
+ }
220
+ if (typeof reporter === "string") {
221
+ if (this.isTestdinoReporter(reporter)) {
222
+ return { hasReporter: true, options: {} };
223
+ }
224
+ return { hasReporter: false };
225
+ }
226
+ if (Array.isArray(reporter) && reporter.length > 0) {
227
+ if (typeof reporter[0] === "string") {
228
+ const [name, options] = reporter;
229
+ if (this.isTestdinoReporter(name)) {
230
+ return {
231
+ hasReporter: true,
232
+ options: this.extractOptions(options)
233
+ };
234
+ }
235
+ }
236
+ for (const item of reporter) {
237
+ if (typeof item === "string") {
238
+ if (this.isTestdinoReporter(item)) {
239
+ return { hasReporter: true, options: {} };
240
+ }
241
+ } else if (Array.isArray(item) && item.length > 0) {
242
+ const [name, options] = item;
243
+ if (this.isTestdinoReporter(name)) {
244
+ return {
245
+ hasReporter: true,
246
+ options: this.extractOptions(options)
247
+ };
248
+ }
249
+ }
250
+ }
251
+ }
252
+ return { hasReporter: false };
253
+ }
254
+ /**
255
+ * Check if reporter name matches TestdinoReporter
256
+ */
257
+ isTestdinoReporter(name) {
258
+ return TESTDINO_REPORTER_NAMES.some((testdinoName) => name === testdinoName || name.includes(testdinoName));
259
+ }
260
+ /**
261
+ * Extract and validate TestdinoConfig options
262
+ */
263
+ extractOptions(options) {
264
+ if (!options || typeof options !== "object") {
265
+ return {};
266
+ }
267
+ const config = {};
268
+ if ("token" in options && typeof options.token === "string") {
269
+ config.token = options.token;
270
+ }
271
+ if ("serverUrl" in options && typeof options.serverUrl === "string") {
272
+ config.serverUrl = options.serverUrl;
273
+ }
274
+ if ("ciBuildId" in options && typeof options.ciBuildId === "string") {
275
+ config.ciBuildId = options.ciBuildId;
276
+ }
277
+ if ("debug" in options && typeof options.debug === "boolean") {
278
+ config.debug = options.debug;
279
+ }
280
+ return config;
281
+ }
282
+ };
283
+ var ConfigValidationError = class extends Error {
284
+ constructor(message) {
285
+ super(message);
286
+ this.name = "ConfigValidationError";
287
+ }
288
+ };
289
+ var ConfigMerger = class _ConfigMerger {
290
+ static DEFAULT_SERVER_URL = "https://api.testdino.com";
291
+ /**
292
+ * Merge configurations from all sources
293
+ * Priority (highest to lowest):
294
+ * 1. CLI flags
295
+ * 2. testdino.config.[ts|js]
296
+ * 3. playwright.config reporter options
297
+ * 4. Environment variables
298
+ */
299
+ merge(sources) {
300
+ const { env = {}, playwrightConfig = {}, testdinoConfig = {}, cliOptions = {} } = sources;
301
+ const token = this.selectValue(cliOptions.token, testdinoConfig.token, playwrightConfig.token, env.token);
302
+ const serverUrl = this.selectValue(cliOptions.serverUrl, testdinoConfig.serverUrl, playwrightConfig.serverUrl, env.serverUrl) || _ConfigMerger.DEFAULT_SERVER_URL;
303
+ const ciRunId = this.selectValue(cliOptions.ciRunId, testdinoConfig.ciRunId, playwrightConfig.ciBuildId) || this.generateCiRunId();
304
+ const debug = this.selectValue(cliOptions.debug, testdinoConfig.debug, playwrightConfig.debug, env.debug) ?? false;
305
+ const artifacts = cliOptions.noArtifacts === true ? false : this.selectValue(testdinoConfig.artifacts, playwrightConfig.artifacts) ?? true;
306
+ const mergedConfig = {
307
+ token,
308
+ serverUrl,
309
+ ciRunId,
310
+ debug,
311
+ artifacts
312
+ };
313
+ this.validate(mergedConfig);
314
+ return mergedConfig;
315
+ }
316
+ /**
317
+ * Select first non-undefined value from arguments
318
+ */
319
+ selectValue(...values) {
320
+ return values.find((value) => value !== void 0 && value !== null);
321
+ }
322
+ /**
323
+ * Generate a unique CI run ID
324
+ */
325
+ generateCiRunId() {
326
+ return `run-${crypto.randomUUID()}`;
327
+ }
328
+ /**
329
+ * Validate merged configuration
330
+ */
331
+ validate(config) {
332
+ const errors = [];
333
+ if (!config.token || typeof config.token !== "string" || config.token.trim().length === 0) {
334
+ errors.push("Token is required and must be a non-empty string");
335
+ }
336
+ if (config.serverUrl) {
337
+ if (typeof config.serverUrl !== "string") {
338
+ errors.push("Server URL must be a string");
339
+ } else if (!this.isValidUrl(config.serverUrl)) {
340
+ errors.push("Server URL must be a valid HTTP/HTTPS URL");
341
+ }
342
+ }
343
+ if (config.ciRunId && typeof config.ciRunId !== "string") {
344
+ errors.push("CI run ID must be a string");
345
+ }
346
+ if (config.debug !== void 0 && typeof config.debug !== "boolean") {
347
+ errors.push("Debug flag must be a boolean");
348
+ }
349
+ if (errors.length > 0) {
350
+ throw new ConfigValidationError(`Configuration validation failed:
351
+ ${errors.map((e) => ` - ${e}`).join("\n")}`);
352
+ }
353
+ }
354
+ /**
355
+ * Check if string is a valid URL
356
+ */
357
+ isValidUrl(urlString) {
358
+ try {
359
+ const url = new URL(urlString);
360
+ return url.protocol === "http:" || url.protocol === "https:";
361
+ } catch {
362
+ return false;
363
+ }
364
+ }
365
+ /**
366
+ * Get environment variables as config
367
+ */
368
+ static getEnvConfig() {
369
+ const config = {};
370
+ if (process.env.TESTDINO_TOKEN) {
371
+ config.token = process.env.TESTDINO_TOKEN;
372
+ }
373
+ if (process.env.TESTDINO_SERVER_URL) {
374
+ config.serverUrl = process.env.TESTDINO_SERVER_URL;
375
+ }
376
+ if (process.env.TESTDINO_DEBUG) {
377
+ config.debug = process.env.TESTDINO_DEBUG === "true" || process.env.TESTDINO_DEBUG === "1";
378
+ }
379
+ return config;
380
+ }
381
+ };
382
+
383
+ // src/cli/arg-filter.ts
384
+ var TESTDINO_FLAGS = ["--token", "-t", "--ci-run-id", "--server-url", "--debug", "--no-artifacts"];
385
+ var FLAGS_WITH_VALUES = ["--token", "-t", "--ci-run-id", "--server-url"];
386
+ var ArgFilter = class {
387
+ /**
388
+ * Filter TestDino-specific arguments from the argument list
389
+ * Removes both flags and their values
390
+ *
391
+ * @param args - Raw command line arguments
392
+ * @returns Filtered arguments safe to pass to Playwright
393
+ */
394
+ filter(args) {
395
+ const result = [];
396
+ let skipNext = false;
397
+ for (let i = 0; i < args.length; i++) {
398
+ const arg = args[i];
399
+ if (skipNext) {
400
+ skipNext = false;
401
+ continue;
402
+ }
403
+ if (this.isTestdinoFlag(arg)) {
404
+ if (this.isFlagWithValue(arg) && !arg.includes("=")) {
405
+ skipNext = true;
406
+ }
407
+ continue;
408
+ }
409
+ if (this.isTestdinoFlagWithEquals(arg)) {
410
+ continue;
411
+ }
412
+ result.push(arg);
413
+ }
414
+ return result;
415
+ }
416
+ /**
417
+ * Check if argument is a TestDino flag
418
+ */
419
+ isTestdinoFlag(arg) {
420
+ const flagName = arg.split("=")[0];
421
+ return TESTDINO_FLAGS.includes(flagName);
422
+ }
423
+ /**
424
+ * Check if argument is a TestDino flag with = syntax
425
+ */
426
+ isTestdinoFlagWithEquals(arg) {
427
+ if (!arg.includes("=")) {
428
+ return false;
429
+ }
430
+ const flagName = arg.split("=")[0];
431
+ return TESTDINO_FLAGS.includes(flagName);
432
+ }
433
+ /**
434
+ * Check if flag takes a value
435
+ */
436
+ isFlagWithValue(arg) {
437
+ const flagName = arg.split("=")[0];
438
+ return FLAGS_WITH_VALUES.includes(flagName);
439
+ }
440
+ /**
441
+ * Get list of TestDino flags (for reference/testing)
442
+ */
443
+ static getTestdinoFlags() {
444
+ return [...TESTDINO_FLAGS];
445
+ }
446
+ };
447
+ var TempConfigManager = class {
448
+ tempFiles = /* @__PURE__ */ new Set();
449
+ cleanupHandlersRegistered = false;
450
+ exitHandler;
451
+ sigintHandler;
452
+ sigtermHandler;
453
+ uncaughtExceptionHandler;
454
+ unhandledRejectionHandler;
455
+ /**
456
+ * Create a temporary config file with the merged configuration
457
+ * @param config - Merged configuration to write
458
+ * @returns Temp config info with path and config
459
+ */
460
+ create(config) {
461
+ const tempPath = this.generateTempPath();
462
+ try {
463
+ const configJson = JSON.stringify(config, null, 2);
464
+ fs.writeFileSync(tempPath, configJson, "utf-8");
465
+ this.tempFiles.add(tempPath);
466
+ if (!this.cleanupHandlersRegistered) {
467
+ this.registerCleanupHandlers();
468
+ this.cleanupHandlersRegistered = true;
469
+ }
470
+ return {
471
+ path: tempPath,
472
+ config
473
+ };
474
+ } catch (error) {
475
+ throw new Error(
476
+ `Failed to create temp config file: ${tempPath}
477
+ ${error instanceof Error ? error.message : String(error)}`
478
+ );
479
+ }
480
+ }
481
+ /**
482
+ * Clean up a specific temp config file
483
+ * @param tempPath - Path to temp file to clean up
484
+ */
485
+ cleanup(tempPath) {
486
+ try {
487
+ if (fs.existsSync(tempPath)) {
488
+ fs.unlinkSync(tempPath);
489
+ }
490
+ this.tempFiles.delete(tempPath);
491
+ } catch {
492
+ console.warn(`\u26A0\uFE0F TestDino: Failed to cleanup temp file: ${tempPath}`);
493
+ }
494
+ }
495
+ /**
496
+ * Clean up all tracked temp files
497
+ */
498
+ cleanupAll() {
499
+ for (const tempPath of this.tempFiles) {
500
+ this.cleanup(tempPath);
501
+ }
502
+ this.tempFiles.clear();
503
+ }
504
+ /**
505
+ * Remove all event handlers (for testing)
506
+ */
507
+ removeHandlers() {
508
+ if (this.exitHandler) {
509
+ process.removeListener("exit", this.exitHandler);
510
+ }
511
+ if (this.sigintHandler) {
512
+ process.removeListener("SIGINT", this.sigintHandler);
513
+ }
514
+ if (this.sigtermHandler) {
515
+ process.removeListener("SIGTERM", this.sigtermHandler);
516
+ }
517
+ if (this.uncaughtExceptionHandler) {
518
+ process.removeListener("uncaughtException", this.uncaughtExceptionHandler);
519
+ }
520
+ if (this.unhandledRejectionHandler) {
521
+ process.removeListener("unhandledRejection", this.unhandledRejectionHandler);
522
+ }
523
+ this.cleanupHandlersRegistered = false;
524
+ }
525
+ /**
526
+ * Generate unique temp file path
527
+ */
528
+ generateTempPath() {
529
+ const filename = `testdino-config-${crypto.randomUUID()}.json`;
530
+ return path.join(os.tmpdir(), filename);
531
+ }
532
+ /**
533
+ * Register cleanup handlers for process exit and signals
534
+ */
535
+ registerCleanupHandlers() {
536
+ this.exitHandler = () => {
537
+ this.cleanupAll();
538
+ };
539
+ process.on("exit", this.exitHandler);
540
+ this.sigintHandler = () => {
541
+ this.cleanupAll();
542
+ process.exit(130);
543
+ };
544
+ process.on("SIGINT", this.sigintHandler);
545
+ this.sigtermHandler = () => {
546
+ this.cleanupAll();
547
+ process.exit(143);
548
+ };
549
+ process.on("SIGTERM", this.sigtermHandler);
550
+ this.uncaughtExceptionHandler = (error) => {
551
+ console.error("Uncaught exception:", error);
552
+ this.cleanupAll();
553
+ process.exit(1);
554
+ };
555
+ process.on("uncaughtException", this.uncaughtExceptionHandler);
556
+ this.unhandledRejectionHandler = (reason) => {
557
+ console.error("Unhandled rejection:", reason);
558
+ this.cleanupAll();
559
+ process.exit(1);
560
+ };
561
+ process.on("unhandledRejection", this.unhandledRejectionHandler);
562
+ }
563
+ /**
564
+ * Get list of tracked temp files (for testing)
565
+ */
566
+ getTempFiles() {
567
+ return Array.from(this.tempFiles);
568
+ }
569
+ };
570
+ var PlaywrightSpawner = class {
571
+ /**
572
+ * Spawn Playwright test process
573
+ * @param options - Spawn options
574
+ * @returns Spawn result with exit code
575
+ */
576
+ async spawn(options) {
577
+ const { args, tempConfigPath, config, cwd = process.cwd() } = options;
578
+ try {
579
+ const env = {
580
+ ...process.env,
581
+ TESTDINO_CLI_CONFIG_PATH: tempConfigPath,
582
+ TESTDINO_TOKEN: config.token,
583
+ TESTDINO_SERVER_URL: config.serverUrl,
584
+ TESTDINO_CI_RUN_ID: config.ciRunId,
585
+ TESTDINO_DEBUG: config.debug ? "true" : "false"
586
+ };
587
+ const playwrightArgs = ["playwright", "test", "--reporter", "@testdino/playwright", ...args];
588
+ const result = await execa.execa("npx", playwrightArgs, {
589
+ stdio: "inherit",
590
+ // Forward stdout/stderr in real-time
591
+ cwd,
592
+ env,
593
+ reject: false
594
+ // Don't throw on non-zero exit codes
595
+ });
596
+ const exitCode = result.exitCode ?? 0;
597
+ return {
598
+ exitCode,
599
+ success: exitCode === 0
600
+ };
601
+ } catch (error) {
602
+ return this.handleSpawnError(error);
603
+ }
604
+ }
605
+ /**
606
+ * Handle spawn errors
607
+ */
608
+ handleSpawnError(error) {
609
+ const execaError = error;
610
+ if (execaError.code === "ENOENT") {
611
+ console.error("\u274C TestDino: Failed to spawn Playwright");
612
+ console.error("");
613
+ console.error("Playwright is not installed or npx is not available.");
614
+ console.error("");
615
+ console.error("To install Playwright:");
616
+ console.error(" npm install -D @playwright/test");
617
+ console.error(" npx playwright install");
618
+ console.error("");
619
+ return {
620
+ exitCode: 1,
621
+ success: false
622
+ };
623
+ }
624
+ if (execaError.code === "EACCES") {
625
+ console.error("\u274C TestDino: Permission denied when trying to spawn Playwright");
626
+ console.error("");
627
+ console.error("Please check file permissions and try again.");
628
+ console.error("");
629
+ return {
630
+ exitCode: 1,
631
+ success: false
632
+ };
633
+ }
634
+ console.error("\u274C TestDino: Failed to spawn Playwright");
635
+ console.error("");
636
+ console.error("Error:", execaError.message || String(error));
637
+ console.error("");
638
+ return {
639
+ exitCode: 1,
640
+ success: false
641
+ };
642
+ }
643
+ };
644
+
645
+ // src/cli/commands/test.ts
646
+ var TestCommand = class {
647
+ configLoader;
648
+ configDetector;
649
+ configMerger;
650
+ argFilter;
651
+ tempConfigManager;
652
+ playwrightSpawner;
653
+ constructor(configLoader, configDetector, configMerger, argFilter, tempConfigManager, playwrightSpawner) {
654
+ this.configLoader = configLoader || new ConfigLoader();
655
+ this.configDetector = configDetector || new ConfigDetector();
656
+ this.configMerger = configMerger || new ConfigMerger();
657
+ this.argFilter = argFilter || new ArgFilter();
658
+ this.tempConfigManager = tempConfigManager || new TempConfigManager();
659
+ this.playwrightSpawner = playwrightSpawner || new PlaywrightSpawner();
660
+ }
661
+ /**
662
+ * Execute the test command
663
+ * @param options - CLI options from commander
664
+ * @param args - Remaining arguments to pass to Playwright
665
+ * @returns Spawn result with exit code
666
+ */
667
+ async execute(options, args) {
668
+ let tempConfigPath;
669
+ try {
670
+ const testdinoConfigResult = await this.configLoader.load();
671
+ const playwrightConfigResult = await this.configDetector.detect();
672
+ const envConfig = ConfigMerger.getEnvConfig();
673
+ const mergedConfig = this.configMerger.merge({
674
+ env: envConfig,
675
+ playwrightConfig: playwrightConfigResult.options,
676
+ testdinoConfig: testdinoConfigResult.config,
677
+ cliOptions: options
678
+ });
679
+ const tempConfigInfo = this.tempConfigManager.create(mergedConfig);
680
+ tempConfigPath = tempConfigInfo.path;
681
+ const filteredArgs = this.argFilter.filter(args);
682
+ const result = await this.playwrightSpawner.spawn({
683
+ args: filteredArgs,
684
+ tempConfigPath,
685
+ config: mergedConfig
686
+ });
687
+ return result;
688
+ } finally {
689
+ if (tempConfigPath) {
690
+ this.tempConfigManager.cleanup(tempConfigPath);
691
+ }
692
+ }
693
+ }
694
+ };
695
+
696
+ // src/cli/index.ts
697
+ var __filename$1 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)));
698
+ var __dirname$1 = path.dirname(__filename$1);
699
+ function getVersion() {
700
+ try {
701
+ const packagePath = path.join(__dirname$1, "../../package.json");
702
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
703
+ return packageJson.version;
704
+ } catch {
705
+ return "0.0.0";
706
+ }
707
+ }
708
+ function printBanner() {
709
+ const version = getVersion();
710
+ console.log(
711
+ chalk__default.default.cyan(`
712
+ \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
713
+ \u2551 \u2551
714
+ \u2551 ${chalk__default.default.bold("TestDino Playwright")} v${version.padEnd(36)}\u2551
715
+ \u2551 ${chalk__default.default.dim("https://testdino.com")} \u2551
716
+ \u2551 \u2551
717
+ \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
718
+ `)
719
+ );
720
+ }
721
+ function buildProgram() {
722
+ const program = new commander.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");
723
+ 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) => {
724
+ try {
725
+ const args = command.args || [];
726
+ const testCommand = new TestCommand();
727
+ const result = await testCommand.execute(options, args);
728
+ process.exit(result.exitCode);
729
+ } catch (error) {
730
+ console.error(chalk__default.default.red("\n\u2716 Unexpected error:"), error instanceof Error ? error.message : String(error));
731
+ if (options.debug) {
732
+ console.error(chalk__default.default.dim("\nStack trace:"));
733
+ console.error(error);
734
+ }
735
+ process.exit(1);
736
+ }
737
+ });
738
+ return program;
739
+ }
740
+ async function main() {
741
+ try {
742
+ printBanner();
743
+ const program = buildProgram();
744
+ await program.parseAsync(process.argv);
745
+ } catch (error) {
746
+ console.error(chalk__default.default.red("\n\u2716 Error:"), error instanceof Error ? error.message : String(error));
747
+ if (process.env.DEBUG || process.env.TESTDINO_DEBUG) {
748
+ console.error(chalk__default.default.dim("\nStack trace:"));
749
+ console.error(error);
750
+ }
751
+ process.exit(1);
752
+ }
753
+ }
754
+ main().catch((error) => {
755
+ console.error(chalk__default.default.red("Unexpected error:"), error);
756
+ process.exit(1);
757
+ });
758
+ //# sourceMappingURL=index.js.map
759
+ //# sourceMappingURL=index.js.map