cbrowser 7.4.15 → 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)
@@ -69,11 +73,13 @@ NATURAL LANGUAGE TEST SUITES (v6.1.0)
69
73
  --timeout <ms> Timeout per step (default: 30000)
70
74
  --dry-run Parse and display test steps without executing
71
75
  --fuzzy-match Use case-insensitive fuzzy matching for assertions
76
+ --step-through Pause before each step for interactive execution
72
77
  test-suite --inline "..." Run inline test (semicolon-separated steps)
73
78
  Examples:
74
79
  cbrowser test-suite login-flow.txt --html
75
80
  cbrowser test-suite --inline "go to https://example.com ; click login ; verify url contains /dashboard"
76
81
  cbrowser test-suite login-flow.txt --dry-run
82
+ cbrowser test-suite login-flow.txt --step-through
77
83
 
78
84
  Test File Format:
79
85
  # Test: Login Flow
@@ -1110,12 +1116,26 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
1110
1116
  if (options.url) {
1111
1117
  await browser.navigate(options.url);
1112
1118
  }
1113
- 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 });
1114
1122
  if (result.success) {
1115
1123
  console.log(`✓ ${result.message}`);
1116
1124
  }
1117
1125
  else {
1118
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
+ }
1119
1139
  process.exit(1);
1120
1140
  }
1121
1141
  break;
@@ -1130,12 +1150,27 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
1130
1150
  if (options.url) {
1131
1151
  await browser.navigate(options.url);
1132
1152
  }
1133
- 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 });
1134
1156
  if (result.success) {
1135
1157
  console.log(`✓ ${result.message}`);
1136
1158
  }
1137
1159
  else {
1138
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
+ }
1139
1174
  process.exit(1);
1140
1175
  }
1141
1176
  break;
@@ -3337,6 +3372,7 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
3337
3372
  console.error(" --timeout <ms> Timeout per step (default: 30000)");
3338
3373
  console.error(" --dry-run Parse and display test steps without executing");
3339
3374
  console.error(" --fuzzy-match Case-insensitive fuzzy matching for assertions");
3375
+ console.error(" --step-through Pause before each step for interactive execution");
3340
3376
  console.error("");
3341
3377
  console.error("Test File Format:");
3342
3378
  console.error(" # Test: Login Flow");
@@ -3386,6 +3422,90 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
3386
3422
  }
3387
3423
  break;
3388
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
+ }
3389
3509
  const suiteOptions = {
3390
3510
  stepTimeout: options.timeout ? parseInt(options.timeout) : 30000,
3391
3511
  continueOnFailure: options["continue-on-failure"] === true,