cbrowser 7.2.0 ā 7.4.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/analysis/bug-hunter.d.ts +32 -0
- package/dist/analysis/bug-hunter.d.ts.map +1 -0
- package/dist/analysis/bug-hunter.js +106 -0
- package/dist/analysis/bug-hunter.js.map +1 -0
- package/dist/analysis/chaos-testing.d.ts +41 -0
- package/dist/analysis/chaos-testing.d.ts.map +1 -0
- package/dist/analysis/chaos-testing.js +87 -0
- package/dist/analysis/chaos-testing.js.map +1 -0
- package/dist/analysis/index.d.ts +10 -0
- package/dist/analysis/index.d.ts.map +1 -0
- package/dist/analysis/index.js +26 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/analysis/natural-language.d.ts +43 -0
- package/dist/analysis/natural-language.d.ts.map +1 -0
- package/dist/analysis/natural-language.js +205 -0
- package/dist/analysis/natural-language.js.map +1 -0
- package/dist/analysis/persona-comparison.d.ts +31 -0
- package/dist/analysis/persona-comparison.d.ts.map +1 -0
- package/dist/analysis/persona-comparison.js +217 -0
- package/dist/analysis/persona-comparison.js.map +1 -0
- package/dist/browser.d.ts +1 -395
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +0 -4388
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +198 -55
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +2 -1
- package/dist/daemon.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/performance/index.d.ts +7 -0
- package/dist/performance/index.d.ts.map +1 -0
- package/dist/performance/index.js +23 -0
- package/dist/performance/index.js.map +1 -0
- package/dist/performance/metrics.d.ts +49 -0
- package/dist/performance/metrics.d.ts.map +1 -0
- package/dist/performance/metrics.js +386 -0
- package/dist/performance/metrics.js.map +1 -0
- package/dist/testing/coverage.d.ts +39 -0
- package/dist/testing/coverage.d.ts.map +1 -0
- package/dist/testing/coverage.js +713 -0
- package/dist/testing/coverage.js.map +1 -0
- package/dist/testing/flaky-detection.d.ts +28 -0
- package/dist/testing/flaky-detection.d.ts.map +1 -0
- package/dist/testing/flaky-detection.js +332 -0
- package/dist/testing/flaky-detection.js.map +1 -0
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +26 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/nl-test-suite.d.ts +70 -0
- package/dist/testing/nl-test-suite.d.ts.map +1 -0
- package/dist/testing/nl-test-suite.js +427 -0
- package/dist/testing/nl-test-suite.js.map +1 -0
- package/dist/testing/test-repair.d.ts +36 -0
- package/dist/testing/test-repair.d.ts.map +1 -0
- package/dist/testing/test-repair.js +528 -0
- package/dist/testing/test-repair.js.map +1 -0
- package/dist/types.d.ts +125 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/visual/ab-comparison.d.ts +23 -0
- package/dist/visual/ab-comparison.d.ts.map +1 -0
- package/dist/visual/ab-comparison.js +366 -0
- package/dist/visual/ab-comparison.js.map +1 -0
- package/dist/visual/cross-browser.d.ts +41 -0
- package/dist/visual/cross-browser.d.ts.map +1 -0
- package/dist/visual/cross-browser.js +442 -0
- package/dist/visual/cross-browser.js.map +1 -0
- package/dist/visual/index.d.ts +10 -0
- package/dist/visual/index.d.ts.map +1 -0
- package/dist/visual/index.js +26 -0
- package/dist/visual/index.js.map +1 -0
- package/dist/visual/regression.d.ts +55 -0
- package/dist/visual/regression.d.ts.map +1 -0
- package/dist/visual/regression.js +616 -0
- package/dist/visual/regression.js.map +1 -0
- package/dist/visual/responsive.d.ts +27 -0
- package/dist/visual/responsive.d.ts.map +1 -0
- package/dist/visual/responsive.js +450 -0
- package/dist/visual/responsive.js.map +1 -0
- package/package.json +32 -3
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
const browser_js_1 = require("./browser.js");
|
|
10
|
+
// Analysis module imports
|
|
11
|
+
const index_js_1 = require("./analysis/index.js");
|
|
12
|
+
// Testing module imports
|
|
13
|
+
const index_js_2 = require("./testing/index.js");
|
|
14
|
+
// Performance module imports
|
|
15
|
+
const index_js_3 = require("./performance/index.js");
|
|
16
|
+
// Visual module imports
|
|
17
|
+
const index_js_4 = require("./visual/index.js");
|
|
10
18
|
const personas_js_1 = require("./personas.js");
|
|
11
19
|
const types_js_1 = require("./types.js");
|
|
12
20
|
const mcp_server_js_1 = require("./mcp-server.js");
|
|
@@ -14,7 +22,7 @@ const daemon_js_1 = require("./daemon.js");
|
|
|
14
22
|
function showHelp() {
|
|
15
23
|
console.log(`
|
|
16
24
|
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
17
|
-
ā CBrowser CLI v7.
|
|
25
|
+
ā CBrowser CLI v7.4.0 ā
|
|
18
26
|
ā AI-powered browser automation with cross-browser visual testing ā
|
|
19
27
|
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
20
28
|
|
|
@@ -232,7 +240,7 @@ AI VISUAL REGRESSION (v7.0.0)
|
|
|
232
240
|
ai-visual show <name> Show baseline details
|
|
233
241
|
ai-visual delete <name> Delete a baseline
|
|
234
242
|
|
|
235
|
-
CROSS-BROWSER VISUAL TESTING (v7.
|
|
243
|
+
CROSS-BROWSER VISUAL TESTING (v7.3.0)
|
|
236
244
|
cross-browser <url> Compare visual rendering across browsers
|
|
237
245
|
--browsers <list> Browsers to test: chromium,firefox,webkit (default: all)
|
|
238
246
|
--width <n> Viewport width (default: 1920)
|
|
@@ -258,7 +266,7 @@ CROSS-BROWSER VISUAL TESTING (v7.2.0)
|
|
|
258
266
|
"options": { "browsers": ["chromium", "firefox"] }
|
|
259
267
|
}
|
|
260
268
|
|
|
261
|
-
RESPONSIVE VISUAL TESTING (v7.
|
|
269
|
+
RESPONSIVE VISUAL TESTING (v7.3.0)
|
|
262
270
|
responsive <url> Test visual rendering across viewport sizes
|
|
263
271
|
--viewports <list> Viewports to test (default: mobile,tablet,desktop)
|
|
264
272
|
--wait <ms> Wait before screenshot (ms)
|
|
@@ -290,6 +298,36 @@ RESPONSIVE VISUAL TESTING (v7.2.0)
|
|
|
290
298
|
desktop-sm (1280x800) desktop (1440x900) desktop-lg (1920x1080)
|
|
291
299
|
desktop-xl (2560x1440)
|
|
292
300
|
|
|
301
|
+
A/B VISUAL COMPARISON (v7.3.0)
|
|
302
|
+
ab <urlA> <urlB> Compare two URLs visually
|
|
303
|
+
--label-a <name> Label for URL A (default: "Version A")
|
|
304
|
+
--label-b <name> Label for URL B (default: "Version B")
|
|
305
|
+
--width <n> Viewport width (default: 1920)
|
|
306
|
+
--height <n> Viewport height (default: 1080)
|
|
307
|
+
--wait <ms> Wait before screenshot (ms)
|
|
308
|
+
--wait-for <selector> Wait for selector before screenshot
|
|
309
|
+
--sensitivity <level> Comparison sensitivity: low, medium, high
|
|
310
|
+
--html Generate HTML report
|
|
311
|
+
--output <file> Save JSON report to file
|
|
312
|
+
Examples:
|
|
313
|
+
cbrowser ab "https://staging.example.com" "https://example.com"
|
|
314
|
+
cbrowser ab "https://old.site.com" "https://new.site.com" --label-a "Old Design" --label-b "New Design"
|
|
315
|
+
cbrowser ab "https://site-a.com" "https://site-b.com" --html --output comparison.html
|
|
316
|
+
|
|
317
|
+
ab suite <file.json> Run A/B comparison suite
|
|
318
|
+
--html Generate HTML report
|
|
319
|
+
--output <file> Save JSON report to file
|
|
320
|
+
|
|
321
|
+
Suite file format:
|
|
322
|
+
{
|
|
323
|
+
"name": "Staging vs Production",
|
|
324
|
+
"pairs": [
|
|
325
|
+
{ "urlA": "https://staging.example.com", "urlB": "https://example.com", "name": "Homepage" },
|
|
326
|
+
{ "urlA": "https://staging.example.com/about", "urlB": "https://example.com/about", "name": "About" }
|
|
327
|
+
],
|
|
328
|
+
"options": { "sensitivity": "medium" }
|
|
329
|
+
}
|
|
330
|
+
|
|
293
331
|
ACCESSIBILITY (v2.5.0)
|
|
294
332
|
a11y audit Run WCAG accessibility audit
|
|
295
333
|
--url <url> Navigate to URL first
|
|
@@ -1274,7 +1312,7 @@ async function main() {
|
|
|
1274
1312
|
const concurrency = options.concurrency
|
|
1275
1313
|
? parseInt(options.concurrency)
|
|
1276
1314
|
: 3;
|
|
1277
|
-
const comparison = await (0,
|
|
1315
|
+
const comparison = await (0, index_js_1.comparePersonas)({
|
|
1278
1316
|
startUrl,
|
|
1279
1317
|
goal,
|
|
1280
1318
|
personas: personaNames,
|
|
@@ -1282,7 +1320,7 @@ async function main() {
|
|
|
1282
1320
|
headless,
|
|
1283
1321
|
});
|
|
1284
1322
|
// Print formatted report
|
|
1285
|
-
const report = (0,
|
|
1323
|
+
const report = (0, index_js_1.formatComparisonReport)(comparison);
|
|
1286
1324
|
console.log(report);
|
|
1287
1325
|
// Save JSON output if requested
|
|
1288
1326
|
if (options.output) {
|
|
@@ -1990,7 +2028,7 @@ async function main() {
|
|
|
1990
2028
|
}
|
|
1991
2029
|
console.log(`šø Capturing visual baseline: ${options.name}`);
|
|
1992
2030
|
console.log(` URL: ${url}`);
|
|
1993
|
-
const baseline = await (0,
|
|
2031
|
+
const baseline = await (0, index_js_4.captureVisualBaseline)(url, options.name, {
|
|
1994
2032
|
selector: options.selector,
|
|
1995
2033
|
device: options.device,
|
|
1996
2034
|
viewport: options.width || options.height ? {
|
|
@@ -2021,13 +2059,13 @@ async function main() {
|
|
|
2021
2059
|
console.log(`\nš Running AI visual regression test...`);
|
|
2022
2060
|
console.log(` URL: ${url}`);
|
|
2023
2061
|
console.log(` Baseline: ${baselineName}\n`);
|
|
2024
|
-
const result = await (0,
|
|
2062
|
+
const result = await (0, index_js_4.runVisualRegression)(url, baselineName, {
|
|
2025
2063
|
threshold: options.threshold ? parseFloat(options.threshold) : 0.9,
|
|
2026
2064
|
sensitivity: options.sensitivity || "medium",
|
|
2027
2065
|
generateDiff: true,
|
|
2028
2066
|
});
|
|
2029
2067
|
// Print report
|
|
2030
|
-
console.log((0,
|
|
2068
|
+
console.log((0, index_js_4.formatVisualRegressionReport)(result));
|
|
2031
2069
|
// Save JSON output if requested
|
|
2032
2070
|
if (options.output && !options.html) {
|
|
2033
2071
|
const fs = await import("fs");
|
|
@@ -2052,7 +2090,7 @@ async function main() {
|
|
|
2052
2090
|
duration: result.duration,
|
|
2053
2091
|
timestamp: new Date().toISOString(),
|
|
2054
2092
|
};
|
|
2055
|
-
const htmlReport = (0,
|
|
2093
|
+
const htmlReport = (0, index_js_4.generateVisualRegressionHtmlReport)(suiteResult);
|
|
2056
2094
|
const outputPath = options.output || `visual-regression-${baselineName}-${Date.now()}.html`;
|
|
2057
2095
|
fs.writeFileSync(outputPath, htmlReport);
|
|
2058
2096
|
console.log(`\nš HTML report saved to: ${outputPath}`);
|
|
@@ -2078,7 +2116,7 @@ async function main() {
|
|
|
2078
2116
|
}
|
|
2079
2117
|
const suite = JSON.parse(fs.readFileSync(suiteFile, "utf-8"));
|
|
2080
2118
|
console.log(`\nš Running visual regression suite: ${suite.name}`);
|
|
2081
|
-
const result = await (0,
|
|
2119
|
+
const result = await (0, index_js_4.runVisualRegressionSuite)(suite, {
|
|
2082
2120
|
threshold: options.threshold ? parseFloat(options.threshold) : 0.9,
|
|
2083
2121
|
});
|
|
2084
2122
|
// Save outputs
|
|
@@ -2087,7 +2125,7 @@ async function main() {
|
|
|
2087
2125
|
console.log(`š JSON report saved to: ${options.output}`);
|
|
2088
2126
|
}
|
|
2089
2127
|
if (options.html) {
|
|
2090
|
-
const htmlReport = (0,
|
|
2128
|
+
const htmlReport = (0, index_js_4.generateVisualRegressionHtmlReport)(result);
|
|
2091
2129
|
const outputPath = options.output || `visual-suite-${Date.now()}.html`;
|
|
2092
2130
|
fs.writeFileSync(outputPath, htmlReport);
|
|
2093
2131
|
console.log(`š HTML report saved to: ${outputPath}`);
|
|
@@ -2098,7 +2136,7 @@ async function main() {
|
|
|
2098
2136
|
break;
|
|
2099
2137
|
}
|
|
2100
2138
|
case "list": {
|
|
2101
|
-
const baselines = (0,
|
|
2139
|
+
const baselines = (0, index_js_4.listVisualBaselines)();
|
|
2102
2140
|
if (baselines.length === 0) {
|
|
2103
2141
|
console.log("\nNo AI visual baselines saved.\n");
|
|
2104
2142
|
console.log("Capture one with:");
|
|
@@ -2126,7 +2164,7 @@ async function main() {
|
|
|
2126
2164
|
console.error("Usage: cbrowser ai-visual show <name>");
|
|
2127
2165
|
process.exit(1);
|
|
2128
2166
|
}
|
|
2129
|
-
const baseline = (0,
|
|
2167
|
+
const baseline = (0, index_js_4.getVisualBaseline)(name);
|
|
2130
2168
|
if (!baseline) {
|
|
2131
2169
|
console.error(`Baseline not found: ${name}`);
|
|
2132
2170
|
process.exit(1);
|
|
@@ -2153,7 +2191,7 @@ async function main() {
|
|
|
2153
2191
|
console.error("Usage: cbrowser ai-visual delete <name>");
|
|
2154
2192
|
process.exit(1);
|
|
2155
2193
|
}
|
|
2156
|
-
if ((0,
|
|
2194
|
+
if ((0, index_js_4.deleteVisualBaseline)(name)) {
|
|
2157
2195
|
console.log(`ā
Baseline deleted: ${name}`);
|
|
2158
2196
|
}
|
|
2159
2197
|
else {
|
|
@@ -2174,7 +2212,7 @@ async function main() {
|
|
|
2174
2212
|
break;
|
|
2175
2213
|
}
|
|
2176
2214
|
// =========================================================================
|
|
2177
|
-
// Cross-Browser Visual Testing (v7.
|
|
2215
|
+
// Cross-Browser Visual Testing (v7.3.0)
|
|
2178
2216
|
// =========================================================================
|
|
2179
2217
|
case "cross-browser": {
|
|
2180
2218
|
const subcommand = args[0];
|
|
@@ -2193,14 +2231,14 @@ async function main() {
|
|
|
2193
2231
|
process.exit(1);
|
|
2194
2232
|
}
|
|
2195
2233
|
const suite = JSON.parse(fs.readFileSync(suiteFile, "utf-8"));
|
|
2196
|
-
const result = await (0,
|
|
2234
|
+
const result = await (0, index_js_4.runCrossBrowserSuite)(suite);
|
|
2197
2235
|
// Save outputs
|
|
2198
2236
|
if (options.output && !options.html) {
|
|
2199
2237
|
fs.writeFileSync(options.output, JSON.stringify(result, null, 2));
|
|
2200
2238
|
console.log(`\nš JSON report saved to: ${options.output}`);
|
|
2201
2239
|
}
|
|
2202
2240
|
if (options.html) {
|
|
2203
|
-
const htmlReport = (0,
|
|
2241
|
+
const htmlReport = (0, index_js_4.generateCrossBrowserHtmlReport)(result);
|
|
2204
2242
|
const outputPath = options.output || `cross-browser-${Date.now()}.html`;
|
|
2205
2243
|
fs.writeFileSync(outputPath, htmlReport);
|
|
2206
2244
|
console.log(`\nš HTML report saved to: ${outputPath}`);
|
|
@@ -2233,7 +2271,7 @@ async function main() {
|
|
|
2233
2271
|
const browsers = options.browsers
|
|
2234
2272
|
? options.browsers.split(",")
|
|
2235
2273
|
: undefined;
|
|
2236
|
-
const result = await (0,
|
|
2274
|
+
const result = await (0, index_js_4.runCrossBrowserTest)(url, {
|
|
2237
2275
|
browsers,
|
|
2238
2276
|
viewport: options.width || options.height ? {
|
|
2239
2277
|
width: parseInt(options.width) || 1920,
|
|
@@ -2244,7 +2282,7 @@ async function main() {
|
|
|
2244
2282
|
sensitivity: options.sensitivity,
|
|
2245
2283
|
});
|
|
2246
2284
|
// Print report
|
|
2247
|
-
console.log("\n" + (0,
|
|
2285
|
+
console.log("\n" + (0, index_js_4.formatCrossBrowserReport)(result));
|
|
2248
2286
|
// Save outputs
|
|
2249
2287
|
const fs = await import("fs");
|
|
2250
2288
|
if (options.output && !options.html) {
|
|
@@ -2264,7 +2302,7 @@ async function main() {
|
|
|
2264
2302
|
duration: result.duration,
|
|
2265
2303
|
timestamp: result.timestamp,
|
|
2266
2304
|
};
|
|
2267
|
-
const htmlReport = (0,
|
|
2305
|
+
const htmlReport = (0, index_js_4.generateCrossBrowserHtmlReport)(suiteResult);
|
|
2268
2306
|
const outputPath = options.output || `cross-browser-${Date.now()}.html`;
|
|
2269
2307
|
fs.writeFileSync(outputPath, htmlReport);
|
|
2270
2308
|
console.log(`\nš HTML report saved to: ${outputPath}`);
|
|
@@ -2276,13 +2314,13 @@ async function main() {
|
|
|
2276
2314
|
break;
|
|
2277
2315
|
}
|
|
2278
2316
|
// =========================================================================
|
|
2279
|
-
// Responsive Visual Testing (v7.
|
|
2317
|
+
// Responsive Visual Testing (v7.3.0)
|
|
2280
2318
|
// =========================================================================
|
|
2281
2319
|
case "responsive": {
|
|
2282
2320
|
const subcommand = args[0];
|
|
2283
2321
|
if (subcommand === "viewports") {
|
|
2284
2322
|
// List viewport presets
|
|
2285
|
-
const presets = (0,
|
|
2323
|
+
const presets = (0, index_js_4.listViewportPresets)();
|
|
2286
2324
|
console.log("\nš± Available Viewport Presets\n");
|
|
2287
2325
|
console.log("ā".repeat(60));
|
|
2288
2326
|
const byType = {
|
|
@@ -2318,14 +2356,14 @@ async function main() {
|
|
|
2318
2356
|
process.exit(1);
|
|
2319
2357
|
}
|
|
2320
2358
|
const suite = JSON.parse(fs.readFileSync(suiteFile, "utf-8"));
|
|
2321
|
-
const result = await (0,
|
|
2359
|
+
const result = await (0, index_js_4.runResponsiveSuite)(suite);
|
|
2322
2360
|
// Save outputs
|
|
2323
2361
|
if (options.output && !options.html) {
|
|
2324
2362
|
fs.writeFileSync(options.output, JSON.stringify(result, null, 2));
|
|
2325
2363
|
console.log(`\nš JSON report saved to: ${options.output}`);
|
|
2326
2364
|
}
|
|
2327
2365
|
if (options.html) {
|
|
2328
|
-
const htmlReport = (0,
|
|
2366
|
+
const htmlReport = (0, index_js_4.generateResponsiveHtmlReport)(result);
|
|
2329
2367
|
const outputPath = options.output || `responsive-${Date.now()}.html`;
|
|
2330
2368
|
fs.writeFileSync(outputPath, htmlReport);
|
|
2331
2369
|
console.log(`\nš HTML report saved to: ${outputPath}`);
|
|
@@ -2361,14 +2399,14 @@ async function main() {
|
|
|
2361
2399
|
const viewports = options.viewports
|
|
2362
2400
|
? options.viewports.split(",")
|
|
2363
2401
|
: undefined;
|
|
2364
|
-
const result = await (0,
|
|
2402
|
+
const result = await (0, index_js_4.runResponsiveTest)(url, {
|
|
2365
2403
|
viewports,
|
|
2366
2404
|
waitBeforeCapture: options.wait ? parseInt(options.wait) : undefined,
|
|
2367
2405
|
waitForSelector: options["wait-for"],
|
|
2368
2406
|
sensitivity: options.sensitivity,
|
|
2369
2407
|
});
|
|
2370
2408
|
// Print report
|
|
2371
|
-
console.log("\n" + (0,
|
|
2409
|
+
console.log("\n" + (0, index_js_4.formatResponsiveReport)(result));
|
|
2372
2410
|
// Save outputs
|
|
2373
2411
|
const fs = await import("fs");
|
|
2374
2412
|
if (options.output && !options.html) {
|
|
@@ -2390,7 +2428,7 @@ async function main() {
|
|
|
2390
2428
|
duration: result.duration,
|
|
2391
2429
|
timestamp: result.timestamp,
|
|
2392
2430
|
};
|
|
2393
|
-
const htmlReport = (0,
|
|
2431
|
+
const htmlReport = (0, index_js_4.generateResponsiveHtmlReport)(suiteResult);
|
|
2394
2432
|
const outputPath = options.output || `responsive-${Date.now()}.html`;
|
|
2395
2433
|
fs.writeFileSync(outputPath, htmlReport);
|
|
2396
2434
|
console.log(`\nš HTML report saved to: ${outputPath}`);
|
|
@@ -2402,6 +2440,111 @@ async function main() {
|
|
|
2402
2440
|
break;
|
|
2403
2441
|
}
|
|
2404
2442
|
// =========================================================================
|
|
2443
|
+
// A/B Visual Comparison (v7.3.0)
|
|
2444
|
+
// =========================================================================
|
|
2445
|
+
case "ab": {
|
|
2446
|
+
const subcommand = args[0];
|
|
2447
|
+
if (subcommand === "suite") {
|
|
2448
|
+
// A/B suite
|
|
2449
|
+
const suiteFile = args[1];
|
|
2450
|
+
if (!suiteFile) {
|
|
2451
|
+
console.error("Usage: cbrowser ab suite <file.json> [options]");
|
|
2452
|
+
console.error(" --html Generate HTML report");
|
|
2453
|
+
console.error(" --output <file> Save report to file");
|
|
2454
|
+
process.exit(1);
|
|
2455
|
+
}
|
|
2456
|
+
const fs = await import("fs");
|
|
2457
|
+
if (!fs.existsSync(suiteFile)) {
|
|
2458
|
+
console.error(`Suite file not found: ${suiteFile}`);
|
|
2459
|
+
process.exit(1);
|
|
2460
|
+
}
|
|
2461
|
+
const suite = JSON.parse(fs.readFileSync(suiteFile, "utf-8"));
|
|
2462
|
+
const result = await (0, index_js_4.runABSuite)(suite);
|
|
2463
|
+
// Save outputs
|
|
2464
|
+
if (options.output && !options.html) {
|
|
2465
|
+
fs.writeFileSync(options.output, JSON.stringify(result, null, 2));
|
|
2466
|
+
console.log(`\nš JSON report saved to: ${options.output}`);
|
|
2467
|
+
}
|
|
2468
|
+
if (options.html) {
|
|
2469
|
+
const htmlReport = (0, index_js_4.generateABHtmlReport)(result);
|
|
2470
|
+
const outputPath = options.output || `ab-comparison-${Date.now()}.html`;
|
|
2471
|
+
fs.writeFileSync(outputPath, htmlReport);
|
|
2472
|
+
console.log(`\nš HTML report saved to: ${outputPath}`);
|
|
2473
|
+
}
|
|
2474
|
+
// Summary
|
|
2475
|
+
console.log(`\n${"ā".repeat(60)}`);
|
|
2476
|
+
console.log(` Results: ${result.summary.identical} identical, ${result.summary.similar} similar, ${result.summary.different} different, ${result.summary.veryDifferent} very different`);
|
|
2477
|
+
console.log(`${"ā".repeat(60)}\n`);
|
|
2478
|
+
if (result.summary.veryDifferent > 0) {
|
|
2479
|
+
process.exit(1);
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
else {
|
|
2483
|
+
// Single A/B comparison: ab <urlA> <urlB>
|
|
2484
|
+
const urlA = args[0];
|
|
2485
|
+
const urlB = args[1];
|
|
2486
|
+
if (!urlA || !urlB || urlA.startsWith("--") || urlB.startsWith("--")) {
|
|
2487
|
+
console.error("Usage: cbrowser ab <urlA> <urlB> [options]");
|
|
2488
|
+
console.error(" cbrowser ab suite <file.json>");
|
|
2489
|
+
console.error("\nOptions:");
|
|
2490
|
+
console.error(" --label-a <name> Label for URL A (default: 'Version A')");
|
|
2491
|
+
console.error(" --label-b <name> Label for URL B (default: 'Version B')");
|
|
2492
|
+
console.error(" --width <n> Viewport width (default: 1920)");
|
|
2493
|
+
console.error(" --height <n> Viewport height (default: 1080)");
|
|
2494
|
+
console.error(" --wait <ms> Wait before screenshot");
|
|
2495
|
+
console.error(" --wait-for <sel> Wait for selector");
|
|
2496
|
+
console.error(" --sensitivity <l> low, medium, high");
|
|
2497
|
+
console.error(" --html Generate HTML report");
|
|
2498
|
+
console.error(" --output <file> Save report");
|
|
2499
|
+
process.exit(1);
|
|
2500
|
+
}
|
|
2501
|
+
const result = await (0, index_js_4.runABComparison)(urlA, urlB, {
|
|
2502
|
+
labels: options["label-a"] || options["label-b"] ? {
|
|
2503
|
+
a: options["label-a"] || "Version A",
|
|
2504
|
+
b: options["label-b"] || "Version B",
|
|
2505
|
+
} : undefined,
|
|
2506
|
+
viewport: options.width || options.height ? {
|
|
2507
|
+
width: parseInt(options.width) || 1920,
|
|
2508
|
+
height: parseInt(options.height) || 1080,
|
|
2509
|
+
} : undefined,
|
|
2510
|
+
waitBeforeCapture: options.wait ? parseInt(options.wait) : undefined,
|
|
2511
|
+
waitForSelector: options["wait-for"],
|
|
2512
|
+
sensitivity: options.sensitivity,
|
|
2513
|
+
});
|
|
2514
|
+
// Print report
|
|
2515
|
+
console.log("\n" + (0, index_js_4.formatABReport)(result));
|
|
2516
|
+
// Save outputs
|
|
2517
|
+
const fs = await import("fs");
|
|
2518
|
+
if (options.output && !options.html) {
|
|
2519
|
+
fs.writeFileSync(options.output, JSON.stringify(result, null, 2));
|
|
2520
|
+
console.log(`\nš JSON report saved to: ${options.output}`);
|
|
2521
|
+
}
|
|
2522
|
+
if (options.html) {
|
|
2523
|
+
const suiteResult = {
|
|
2524
|
+
suite: { name: "Single Comparison", pairs: [{ urlA, urlB }] },
|
|
2525
|
+
results: [result],
|
|
2526
|
+
summary: {
|
|
2527
|
+
total: 1,
|
|
2528
|
+
identical: result.overallStatus === "identical" ? 1 : 0,
|
|
2529
|
+
similar: result.overallStatus === "similar" ? 1 : 0,
|
|
2530
|
+
different: result.overallStatus === "different" ? 1 : 0,
|
|
2531
|
+
veryDifferent: result.overallStatus === "very_different" ? 1 : 0,
|
|
2532
|
+
},
|
|
2533
|
+
duration: result.duration,
|
|
2534
|
+
timestamp: result.timestamp,
|
|
2535
|
+
};
|
|
2536
|
+
const htmlReport = (0, index_js_4.generateABHtmlReport)(suiteResult);
|
|
2537
|
+
const outputPath = options.output || `ab-comparison-${Date.now()}.html`;
|
|
2538
|
+
fs.writeFileSync(outputPath, htmlReport);
|
|
2539
|
+
console.log(`\nš HTML report saved to: ${outputPath}`);
|
|
2540
|
+
}
|
|
2541
|
+
if (result.overallStatus === "very_different") {
|
|
2542
|
+
process.exit(1);
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
break;
|
|
2546
|
+
}
|
|
2547
|
+
// =========================================================================
|
|
2405
2548
|
// Accessibility (Tier 2)
|
|
2406
2549
|
// =========================================================================
|
|
2407
2550
|
case "a11y": {
|
|
@@ -2781,7 +2924,7 @@ async function main() {
|
|
|
2781
2924
|
process.exit(1);
|
|
2782
2925
|
}
|
|
2783
2926
|
console.log(`\nš£ļø Executing: "${nlCommand}"\n`);
|
|
2784
|
-
const result = await (0,
|
|
2927
|
+
const result = await (0, index_js_1.executeNaturalLanguage)(browser, nlCommand);
|
|
2785
2928
|
if (result.success) {
|
|
2786
2929
|
console.log(`ā Action: ${result.action}`);
|
|
2787
2930
|
if (result.result && typeof result.result === "object") {
|
|
@@ -2817,7 +2960,7 @@ async function main() {
|
|
|
2817
2960
|
const commands = content.split("\n").filter(line => line.trim() && !line.trim().startsWith("#"));
|
|
2818
2961
|
console.log(`\nš Executing script: ${scriptFile}`);
|
|
2819
2962
|
console.log(` Commands: ${commands.length}\n`);
|
|
2820
|
-
const results = await (0,
|
|
2963
|
+
const results = await (0, index_js_1.executeNaturalLanguageScript)(browser, commands);
|
|
2821
2964
|
for (const r of results) {
|
|
2822
2965
|
if (r.success) {
|
|
2823
2966
|
console.log(`ā ${r.command}`);
|
|
@@ -2853,7 +2996,7 @@ async function main() {
|
|
|
2853
2996
|
switch (subcommand) {
|
|
2854
2997
|
case "find": {
|
|
2855
2998
|
console.log(`\nš§ Finding element: "${intent}"\n`);
|
|
2856
|
-
const result = await (0,
|
|
2999
|
+
const result = await (0, index_js_1.findElementByIntent)(browser, intent);
|
|
2857
3000
|
if (result) {
|
|
2858
3001
|
console.log(`ā Found element`);
|
|
2859
3002
|
console.log(` Selector: ${result.selector}`);
|
|
@@ -2868,7 +3011,7 @@ async function main() {
|
|
|
2868
3011
|
}
|
|
2869
3012
|
case "click": {
|
|
2870
3013
|
console.log(`\nš§ Finding and clicking: "${intent}"\n`);
|
|
2871
|
-
const result = await (0,
|
|
3014
|
+
const result = await (0, index_js_1.findElementByIntent)(browser, intent);
|
|
2872
3015
|
if (result) {
|
|
2873
3016
|
console.log(`ā Found: ${result.description}`);
|
|
2874
3017
|
await browser.click(result.selector);
|
|
@@ -2900,7 +3043,7 @@ async function main() {
|
|
|
2900
3043
|
console.log(` URL: ${url}`);
|
|
2901
3044
|
console.log(` Max pages: ${maxPages}`);
|
|
2902
3045
|
console.log(` Timeout: ${timeout}ms\n`);
|
|
2903
|
-
const result = await (0,
|
|
3046
|
+
const result = await (0, index_js_1.huntBugs)(browser, url, { maxPages, timeout });
|
|
2904
3047
|
console.log(`š Bug Hunt Results:\n`);
|
|
2905
3048
|
console.log(` Pages visited: ${result.pagesVisited}`);
|
|
2906
3049
|
console.log(` Duration: ${result.duration}ms`);
|
|
@@ -2937,7 +3080,7 @@ async function main() {
|
|
|
2937
3080
|
console.log(`\nš Cross-Browser Diff`);
|
|
2938
3081
|
console.log(` URL: ${url}`);
|
|
2939
3082
|
console.log(` Browsers: ${browserList.join(", ")}\n`);
|
|
2940
|
-
const result = await (0,
|
|
3083
|
+
const result = await (0, index_js_4.crossBrowserDiff)(url, [...browserList]);
|
|
2941
3084
|
console.log(`š Results:\n`);
|
|
2942
3085
|
console.log(` Metrics:`);
|
|
2943
3086
|
for (const [browser, metrics] of Object.entries(result.metrics)) {
|
|
@@ -2985,7 +3128,7 @@ async function main() {
|
|
|
2985
3128
|
console.log(` URL: ${url}`);
|
|
2986
3129
|
console.log(` Chaos config:`, chaosConfig);
|
|
2987
3130
|
console.log("");
|
|
2988
|
-
const result = await (0,
|
|
3131
|
+
const result = await (0, index_js_1.runChaosTest)(browser, url, chaosConfig);
|
|
2989
3132
|
console.log(`š Results:\n`);
|
|
2990
3133
|
console.log(` Passed: ${result.passed ? "ā
Yes" : "ā No"}`);
|
|
2991
3134
|
console.log(` Duration: ${result.duration}ms`);
|
|
@@ -3039,7 +3182,7 @@ async function main() {
|
|
|
3039
3182
|
const steps = inlineTest.split(";").map(s => s.trim()).filter(s => s);
|
|
3040
3183
|
const testCase = {
|
|
3041
3184
|
name: "Inline Test",
|
|
3042
|
-
steps: steps.map(s => (0,
|
|
3185
|
+
steps: steps.map(s => (0, index_js_2.parseNLInstruction)(s)),
|
|
3043
3186
|
};
|
|
3044
3187
|
suite = { name: "Inline Suite", tests: [testCase] };
|
|
3045
3188
|
}
|
|
@@ -3052,7 +3195,7 @@ async function main() {
|
|
|
3052
3195
|
}
|
|
3053
3196
|
const content = fs.readFileSync(filepath, "utf-8");
|
|
3054
3197
|
const suiteName = filepath.split("/").pop()?.replace(/\.[^.]+$/, "") || "Test Suite";
|
|
3055
|
-
suite = (0,
|
|
3198
|
+
suite = (0, index_js_2.parseNLTestSuite)(content, suiteName);
|
|
3056
3199
|
}
|
|
3057
3200
|
console.log(`\nš Parsed ${suite.tests.length} test(s) from ${inlineTest ? "inline" : filepath}`);
|
|
3058
3201
|
for (const test of suite.tests) {
|
|
@@ -3064,9 +3207,9 @@ async function main() {
|
|
|
3064
3207
|
screenshotOnFailure: options["screenshot-on-failure"] !== false,
|
|
3065
3208
|
headless,
|
|
3066
3209
|
};
|
|
3067
|
-
const result = await (0,
|
|
3210
|
+
const result = await (0, index_js_2.runNLTestSuite)(suite, suiteOptions);
|
|
3068
3211
|
// Print formatted report
|
|
3069
|
-
const report = (0,
|
|
3212
|
+
const report = (0, index_js_2.formatNLTestReport)(result);
|
|
3070
3213
|
console.log(report);
|
|
3071
3214
|
// Save JSON output if requested
|
|
3072
3215
|
if (options.output) {
|
|
@@ -3115,7 +3258,7 @@ async function main() {
|
|
|
3115
3258
|
}
|
|
3116
3259
|
const content = fs.readFileSync(filepath, "utf-8");
|
|
3117
3260
|
const suiteName = filepath.split("/").pop()?.replace(/\.[^.]+$/, "") || "Test Suite";
|
|
3118
|
-
const suite = (0,
|
|
3261
|
+
const suite = (0, index_js_2.parseNLTestSuite)(content, suiteName);
|
|
3119
3262
|
console.log(`\nš Parsed ${suite.tests.length} test(s) from ${filepath}`);
|
|
3120
3263
|
for (const test of suite.tests) {
|
|
3121
3264
|
console.log(` - ${test.name}: ${test.steps.length} steps`);
|
|
@@ -3126,9 +3269,9 @@ async function main() {
|
|
|
3126
3269
|
verifyRepairs: options.verify === true,
|
|
3127
3270
|
maxRetries: options.retries ? parseInt(options.retries) : 3,
|
|
3128
3271
|
};
|
|
3129
|
-
const result = await (0,
|
|
3272
|
+
const result = await (0, index_js_2.repairTestSuite)(suite, repairOptions);
|
|
3130
3273
|
// Print formatted report
|
|
3131
|
-
const report = (0,
|
|
3274
|
+
const report = (0, index_js_2.formatRepairReport)(result);
|
|
3132
3275
|
console.log(report);
|
|
3133
3276
|
// Save JSON report if requested
|
|
3134
3277
|
if (options.json) {
|
|
@@ -3139,7 +3282,7 @@ async function main() {
|
|
|
3139
3282
|
if (options.output && repairOptions.autoApply) {
|
|
3140
3283
|
const repairedContent = [];
|
|
3141
3284
|
for (const testResult of result.testResults) {
|
|
3142
|
-
repairedContent.push((0,
|
|
3285
|
+
repairedContent.push((0, index_js_2.exportRepairedTest)(testResult));
|
|
3143
3286
|
repairedContent.push("");
|
|
3144
3287
|
}
|
|
3145
3288
|
fs.writeFileSync(options.output, repairedContent.join("\n"));
|
|
@@ -3179,7 +3322,7 @@ async function main() {
|
|
|
3179
3322
|
}
|
|
3180
3323
|
const content = fs.readFileSync(filepath, "utf-8");
|
|
3181
3324
|
const suiteName = filepath.split("/").pop()?.replace(/\.[^.]+$/, "") || "Test Suite";
|
|
3182
|
-
const suite = (0,
|
|
3325
|
+
const suite = (0, index_js_2.parseNLTestSuite)(content, suiteName);
|
|
3183
3326
|
console.log(`\nš Parsed ${suite.tests.length} test(s) from ${filepath}`);
|
|
3184
3327
|
for (const test of suite.tests) {
|
|
3185
3328
|
console.log(` - ${test.name}: ${test.steps.length} steps`);
|
|
@@ -3190,9 +3333,9 @@ async function main() {
|
|
|
3190
3333
|
flakinessThreshold: options.threshold ? parseInt(options.threshold) : 20,
|
|
3191
3334
|
delayBetweenRuns: options.delay ? parseInt(options.delay) : 500,
|
|
3192
3335
|
};
|
|
3193
|
-
const result = await (0,
|
|
3336
|
+
const result = await (0, index_js_2.detectFlakyTests)(suite, flakyOptions);
|
|
3194
3337
|
// Print formatted report
|
|
3195
|
-
const report = (0,
|
|
3338
|
+
const report = (0, index_js_2.formatFlakyTestReport)(result);
|
|
3196
3339
|
console.log(report);
|
|
3197
3340
|
// Save JSON report if requested
|
|
3198
3341
|
if (options.output) {
|
|
@@ -3225,7 +3368,7 @@ async function main() {
|
|
|
3225
3368
|
runs: options.runs ? parseInt(options.runs) : 3,
|
|
3226
3369
|
};
|
|
3227
3370
|
console.log(` Running ${baselineOptions.runs} measurement(s)...`);
|
|
3228
|
-
const baseline = await (0,
|
|
3371
|
+
const baseline = await (0, index_js_3.capturePerformanceBaseline)(url, baselineOptions);
|
|
3229
3372
|
console.log(`\nā
Baseline saved: ${baseline.name}`);
|
|
3230
3373
|
console.log(` ID: ${baseline.id}`);
|
|
3231
3374
|
console.log(` URL: ${baseline.url}`);
|
|
@@ -3252,7 +3395,7 @@ async function main() {
|
|
|
3252
3395
|
break;
|
|
3253
3396
|
}
|
|
3254
3397
|
case "list": {
|
|
3255
|
-
const baselines = (0,
|
|
3398
|
+
const baselines = (0, index_js_3.listPerformanceBaselines)();
|
|
3256
3399
|
if (baselines.length === 0) {
|
|
3257
3400
|
console.log("\nš No performance baselines saved yet.");
|
|
3258
3401
|
console.log(" Use: cbrowser perf-baseline save <url> --name <name>");
|
|
@@ -3277,7 +3420,7 @@ async function main() {
|
|
|
3277
3420
|
console.error("Usage: cbrowser perf-baseline show <name|id>");
|
|
3278
3421
|
process.exit(1);
|
|
3279
3422
|
}
|
|
3280
|
-
const baseline = (0,
|
|
3423
|
+
const baseline = (0, index_js_3.loadPerformanceBaseline)(name);
|
|
3281
3424
|
if (!baseline) {
|
|
3282
3425
|
console.error(`Baseline not found: ${name}`);
|
|
3283
3426
|
process.exit(1);
|
|
@@ -3299,7 +3442,7 @@ async function main() {
|
|
|
3299
3442
|
console.error("Usage: cbrowser perf-baseline delete <name|id>");
|
|
3300
3443
|
process.exit(1);
|
|
3301
3444
|
}
|
|
3302
|
-
const deleted = (0,
|
|
3445
|
+
const deleted = (0, index_js_3.deletePerformanceBaseline)(name);
|
|
3303
3446
|
if (deleted) {
|
|
3304
3447
|
console.log(`\nā
Deleted baseline: ${name}`);
|
|
3305
3448
|
}
|
|
@@ -3360,9 +3503,9 @@ async function main() {
|
|
|
3360
3503
|
thresholds: Object.keys(thresholds).length > 0 ? thresholds : undefined,
|
|
3361
3504
|
};
|
|
3362
3505
|
try {
|
|
3363
|
-
const result = await (0,
|
|
3506
|
+
const result = await (0, index_js_3.detectPerformanceRegression)(url, baselineName, regressionOptions);
|
|
3364
3507
|
// Print formatted report
|
|
3365
|
-
const report = (0,
|
|
3508
|
+
const report = (0, index_js_3.formatPerformanceRegressionReport)(result);
|
|
3366
3509
|
console.log("\n" + report);
|
|
3367
3510
|
// Save JSON report if requested
|
|
3368
3511
|
const fs = await import("fs");
|
|
@@ -3428,7 +3571,7 @@ async function main() {
|
|
|
3428
3571
|
maxPages: 50, // Quick mode
|
|
3429
3572
|
minCoverage: 50,
|
|
3430
3573
|
};
|
|
3431
|
-
const result = await (0,
|
|
3574
|
+
const result = await (0, index_js_2.generateCoverageMap)(url, testFiles, coverageOptions);
|
|
3432
3575
|
// Show only gaps
|
|
3433
3576
|
console.log(`\nš³ļø Coverage Gaps (${result.gaps.length} found):\n`);
|
|
3434
3577
|
const priorityEmoji = { critical: "šØ", high: "š“", medium: "š”", low: "š¢" };
|
|
@@ -3474,10 +3617,10 @@ async function main() {
|
|
|
3474
3617
|
else {
|
|
3475
3618
|
console.log(` Crawling site (max ${coverageOptions.maxPages} pages)...`);
|
|
3476
3619
|
}
|
|
3477
|
-
const result = await (0,
|
|
3620
|
+
const result = await (0, index_js_2.generateCoverageMap)(url, testFiles, coverageOptions);
|
|
3478
3621
|
// Output format
|
|
3479
3622
|
if (options.html) {
|
|
3480
|
-
const htmlReport = (0,
|
|
3623
|
+
const htmlReport = (0, index_js_2.generateCoverageHtmlReport)(result);
|
|
3481
3624
|
const outputPath = options.output || "coverage-report.html";
|
|
3482
3625
|
fs.writeFileSync(outputPath, htmlReport);
|
|
3483
3626
|
console.log(`\nā
HTML report saved: ${outputPath}`);
|
|
@@ -3488,7 +3631,7 @@ async function main() {
|
|
|
3488
3631
|
}
|
|
3489
3632
|
else {
|
|
3490
3633
|
// Print text report
|
|
3491
|
-
const report = (0,
|
|
3634
|
+
const report = (0, index_js_2.formatCoverageReport)(result);
|
|
3492
3635
|
console.log(report);
|
|
3493
3636
|
if (options.output) {
|
|
3494
3637
|
fs.writeFileSync(options.output, report);
|