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/analysis/natural-language.d.ts +14 -1
- package/dist/analysis/natural-language.d.ts.map +1 -1
- package/dist/analysis/natural-language.js +32 -4
- package/dist/analysis/natural-language.js.map +1 -1
- package/dist/browser.d.ts +27 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +171 -5
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +122 -2
- package/dist/cli.js.map +1 -1
- package/dist/mcp-server-remote.d.ts.map +1 -1
- package/dist/mcp-server-remote.js +48 -37
- package/dist/mcp-server-remote.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +47 -35
- package/dist/mcp-server.js.map +1 -1
- package/dist/types.d.ts +37 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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,
|