cbrowser 7.1.1 → 7.3.0

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
@@ -14,7 +14,7 @@ const daemon_js_1 = require("./daemon.js");
14
14
  function showHelp() {
15
15
  console.log(`
16
16
  ╔══════════════════════════════════════════════════════════════════════════════╗
17
- ║ CBrowser CLI v7.1.1
17
+ ║ CBrowser CLI v7.3.0
18
18
  ║ AI-powered browser automation with cross-browser visual testing ║
19
19
  ╚══════════════════════════════════════════════════════════════════════════════╝
20
20
 
@@ -232,7 +232,7 @@ AI VISUAL REGRESSION (v7.0.0)
232
232
  ai-visual show <name> Show baseline details
233
233
  ai-visual delete <name> Delete a baseline
234
234
 
235
- CROSS-BROWSER VISUAL TESTING (v7.1.1)
235
+ CROSS-BROWSER VISUAL TESTING (v7.3.0)
236
236
  cross-browser <url> Compare visual rendering across browsers
237
237
  --browsers <list> Browsers to test: chromium,firefox,webkit (default: all)
238
238
  --width <n> Viewport width (default: 1920)
@@ -258,6 +258,68 @@ CROSS-BROWSER VISUAL TESTING (v7.1.1)
258
258
  "options": { "browsers": ["chromium", "firefox"] }
259
259
  }
260
260
 
261
+ RESPONSIVE VISUAL TESTING (v7.3.0)
262
+ responsive <url> Test visual rendering across viewport sizes
263
+ --viewports <list> Viewports to test (default: mobile,tablet,desktop)
264
+ --wait <ms> Wait before screenshot (ms)
265
+ --wait-for <selector> Wait for selector before screenshot
266
+ --sensitivity <level> Comparison sensitivity: low, medium, high
267
+ --html Generate HTML report
268
+ --output <file> Save JSON report to file
269
+ Examples:
270
+ cbrowser responsive "https://example.com"
271
+ cbrowser responsive "https://example.com" --viewports mobile,tablet,desktop-lg
272
+ cbrowser responsive "https://example.com" --html --output report.html
273
+
274
+ responsive suite <file.json> Run responsive test suite
275
+ --html Generate HTML report
276
+ --output <file> Save JSON report to file
277
+
278
+ responsive viewports List available viewport presets
279
+
280
+ Suite file format:
281
+ {
282
+ "name": "My Site Responsive",
283
+ "urls": ["https://example.com", "https://example.com/about"],
284
+ "options": { "viewports": ["mobile", "tablet", "desktop"] }
285
+ }
286
+
287
+ Available viewport presets:
288
+ mobile-sm (320x568) mobile (375x667) mobile-lg (414x896)
289
+ mobile-xl (428x926) tablet (768x1024) tablet-lg (1024x1366)
290
+ desktop-sm (1280x800) desktop (1440x900) desktop-lg (1920x1080)
291
+ desktop-xl (2560x1440)
292
+
293
+ A/B VISUAL COMPARISON (v7.3.0)
294
+ ab <urlA> <urlB> Compare two URLs visually
295
+ --label-a <name> Label for URL A (default: "Version A")
296
+ --label-b <name> Label for URL B (default: "Version B")
297
+ --width <n> Viewport width (default: 1920)
298
+ --height <n> Viewport height (default: 1080)
299
+ --wait <ms> Wait before screenshot (ms)
300
+ --wait-for <selector> Wait for selector before screenshot
301
+ --sensitivity <level> Comparison sensitivity: low, medium, high
302
+ --html Generate HTML report
303
+ --output <file> Save JSON report to file
304
+ Examples:
305
+ cbrowser ab "https://staging.example.com" "https://example.com"
306
+ cbrowser ab "https://old.site.com" "https://new.site.com" --label-a "Old Design" --label-b "New Design"
307
+ cbrowser ab "https://site-a.com" "https://site-b.com" --html --output comparison.html
308
+
309
+ ab suite <file.json> Run A/B comparison suite
310
+ --html Generate HTML report
311
+ --output <file> Save JSON report to file
312
+
313
+ Suite file format:
314
+ {
315
+ "name": "Staging vs Production",
316
+ "pairs": [
317
+ { "urlA": "https://staging.example.com", "urlB": "https://example.com", "name": "Homepage" },
318
+ { "urlA": "https://staging.example.com/about", "urlB": "https://example.com/about", "name": "About" }
319
+ ],
320
+ "options": { "sensitivity": "medium" }
321
+ }
322
+
261
323
  ACCESSIBILITY (v2.5.0)
262
324
  a11y audit Run WCAG accessibility audit
263
325
  --url <url> Navigate to URL first
@@ -2142,7 +2204,7 @@ async function main() {
2142
2204
  break;
2143
2205
  }
2144
2206
  // =========================================================================
2145
- // Cross-Browser Visual Testing (v7.1.1)
2207
+ // Cross-Browser Visual Testing (v7.3.0)
2146
2208
  // =========================================================================
2147
2209
  case "cross-browser": {
2148
2210
  const subcommand = args[0];
@@ -2244,6 +2306,237 @@ async function main() {
2244
2306
  break;
2245
2307
  }
2246
2308
  // =========================================================================
2309
+ // Responsive Visual Testing (v7.3.0)
2310
+ // =========================================================================
2311
+ case "responsive": {
2312
+ const subcommand = args[0];
2313
+ if (subcommand === "viewports") {
2314
+ // List viewport presets
2315
+ const presets = (0, browser_js_1.listViewportPresets)();
2316
+ console.log("\n📱 Available Viewport Presets\n");
2317
+ console.log("─".repeat(60));
2318
+ const byType = {
2319
+ mobile: [],
2320
+ tablet: [],
2321
+ desktop: [],
2322
+ };
2323
+ for (const preset of presets) {
2324
+ byType[preset.deviceType].push(preset);
2325
+ }
2326
+ for (const [type, typePresets] of Object.entries(byType)) {
2327
+ console.log(`\n${type.toUpperCase()}:`);
2328
+ for (const p of typePresets) {
2329
+ const touch = p.hasTouch ? " (touch)" : "";
2330
+ const device = p.deviceName ? ` - ${p.deviceName}` : "";
2331
+ console.log(` ${p.name.padEnd(15)} ${p.width}x${p.height}${touch}${device}`);
2332
+ }
2333
+ }
2334
+ console.log("");
2335
+ }
2336
+ else if (subcommand === "suite") {
2337
+ // Responsive suite
2338
+ const suiteFile = args[1];
2339
+ if (!suiteFile) {
2340
+ console.error("Usage: cbrowser responsive suite <file.json> [options]");
2341
+ console.error(" --html Generate HTML report");
2342
+ console.error(" --output <file> Save report to file");
2343
+ process.exit(1);
2344
+ }
2345
+ const fs = await import("fs");
2346
+ if (!fs.existsSync(suiteFile)) {
2347
+ console.error(`Suite file not found: ${suiteFile}`);
2348
+ process.exit(1);
2349
+ }
2350
+ const suite = JSON.parse(fs.readFileSync(suiteFile, "utf-8"));
2351
+ const result = await (0, browser_js_1.runResponsiveSuite)(suite);
2352
+ // Save outputs
2353
+ if (options.output && !options.html) {
2354
+ fs.writeFileSync(options.output, JSON.stringify(result, null, 2));
2355
+ console.log(`\n📄 JSON report saved to: ${options.output}`);
2356
+ }
2357
+ if (options.html) {
2358
+ const htmlReport = (0, browser_js_1.generateResponsiveHtmlReport)(result);
2359
+ const outputPath = options.output || `responsive-${Date.now()}.html`;
2360
+ fs.writeFileSync(outputPath, htmlReport);
2361
+ console.log(`\n📄 HTML report saved to: ${outputPath}`);
2362
+ }
2363
+ // Summary
2364
+ console.log(`\n${"═".repeat(60)}`);
2365
+ console.log(` Results: ${result.summary.responsive} responsive, ${result.summary.minorIssues} minor, ${result.summary.majorIssues} major`);
2366
+ console.log(` Total issues: ${result.summary.totalIssues}`);
2367
+ console.log(`${"═".repeat(60)}\n`);
2368
+ if (result.summary.majorIssues > 0) {
2369
+ process.exit(1);
2370
+ }
2371
+ }
2372
+ else {
2373
+ // Single URL test
2374
+ const url = subcommand; // First arg is the URL
2375
+ if (!url || url.startsWith("--")) {
2376
+ console.error("Usage: cbrowser responsive <url> [options]");
2377
+ console.error(" cbrowser responsive suite <file.json>");
2378
+ console.error(" cbrowser responsive viewports");
2379
+ console.error("\nOptions:");
2380
+ console.error(" --viewports <list> mobile,tablet,desktop (default: mobile,tablet,desktop)");
2381
+ console.error(" --wait <ms> Wait before screenshot");
2382
+ console.error(" --wait-for <sel> Wait for selector");
2383
+ console.error(" --sensitivity <l> low, medium, high");
2384
+ console.error(" --html Generate HTML report");
2385
+ console.error(" --output <file> Save report");
2386
+ console.error("\nViewport presets: mobile-sm, mobile, mobile-lg, mobile-xl,");
2387
+ console.error(" tablet, tablet-lg, desktop-sm, desktop,");
2388
+ console.error(" desktop-lg, desktop-xl");
2389
+ process.exit(1);
2390
+ }
2391
+ const viewports = options.viewports
2392
+ ? options.viewports.split(",")
2393
+ : undefined;
2394
+ const result = await (0, browser_js_1.runResponsiveTest)(url, {
2395
+ viewports,
2396
+ waitBeforeCapture: options.wait ? parseInt(options.wait) : undefined,
2397
+ waitForSelector: options["wait-for"],
2398
+ sensitivity: options.sensitivity,
2399
+ });
2400
+ // Print report
2401
+ console.log("\n" + (0, browser_js_1.formatResponsiveReport)(result));
2402
+ // Save outputs
2403
+ const fs = await import("fs");
2404
+ if (options.output && !options.html) {
2405
+ fs.writeFileSync(options.output, JSON.stringify(result, null, 2));
2406
+ console.log(`\n📄 JSON report saved to: ${options.output}`);
2407
+ }
2408
+ if (options.html) {
2409
+ const suiteResult = {
2410
+ suite: { name: "Single URL Test", urls: [url] },
2411
+ results: [result],
2412
+ summary: {
2413
+ total: 1,
2414
+ responsive: result.overallStatus === "responsive" ? 1 : 0,
2415
+ minorIssues: result.overallStatus === "minor_issues" ? 1 : 0,
2416
+ majorIssues: result.overallStatus === "major_issues" ? 1 : 0,
2417
+ totalIssues: result.issues.length,
2418
+ },
2419
+ commonIssues: result.issues,
2420
+ duration: result.duration,
2421
+ timestamp: result.timestamp,
2422
+ };
2423
+ const htmlReport = (0, browser_js_1.generateResponsiveHtmlReport)(suiteResult);
2424
+ const outputPath = options.output || `responsive-${Date.now()}.html`;
2425
+ fs.writeFileSync(outputPath, htmlReport);
2426
+ console.log(`\n📄 HTML report saved to: ${outputPath}`);
2427
+ }
2428
+ if (result.overallStatus === "major_issues") {
2429
+ process.exit(1);
2430
+ }
2431
+ }
2432
+ break;
2433
+ }
2434
+ // =========================================================================
2435
+ // A/B Visual Comparison (v7.3.0)
2436
+ // =========================================================================
2437
+ case "ab": {
2438
+ const subcommand = args[0];
2439
+ if (subcommand === "suite") {
2440
+ // A/B suite
2441
+ const suiteFile = args[1];
2442
+ if (!suiteFile) {
2443
+ console.error("Usage: cbrowser ab suite <file.json> [options]");
2444
+ console.error(" --html Generate HTML report");
2445
+ console.error(" --output <file> Save report to file");
2446
+ process.exit(1);
2447
+ }
2448
+ const fs = await import("fs");
2449
+ if (!fs.existsSync(suiteFile)) {
2450
+ console.error(`Suite file not found: ${suiteFile}`);
2451
+ process.exit(1);
2452
+ }
2453
+ const suite = JSON.parse(fs.readFileSync(suiteFile, "utf-8"));
2454
+ const result = await (0, browser_js_1.runABSuite)(suite);
2455
+ // Save outputs
2456
+ if (options.output && !options.html) {
2457
+ fs.writeFileSync(options.output, JSON.stringify(result, null, 2));
2458
+ console.log(`\n📄 JSON report saved to: ${options.output}`);
2459
+ }
2460
+ if (options.html) {
2461
+ const htmlReport = (0, browser_js_1.generateABHtmlReport)(result);
2462
+ const outputPath = options.output || `ab-comparison-${Date.now()}.html`;
2463
+ fs.writeFileSync(outputPath, htmlReport);
2464
+ console.log(`\n📄 HTML report saved to: ${outputPath}`);
2465
+ }
2466
+ // Summary
2467
+ console.log(`\n${"═".repeat(60)}`);
2468
+ console.log(` Results: ${result.summary.identical} identical, ${result.summary.similar} similar, ${result.summary.different} different, ${result.summary.veryDifferent} very different`);
2469
+ console.log(`${"═".repeat(60)}\n`);
2470
+ if (result.summary.veryDifferent > 0) {
2471
+ process.exit(1);
2472
+ }
2473
+ }
2474
+ else {
2475
+ // Single A/B comparison: ab <urlA> <urlB>
2476
+ const urlA = args[0];
2477
+ const urlB = args[1];
2478
+ if (!urlA || !urlB || urlA.startsWith("--") || urlB.startsWith("--")) {
2479
+ console.error("Usage: cbrowser ab <urlA> <urlB> [options]");
2480
+ console.error(" cbrowser ab suite <file.json>");
2481
+ console.error("\nOptions:");
2482
+ console.error(" --label-a <name> Label for URL A (default: 'Version A')");
2483
+ console.error(" --label-b <name> Label for URL B (default: 'Version B')");
2484
+ console.error(" --width <n> Viewport width (default: 1920)");
2485
+ console.error(" --height <n> Viewport height (default: 1080)");
2486
+ console.error(" --wait <ms> Wait before screenshot");
2487
+ console.error(" --wait-for <sel> Wait for selector");
2488
+ console.error(" --sensitivity <l> low, medium, high");
2489
+ console.error(" --html Generate HTML report");
2490
+ console.error(" --output <file> Save report");
2491
+ process.exit(1);
2492
+ }
2493
+ const result = await (0, browser_js_1.runABComparison)(urlA, urlB, {
2494
+ labels: options["label-a"] || options["label-b"] ? {
2495
+ a: options["label-a"] || "Version A",
2496
+ b: options["label-b"] || "Version B",
2497
+ } : undefined,
2498
+ viewport: options.width || options.height ? {
2499
+ width: parseInt(options.width) || 1920,
2500
+ height: parseInt(options.height) || 1080,
2501
+ } : undefined,
2502
+ waitBeforeCapture: options.wait ? parseInt(options.wait) : undefined,
2503
+ waitForSelector: options["wait-for"],
2504
+ sensitivity: options.sensitivity,
2505
+ });
2506
+ // Print report
2507
+ console.log("\n" + (0, browser_js_1.formatABReport)(result));
2508
+ // Save outputs
2509
+ const fs = await import("fs");
2510
+ if (options.output && !options.html) {
2511
+ fs.writeFileSync(options.output, JSON.stringify(result, null, 2));
2512
+ console.log(`\n📄 JSON report saved to: ${options.output}`);
2513
+ }
2514
+ if (options.html) {
2515
+ const suiteResult = {
2516
+ suite: { name: "Single Comparison", pairs: [{ urlA, urlB }] },
2517
+ results: [result],
2518
+ summary: {
2519
+ total: 1,
2520
+ identical: result.overallStatus === "identical" ? 1 : 0,
2521
+ similar: result.overallStatus === "similar" ? 1 : 0,
2522
+ different: result.overallStatus === "different" ? 1 : 0,
2523
+ veryDifferent: result.overallStatus === "very_different" ? 1 : 0,
2524
+ },
2525
+ duration: result.duration,
2526
+ timestamp: result.timestamp,
2527
+ };
2528
+ const htmlReport = (0, browser_js_1.generateABHtmlReport)(suiteResult);
2529
+ const outputPath = options.output || `ab-comparison-${Date.now()}.html`;
2530
+ fs.writeFileSync(outputPath, htmlReport);
2531
+ console.log(`\n📄 HTML report saved to: ${outputPath}`);
2532
+ }
2533
+ if (result.overallStatus === "very_different") {
2534
+ process.exit(1);
2535
+ }
2536
+ }
2537
+ break;
2538
+ }
2539
+ // =========================================================================
2247
2540
  // Accessibility (Tier 2)
2248
2541
  // =========================================================================
2249
2542
  case "a11y": {