cbrowser 6.4.0 → 7.0.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/browser.d.ts +79 -2
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +1313 -0
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +375 -2
- package/dist/cli.js.map +1 -1
- package/dist/types.d.ts +236 -13
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -3
package/dist/cli.js
CHANGED
|
@@ -14,8 +14,8 @@ const daemon_js_1 = require("./daemon.js");
|
|
|
14
14
|
function showHelp() {
|
|
15
15
|
console.log(`
|
|
16
16
|
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
17
|
-
║ CBrowser CLI
|
|
18
|
-
║ AI-powered browser automation with
|
|
17
|
+
║ CBrowser CLI v7.0.0 ║
|
|
18
|
+
║ AI-powered browser automation with AI visual regression testing ║
|
|
19
19
|
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
20
20
|
|
|
21
21
|
NAVIGATION
|
|
@@ -99,6 +99,26 @@ FLAKY TEST DETECTION (v6.3.0)
|
|
|
99
99
|
cbrowser flaky-check tests.txt --runs 10
|
|
100
100
|
cbrowser flaky-check tests.txt --runs 5 --threshold 30 --output flaky-report.json
|
|
101
101
|
|
|
102
|
+
TEST COVERAGE MAP (v6.5.0)
|
|
103
|
+
coverage <url> Generate test coverage map for a site
|
|
104
|
+
--tests <glob> Test files to analyze (default: tests/*.txt)
|
|
105
|
+
--sitemap <url> Use sitemap.xml instead of crawling
|
|
106
|
+
--max-pages <n> Max pages to crawl (default: 100)
|
|
107
|
+
--include <pattern> Only include paths matching pattern
|
|
108
|
+
--exclude <pattern> Exclude paths matching pattern
|
|
109
|
+
--min-coverage <n> Min coverage % to not flag (default: 50)
|
|
110
|
+
--html Generate HTML report
|
|
111
|
+
--output <file> Save JSON report to file
|
|
112
|
+
Examples:
|
|
113
|
+
cbrowser coverage "https://example.com" --tests "tests/*.txt"
|
|
114
|
+
cbrowser coverage "https://example.com" --sitemap "https://example.com/sitemap.xml"
|
|
115
|
+
cbrowser coverage "https://example.com" --html --output coverage.html
|
|
116
|
+
cbrowser coverage "https://example.com" --exclude "/admin" --min-coverage 70
|
|
117
|
+
|
|
118
|
+
coverage gaps <url> Show only untested pages (quick analysis)
|
|
119
|
+
--tests <glob> Test files to analyze
|
|
120
|
+
--sitemap <url> Use sitemap.xml
|
|
121
|
+
|
|
102
122
|
PERSONAS
|
|
103
123
|
persona list List all personas (built-in + custom)
|
|
104
124
|
persona create "<desc>" Create persona from natural language description
|
|
@@ -182,6 +202,36 @@ VISUAL REGRESSION (v2.5.0)
|
|
|
182
202
|
visual list List all saved baselines
|
|
183
203
|
visual delete <name> Delete a baseline
|
|
184
204
|
|
|
205
|
+
AI VISUAL REGRESSION (v7.0.0)
|
|
206
|
+
ai-visual capture <url> Capture AI-analyzed visual baseline
|
|
207
|
+
--name <name> Baseline name (required)
|
|
208
|
+
--selector <sel> Capture specific element instead of viewport
|
|
209
|
+
--device <device> Device emulation (iphone-15, pixel-7, etc.)
|
|
210
|
+
--width <n> Viewport width (default: 1920)
|
|
211
|
+
--height <n> Viewport height (default: 1080)
|
|
212
|
+
--wait <ms|selector> Wait before capture (ms or CSS selector)
|
|
213
|
+
Examples:
|
|
214
|
+
cbrowser ai-visual capture "https://example.com" --name homepage
|
|
215
|
+
cbrowser ai-visual capture "https://example.com/dashboard" --name dashboard --device iphone-15
|
|
216
|
+
|
|
217
|
+
ai-visual test <url> <baseline> Test current page against baseline using AI
|
|
218
|
+
--threshold <n> Similarity threshold 0-1 (default: 0.9)
|
|
219
|
+
--sensitivity <level> Detection sensitivity: low, medium, high (default: medium)
|
|
220
|
+
--html Generate HTML report
|
|
221
|
+
--output <file> Save JSON report to file
|
|
222
|
+
Examples:
|
|
223
|
+
cbrowser ai-visual test "https://staging.example.com" homepage
|
|
224
|
+
cbrowser ai-visual test "https://staging.example.com" homepage --sensitivity high
|
|
225
|
+
|
|
226
|
+
ai-visual suite <file.json> Run visual regression suite
|
|
227
|
+
--threshold <n> Global similarity threshold (default: 0.9)
|
|
228
|
+
--html Generate HTML report
|
|
229
|
+
--output <file> Save JSON report to file
|
|
230
|
+
|
|
231
|
+
ai-visual list List all AI visual baselines
|
|
232
|
+
ai-visual show <name> Show baseline details
|
|
233
|
+
ai-visual delete <name> Delete a baseline
|
|
234
|
+
|
|
185
235
|
ACCESSIBILITY (v2.5.0)
|
|
186
236
|
a11y audit Run WCAG accessibility audit
|
|
187
237
|
--url <url> Navigate to URL first
|
|
@@ -1864,6 +1914,208 @@ async function main() {
|
|
|
1864
1914
|
break;
|
|
1865
1915
|
}
|
|
1866
1916
|
// =========================================================================
|
|
1917
|
+
// AI Visual Regression (v7.0.0)
|
|
1918
|
+
// =========================================================================
|
|
1919
|
+
case "ai-visual": {
|
|
1920
|
+
const subcommand = args[0];
|
|
1921
|
+
switch (subcommand) {
|
|
1922
|
+
case "capture": {
|
|
1923
|
+
const url = args[1];
|
|
1924
|
+
if (!url || !options.name) {
|
|
1925
|
+
console.error("Usage: cbrowser ai-visual capture <url> --name <name> [options]");
|
|
1926
|
+
console.error(" --selector <sel> Capture specific element");
|
|
1927
|
+
console.error(" --device <device> Device emulation");
|
|
1928
|
+
console.error(" --width <n> Viewport width");
|
|
1929
|
+
console.error(" --height <n> Viewport height");
|
|
1930
|
+
console.error(" --wait <ms|sel> Wait before capture");
|
|
1931
|
+
process.exit(1);
|
|
1932
|
+
}
|
|
1933
|
+
console.log(`📸 Capturing visual baseline: ${options.name}`);
|
|
1934
|
+
console.log(` URL: ${url}`);
|
|
1935
|
+
const baseline = await (0, browser_js_1.captureVisualBaseline)(url, options.name, {
|
|
1936
|
+
selector: options.selector,
|
|
1937
|
+
device: options.device,
|
|
1938
|
+
viewport: options.width || options.height ? {
|
|
1939
|
+
width: parseInt(options.width) || 1920,
|
|
1940
|
+
height: parseInt(options.height) || 1080,
|
|
1941
|
+
} : undefined,
|
|
1942
|
+
waitFor: options.wait ? (/^\d+$/.test(options.wait)
|
|
1943
|
+
? parseInt(options.wait)
|
|
1944
|
+
: options.wait) : undefined,
|
|
1945
|
+
});
|
|
1946
|
+
console.log(`\n✅ Baseline captured successfully!`);
|
|
1947
|
+
console.log(` ID: ${baseline.id}`);
|
|
1948
|
+
console.log(` Viewport: ${baseline.viewport.width}x${baseline.viewport.height}`);
|
|
1949
|
+
console.log(` Screenshot: ${baseline.screenshotPath}`);
|
|
1950
|
+
break;
|
|
1951
|
+
}
|
|
1952
|
+
case "test": {
|
|
1953
|
+
const url = args[1];
|
|
1954
|
+
const baselineName = args[2];
|
|
1955
|
+
if (!url || !baselineName) {
|
|
1956
|
+
console.error("Usage: cbrowser ai-visual test <url> <baseline-name> [options]");
|
|
1957
|
+
console.error(" --threshold <n> Similarity threshold 0-1 (default: 0.9)");
|
|
1958
|
+
console.error(" --sensitivity <l> low, medium, high (default: medium)");
|
|
1959
|
+
console.error(" --html Generate HTML report");
|
|
1960
|
+
console.error(" --output <file> Save JSON report");
|
|
1961
|
+
process.exit(1);
|
|
1962
|
+
}
|
|
1963
|
+
console.log(`\n🔍 Running AI visual regression test...`);
|
|
1964
|
+
console.log(` URL: ${url}`);
|
|
1965
|
+
console.log(` Baseline: ${baselineName}\n`);
|
|
1966
|
+
const result = await (0, browser_js_1.runVisualRegression)(url, baselineName, {
|
|
1967
|
+
threshold: options.threshold ? parseFloat(options.threshold) : 0.9,
|
|
1968
|
+
sensitivity: options.sensitivity || "medium",
|
|
1969
|
+
generateDiff: true,
|
|
1970
|
+
});
|
|
1971
|
+
// Print report
|
|
1972
|
+
console.log((0, browser_js_1.formatVisualRegressionReport)(result));
|
|
1973
|
+
// Save JSON output if requested
|
|
1974
|
+
if (options.output && !options.html) {
|
|
1975
|
+
const fs = await import("fs");
|
|
1976
|
+
fs.writeFileSync(options.output, JSON.stringify(result, null, 2));
|
|
1977
|
+
console.log(`\n📄 JSON report saved to: ${options.output}`);
|
|
1978
|
+
}
|
|
1979
|
+
// Generate HTML report if requested
|
|
1980
|
+
if (options.html) {
|
|
1981
|
+
const fs = await import("fs");
|
|
1982
|
+
const suiteResult = {
|
|
1983
|
+
suite: {
|
|
1984
|
+
name: baselineName,
|
|
1985
|
+
pages: [{ name: baselineName, url, baselineName }],
|
|
1986
|
+
},
|
|
1987
|
+
results: [result],
|
|
1988
|
+
summary: {
|
|
1989
|
+
total: 1,
|
|
1990
|
+
passed: result.passed ? 1 : 0,
|
|
1991
|
+
failed: result.passed ? 0 : 1,
|
|
1992
|
+
warnings: result.analysis.overallStatus === "warning" ? 1 : 0,
|
|
1993
|
+
},
|
|
1994
|
+
duration: result.duration,
|
|
1995
|
+
timestamp: new Date().toISOString(),
|
|
1996
|
+
};
|
|
1997
|
+
const htmlReport = (0, browser_js_1.generateVisualRegressionHtmlReport)(suiteResult);
|
|
1998
|
+
const outputPath = options.output || `visual-regression-${baselineName}-${Date.now()}.html`;
|
|
1999
|
+
fs.writeFileSync(outputPath, htmlReport);
|
|
2000
|
+
console.log(`\n📄 HTML report saved to: ${outputPath}`);
|
|
2001
|
+
}
|
|
2002
|
+
if (!result.passed) {
|
|
2003
|
+
process.exit(1);
|
|
2004
|
+
}
|
|
2005
|
+
break;
|
|
2006
|
+
}
|
|
2007
|
+
case "suite": {
|
|
2008
|
+
const suiteFile = args[1];
|
|
2009
|
+
if (!suiteFile) {
|
|
2010
|
+
console.error("Usage: cbrowser ai-visual suite <file.json> [options]");
|
|
2011
|
+
console.error(" --threshold <n> Global similarity threshold");
|
|
2012
|
+
console.error(" --html Generate HTML report");
|
|
2013
|
+
console.error(" --output <file> Save report to file");
|
|
2014
|
+
process.exit(1);
|
|
2015
|
+
}
|
|
2016
|
+
const fs = await import("fs");
|
|
2017
|
+
if (!fs.existsSync(suiteFile)) {
|
|
2018
|
+
console.error(`Suite file not found: ${suiteFile}`);
|
|
2019
|
+
process.exit(1);
|
|
2020
|
+
}
|
|
2021
|
+
const suite = JSON.parse(fs.readFileSync(suiteFile, "utf-8"));
|
|
2022
|
+
console.log(`\n🔍 Running visual regression suite: ${suite.name}`);
|
|
2023
|
+
const result = await (0, browser_js_1.runVisualRegressionSuite)(suite, {
|
|
2024
|
+
threshold: options.threshold ? parseFloat(options.threshold) : 0.9,
|
|
2025
|
+
});
|
|
2026
|
+
// Save outputs
|
|
2027
|
+
if (options.output && !options.html) {
|
|
2028
|
+
fs.writeFileSync(options.output, JSON.stringify(result, null, 2));
|
|
2029
|
+
console.log(`📄 JSON report saved to: ${options.output}`);
|
|
2030
|
+
}
|
|
2031
|
+
if (options.html) {
|
|
2032
|
+
const htmlReport = (0, browser_js_1.generateVisualRegressionHtmlReport)(result);
|
|
2033
|
+
const outputPath = options.output || `visual-suite-${Date.now()}.html`;
|
|
2034
|
+
fs.writeFileSync(outputPath, htmlReport);
|
|
2035
|
+
console.log(`📄 HTML report saved to: ${outputPath}`);
|
|
2036
|
+
}
|
|
2037
|
+
if (result.summary.failed > 0) {
|
|
2038
|
+
process.exit(1);
|
|
2039
|
+
}
|
|
2040
|
+
break;
|
|
2041
|
+
}
|
|
2042
|
+
case "list": {
|
|
2043
|
+
const baselines = (0, browser_js_1.listVisualBaselines)();
|
|
2044
|
+
if (baselines.length === 0) {
|
|
2045
|
+
console.log("\nNo AI visual baselines saved.\n");
|
|
2046
|
+
console.log("Capture one with:");
|
|
2047
|
+
console.log(" cbrowser ai-visual capture <url> --name <name>\n");
|
|
2048
|
+
}
|
|
2049
|
+
else {
|
|
2050
|
+
console.log("\n📸 AI Visual Baselines:\n");
|
|
2051
|
+
console.log("┌──────────────────────┬─────────────────────────────────┬────────────────────┬──────────────┐");
|
|
2052
|
+
console.log("│ Name │ URL │ Viewport │ Created │");
|
|
2053
|
+
console.log("├──────────────────────┼─────────────────────────────────┼────────────────────┼──────────────┤");
|
|
2054
|
+
for (const b of baselines) {
|
|
2055
|
+
const name = b.name.substring(0, 20).padEnd(20);
|
|
2056
|
+
const url = b.url.substring(0, 31).padEnd(31);
|
|
2057
|
+
const viewport = `${b.viewport.width}x${b.viewport.height}`.padEnd(18);
|
|
2058
|
+
const created = new Date(b.timestamp).toLocaleDateString().padEnd(12);
|
|
2059
|
+
console.log(`│ ${name} │ ${url} │ ${viewport} │ ${created} │`);
|
|
2060
|
+
}
|
|
2061
|
+
console.log("└──────────────────────┴─────────────────────────────────┴────────────────────┴──────────────┘\n");
|
|
2062
|
+
}
|
|
2063
|
+
break;
|
|
2064
|
+
}
|
|
2065
|
+
case "show": {
|
|
2066
|
+
const name = args[1];
|
|
2067
|
+
if (!name) {
|
|
2068
|
+
console.error("Usage: cbrowser ai-visual show <name>");
|
|
2069
|
+
process.exit(1);
|
|
2070
|
+
}
|
|
2071
|
+
const baseline = (0, browser_js_1.getVisualBaseline)(name);
|
|
2072
|
+
if (!baseline) {
|
|
2073
|
+
console.error(`Baseline not found: ${name}`);
|
|
2074
|
+
process.exit(1);
|
|
2075
|
+
}
|
|
2076
|
+
console.log("\n📸 AI Visual Baseline Details:\n");
|
|
2077
|
+
console.log(` Name: ${baseline.name}`);
|
|
2078
|
+
console.log(` ID: ${baseline.id}`);
|
|
2079
|
+
console.log(` URL: ${baseline.url}`);
|
|
2080
|
+
console.log(` Viewport: ${baseline.viewport.width}x${baseline.viewport.height}`);
|
|
2081
|
+
if (baseline.device) {
|
|
2082
|
+
console.log(` Device: ${baseline.device}`);
|
|
2083
|
+
}
|
|
2084
|
+
if (baseline.selector) {
|
|
2085
|
+
console.log(` Selector: ${baseline.selector}`);
|
|
2086
|
+
}
|
|
2087
|
+
console.log(` Created: ${baseline.timestamp}`);
|
|
2088
|
+
console.log(` Screenshot: ${baseline.screenshotPath}`);
|
|
2089
|
+
console.log("");
|
|
2090
|
+
break;
|
|
2091
|
+
}
|
|
2092
|
+
case "delete": {
|
|
2093
|
+
const name = args[1];
|
|
2094
|
+
if (!name) {
|
|
2095
|
+
console.error("Usage: cbrowser ai-visual delete <name>");
|
|
2096
|
+
process.exit(1);
|
|
2097
|
+
}
|
|
2098
|
+
if ((0, browser_js_1.deleteVisualBaseline)(name)) {
|
|
2099
|
+
console.log(`✅ Baseline deleted: ${name}`);
|
|
2100
|
+
}
|
|
2101
|
+
else {
|
|
2102
|
+
console.error(`Baseline not found: ${name}`);
|
|
2103
|
+
process.exit(1);
|
|
2104
|
+
}
|
|
2105
|
+
break;
|
|
2106
|
+
}
|
|
2107
|
+
default:
|
|
2108
|
+
console.error("Usage: cbrowser ai-visual [capture|test|suite|list|show|delete]");
|
|
2109
|
+
console.error(" cbrowser ai-visual capture <url> --name <name>");
|
|
2110
|
+
console.error(" cbrowser ai-visual test <url> <baseline>");
|
|
2111
|
+
console.error(" cbrowser ai-visual suite <file.json>");
|
|
2112
|
+
console.error(" cbrowser ai-visual list");
|
|
2113
|
+
console.error(" cbrowser ai-visual show <name>");
|
|
2114
|
+
console.error(" cbrowser ai-visual delete <name>");
|
|
2115
|
+
}
|
|
2116
|
+
break;
|
|
2117
|
+
}
|
|
2118
|
+
// =========================================================================
|
|
1867
2119
|
// Accessibility (Tier 2)
|
|
1868
2120
|
// =========================================================================
|
|
1869
2121
|
case "a11y": {
|
|
@@ -2843,6 +3095,127 @@ async function main() {
|
|
|
2843
3095
|
}
|
|
2844
3096
|
break;
|
|
2845
3097
|
}
|
|
3098
|
+
// =========================================================================
|
|
3099
|
+
// Test Coverage Map (Tier 6 - v6.5.0)
|
|
3100
|
+
// =========================================================================
|
|
3101
|
+
case "coverage": {
|
|
3102
|
+
const fs = await import("fs");
|
|
3103
|
+
const path = await import("path");
|
|
3104
|
+
const subcommand = args[0];
|
|
3105
|
+
// Simple glob function for test files
|
|
3106
|
+
function findTestFiles(pattern) {
|
|
3107
|
+
const files = [];
|
|
3108
|
+
const parts = pattern.split("/");
|
|
3109
|
+
const dir = parts.slice(0, -1).join("/") || ".";
|
|
3110
|
+
const filePattern = parts[parts.length - 1];
|
|
3111
|
+
const regex = new RegExp("^" + filePattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$");
|
|
3112
|
+
try {
|
|
3113
|
+
const dirFiles = fs.readdirSync(dir);
|
|
3114
|
+
for (const file of dirFiles) {
|
|
3115
|
+
if (regex.test(file)) {
|
|
3116
|
+
files.push(path.join(dir, file));
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
catch {
|
|
3121
|
+
// Directory doesn't exist
|
|
3122
|
+
}
|
|
3123
|
+
return files;
|
|
3124
|
+
}
|
|
3125
|
+
// Handle "coverage gaps" subcommand
|
|
3126
|
+
if (subcommand === "gaps") {
|
|
3127
|
+
const url = args[1];
|
|
3128
|
+
if (!url) {
|
|
3129
|
+
console.error("Usage: cbrowser coverage gaps <url> [--tests <glob>] [--sitemap <url>]");
|
|
3130
|
+
process.exit(1);
|
|
3131
|
+
}
|
|
3132
|
+
console.log(`\n🔍 Finding untested pages for: ${url}`);
|
|
3133
|
+
const testPattern = options.tests || "tests/*.txt";
|
|
3134
|
+
const testFiles = findTestFiles(testPattern);
|
|
3135
|
+
if (testFiles.length === 0) {
|
|
3136
|
+
console.error(`No test files found matching: ${testPattern}`);
|
|
3137
|
+
process.exit(1);
|
|
3138
|
+
}
|
|
3139
|
+
console.log(` Analyzing ${testFiles.length} test file(s)...`);
|
|
3140
|
+
const coverageOptions = {
|
|
3141
|
+
sitemapUrl: options.sitemap,
|
|
3142
|
+
maxPages: 50, // Quick mode
|
|
3143
|
+
minCoverage: 50,
|
|
3144
|
+
};
|
|
3145
|
+
const result = await (0, browser_js_1.generateCoverageMap)(url, testFiles, coverageOptions);
|
|
3146
|
+
// Show only gaps
|
|
3147
|
+
console.log(`\n🕳️ Coverage Gaps (${result.gaps.length} found):\n`);
|
|
3148
|
+
const priorityEmoji = { critical: "🚨", high: "🔴", medium: "🟡", low: "🟢" };
|
|
3149
|
+
for (const gap of result.gaps) {
|
|
3150
|
+
const emoji = priorityEmoji[gap.priority];
|
|
3151
|
+
console.log(` ${emoji} ${gap.page.path}`);
|
|
3152
|
+
console.log(` Priority: ${gap.priority} | Reason: ${gap.reason}`);
|
|
3153
|
+
}
|
|
3154
|
+
console.log(`\n📊 Coverage: ${result.analysis.coveragePercent}% (${result.analysis.testedPages}/${result.analysis.totalPages} pages)`);
|
|
3155
|
+
break;
|
|
3156
|
+
}
|
|
3157
|
+
// Main coverage command
|
|
3158
|
+
const url = subcommand;
|
|
3159
|
+
if (!url || url.startsWith("-")) {
|
|
3160
|
+
console.error("Usage: cbrowser coverage <url> [--tests <glob>] [--sitemap <url>] [--html] [--output <file>]");
|
|
3161
|
+
process.exit(1);
|
|
3162
|
+
}
|
|
3163
|
+
console.log(`\n📊 Generating test coverage map for: ${url}`);
|
|
3164
|
+
const testPattern = options.tests || "tests/*.txt";
|
|
3165
|
+
const testFiles = findTestFiles(testPattern);
|
|
3166
|
+
if (testFiles.length === 0) {
|
|
3167
|
+
console.error(`No test files found matching: ${testPattern}`);
|
|
3168
|
+
console.error("Use --tests <glob> to specify test files");
|
|
3169
|
+
process.exit(1);
|
|
3170
|
+
}
|
|
3171
|
+
console.log(` Found ${testFiles.length} test file(s)`);
|
|
3172
|
+
for (const f of testFiles.slice(0, 5)) {
|
|
3173
|
+
console.log(` - ${f}`);
|
|
3174
|
+
}
|
|
3175
|
+
if (testFiles.length > 5) {
|
|
3176
|
+
console.log(` ... and ${testFiles.length - 5} more`);
|
|
3177
|
+
}
|
|
3178
|
+
const coverageOptions = {
|
|
3179
|
+
sitemapUrl: options.sitemap,
|
|
3180
|
+
maxPages: options["max-pages"] ? parseInt(options["max-pages"]) : 100,
|
|
3181
|
+
includePattern: options.include,
|
|
3182
|
+
excludePattern: options.exclude,
|
|
3183
|
+
minCoverage: options["min-coverage"] ? parseInt(options["min-coverage"]) : 50,
|
|
3184
|
+
};
|
|
3185
|
+
if (coverageOptions.sitemapUrl) {
|
|
3186
|
+
console.log(` Using sitemap: ${coverageOptions.sitemapUrl}`);
|
|
3187
|
+
}
|
|
3188
|
+
else {
|
|
3189
|
+
console.log(` Crawling site (max ${coverageOptions.maxPages} pages)...`);
|
|
3190
|
+
}
|
|
3191
|
+
const result = await (0, browser_js_1.generateCoverageMap)(url, testFiles, coverageOptions);
|
|
3192
|
+
// Output format
|
|
3193
|
+
if (options.html) {
|
|
3194
|
+
const htmlReport = (0, browser_js_1.generateCoverageHtmlReport)(result);
|
|
3195
|
+
const outputPath = options.output || "coverage-report.html";
|
|
3196
|
+
fs.writeFileSync(outputPath, htmlReport);
|
|
3197
|
+
console.log(`\n✅ HTML report saved: ${outputPath}`);
|
|
3198
|
+
}
|
|
3199
|
+
else if (options.output && options.output.endsWith(".json")) {
|
|
3200
|
+
fs.writeFileSync(options.output, JSON.stringify(result, null, 2));
|
|
3201
|
+
console.log(`\n✅ JSON report saved: ${options.output}`);
|
|
3202
|
+
}
|
|
3203
|
+
else {
|
|
3204
|
+
// Print text report
|
|
3205
|
+
const report = (0, browser_js_1.formatCoverageReport)(result);
|
|
3206
|
+
console.log(report);
|
|
3207
|
+
if (options.output) {
|
|
3208
|
+
fs.writeFileSync(options.output, report);
|
|
3209
|
+
console.log(`\n📄 Report saved: ${options.output}`);
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
// Exit with error if coverage too low
|
|
3213
|
+
if (result.analysis.coveragePercent < (coverageOptions.minCoverage || 50)) {
|
|
3214
|
+
console.log(`\n⚠️ Coverage (${result.analysis.coveragePercent}%) is below threshold (${coverageOptions.minCoverage}%)`);
|
|
3215
|
+
process.exit(1);
|
|
3216
|
+
}
|
|
3217
|
+
break;
|
|
3218
|
+
}
|
|
2846
3219
|
default:
|
|
2847
3220
|
console.error(`Unknown command: ${command}`);
|
|
2848
3221
|
console.error("Run 'cbrowser help' for usage");
|