cbrowser 7.4.14 → 7.4.16

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.
package/dist/cli.js CHANGED
@@ -35,8 +35,12 @@ NAVIGATION
35
35
  INTERACTION
36
36
  click <selector> Click element (tries text, label, role, CSS)
37
37
  --url <url> Navigate to URL first, then click
38
+ --verbose Show available elements and AI suggestions on failure
39
+ --debug-dir <dir> Save debug screenshots to directory
38
40
  fill <selector> <value> Fill input field
39
41
  --url <url> Navigate to URL first, then fill
42
+ --verbose Show available inputs and AI suggestions on failure
43
+ --debug-dir <dir> Save debug screenshots to directory
40
44
 
41
45
  EXTRACTION
42
46
  extract <what> Extract data (links, images, headings, forms)
@@ -67,10 +71,15 @@ NATURAL LANGUAGE TEST SUITES (v6.1.0)
67
71
  --output <file> Save JSON report to file
68
72
  --html Generate HTML report
69
73
  --timeout <ms> Timeout per step (default: 30000)
74
+ --dry-run Parse and display test steps without executing
75
+ --fuzzy-match Use case-insensitive fuzzy matching for assertions
76
+ --step-through Pause before each step for interactive execution
70
77
  test-suite --inline "..." Run inline test (semicolon-separated steps)
71
78
  Examples:
72
79
  cbrowser test-suite login-flow.txt --html
73
80
  cbrowser test-suite --inline "go to https://example.com ; click login ; verify url contains /dashboard"
81
+ cbrowser test-suite login-flow.txt --dry-run
82
+ cbrowser test-suite login-flow.txt --step-through
74
83
 
75
84
  Test File Format:
76
85
  # Test: Login Flow
@@ -1107,12 +1116,26 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
1107
1116
  if (options.url) {
1108
1117
  await browser.navigate(options.url);
1109
1118
  }
1110
- const result = await browser.click(selector, { force: options.force === true });
1119
+ const verbose = options.verbose === true;
1120
+ const debugDir = options["debug-dir"];
1121
+ const result = await browser.click(selector, { force: options.force === true, verbose, debugDir });
1111
1122
  if (result.success) {
1112
1123
  console.log(`✓ ${result.message}`);
1113
1124
  }
1114
1125
  else {
1115
1126
  console.error(`✗ ${result.message}`);
1127
+ if (verbose && result.aiSuggestion) {
1128
+ console.error(`\n💡 ${result.aiSuggestion}`);
1129
+ }
1130
+ if (verbose && result.availableElements && result.availableElements.length > 0) {
1131
+ console.error(`\n📋 Available clickable elements:`);
1132
+ for (const el of result.availableElements) {
1133
+ console.error(` ${el.tag}: "${el.text}" → ${el.selector}`);
1134
+ }
1135
+ }
1136
+ if (verbose && result.debugScreenshot) {
1137
+ console.error(`\n📸 Debug screenshot: ${result.debugScreenshot}`);
1138
+ }
1116
1139
  process.exit(1);
1117
1140
  }
1118
1141
  break;
@@ -1127,12 +1150,27 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
1127
1150
  if (options.url) {
1128
1151
  await browser.navigate(options.url);
1129
1152
  }
1130
- const result = await browser.fill(selector, value);
1153
+ const verbose = options.verbose === true;
1154
+ const debugDir = options["debug-dir"];
1155
+ const result = await browser.fill(selector, value, { verbose, debugDir });
1131
1156
  if (result.success) {
1132
1157
  console.log(`✓ ${result.message}`);
1133
1158
  }
1134
1159
  else {
1135
1160
  console.error(`✗ ${result.message}`);
1161
+ if (verbose && result.aiSuggestion) {
1162
+ console.error(`\n💡 ${result.aiSuggestion}`);
1163
+ }
1164
+ if (verbose && result.availableInputs && result.availableInputs.length > 0) {
1165
+ console.error(`\n📋 Available input fields:`);
1166
+ for (const input of result.availableInputs) {
1167
+ const desc = input.label || input.placeholder || input.name || input.type;
1168
+ console.error(` ${desc} (${input.type}) → ${input.selector}`);
1169
+ }
1170
+ }
1171
+ if (verbose && result.debugScreenshot) {
1172
+ console.error(`\n📸 Debug screenshot: ${result.debugScreenshot}`);
1173
+ }
1136
1174
  process.exit(1);
1137
1175
  }
1138
1176
  break;
@@ -3332,6 +3370,9 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
3332
3370
  console.error(" --output <file> Save JSON report to file");
3333
3371
  console.error(" --html Generate HTML report");
3334
3372
  console.error(" --timeout <ms> Timeout per step (default: 30000)");
3373
+ console.error(" --dry-run Parse and display test steps without executing");
3374
+ console.error(" --fuzzy-match Case-insensitive fuzzy matching for assertions");
3375
+ console.error(" --step-through Pause before each step for interactive execution");
3335
3376
  console.error("");
3336
3377
  console.error("Test File Format:");
3337
3378
  console.error(" # Test: Login Flow");
@@ -3366,11 +3407,111 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
3366
3407
  for (const test of suite.tests) {
3367
3408
  console.log(` - ${test.name}: ${test.steps.length} steps`);
3368
3409
  }
3410
+ // Dry-run mode: parse and display without executing
3411
+ if (options["dry-run"]) {
3412
+ const dryResult = (0, index_js_2.dryRunNLTestSuite)(suite);
3413
+ console.log(`\n🔍 DRY RUN - Parsed steps (no execution):\n`);
3414
+ for (const test of dryResult.tests) {
3415
+ console.log(` 📋 ${test.name}`);
3416
+ for (let i = 0; i < test.steps.length; i++) {
3417
+ const step = test.steps[i];
3418
+ const parsed = `[${step.action}${step.target ? `: ${step.target}` : ""}${step.value ? ` = "${step.value}"` : ""}]`;
3419
+ console.log(` ${i + 1}. ${step.instruction} ${parsed}`);
3420
+ }
3421
+ console.log("");
3422
+ }
3423
+ break;
3424
+ }
3425
+ // Step-through mode: pause before each step
3426
+ if (options["step-through"]) {
3427
+ const readline = await import("readline");
3428
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: process.stdin.isTTY || false });
3429
+ const ask = (q) => new Promise((resolve, reject) => {
3430
+ rl.question(q, resolve);
3431
+ rl.once("close", () => reject(new Error("stdin closed")));
3432
+ });
3433
+ console.log(`\n🔍 Step-through mode enabled. Press Enter to execute each step.\n`);
3434
+ const { CBrowser } = await import("./browser.js");
3435
+ const stepBrowser = new CBrowser({ headless });
3436
+ await stepBrowser.launch();
3437
+ try {
3438
+ let totalSteps = suite.tests.reduce((sum, t) => sum + t.steps.length, 0);
3439
+ let stepNum = 0;
3440
+ let stopped = false;
3441
+ for (const test of suite.tests) {
3442
+ if (stopped)
3443
+ break;
3444
+ console.log(`\n📋 Test: ${test.name}`);
3445
+ for (const step of test.steps) {
3446
+ stepNum++;
3447
+ const parsed = `[${step.action}${step.target ? `: ${step.target}` : ""}${step.value ? ` = "${step.value}"` : ""}]`;
3448
+ console.log(`\nStep ${stepNum}/${totalSteps}: ${step.instruction}`);
3449
+ console.log(` → Parsed as: ${parsed}`);
3450
+ let input = "";
3451
+ try {
3452
+ input = await ask(" Press [Enter] to execute, [s] to skip, [q] to quit: ");
3453
+ }
3454
+ catch {
3455
+ console.log("\n⏹ Stopped (stdin closed).");
3456
+ stopped = true;
3457
+ break;
3458
+ }
3459
+ if (input.trim().toLowerCase() === "q") {
3460
+ console.log("\n⏹ Stopped by user.");
3461
+ stopped = true;
3462
+ break;
3463
+ }
3464
+ if (input.trim().toLowerCase() === "s") {
3465
+ console.log(" ⏭ Skipped");
3466
+ continue;
3467
+ }
3468
+ const start = Date.now();
3469
+ try {
3470
+ switch (step.action) {
3471
+ case "navigate":
3472
+ await stepBrowser.navigate(step.target || "");
3473
+ break;
3474
+ case "click":
3475
+ await stepBrowser.smartClick(step.target || "");
3476
+ break;
3477
+ case "fill":
3478
+ await stepBrowser.fill(step.target || "", step.value || "");
3479
+ break;
3480
+ case "assert":
3481
+ const r = await stepBrowser.assert(step.instruction);
3482
+ if (!r.passed)
3483
+ throw new Error(r.message);
3484
+ break;
3485
+ case "screenshot":
3486
+ await stepBrowser.screenshot();
3487
+ break;
3488
+ case "wait":
3489
+ if (step.value)
3490
+ await new Promise(r => setTimeout(r, parseFloat(step.value) * 1000));
3491
+ break;
3492
+ default:
3493
+ await stepBrowser.smartClick(step.target || step.instruction);
3494
+ }
3495
+ console.log(` ✓ Completed (${Date.now() - start}ms)`);
3496
+ }
3497
+ catch (e) {
3498
+ console.log(` ✗ Failed: ${e.message} (${Date.now() - start}ms)`);
3499
+ }
3500
+ }
3501
+ }
3502
+ }
3503
+ finally {
3504
+ rl.close();
3505
+ await stepBrowser.close();
3506
+ }
3507
+ break;
3508
+ }
3369
3509
  const suiteOptions = {
3370
3510
  stepTimeout: options.timeout ? parseInt(options.timeout) : 30000,
3371
3511
  continueOnFailure: options["continue-on-failure"] === true,
3372
3512
  screenshotOnFailure: options["screenshot-on-failure"] !== false,
3373
3513
  headless,
3514
+ fuzzyMatch: options["fuzzy-match"] === true,
3374
3515
  };
3375
3516
  const result = await (0, index_js_2.runNLTestSuite)(suite, suiteOptions);
3376
3517
  // Print formatted report