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/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 +143 -2
- package/dist/cli.js.map +1 -1
- package/dist/mcp-server-remote.d.ts.map +1 -1
- package/dist/mcp-server-remote.js +103 -43
- package/dist/mcp-server-remote.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +102 -41
- package/dist/mcp-server.js.map +1 -1
- package/dist/testing/nl-test-suite.d.ts +15 -0
- package/dist/testing/nl-test-suite.d.ts.map +1 -1
- package/dist/testing/nl-test-suite.js +195 -31
- package/dist/testing/nl-test-suite.js.map +1 -1
- package/dist/types.d.ts +56 -1
- 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)
|
|
@@ -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
|
|
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
|
|
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
|