cbrowser 19.5.0 ā 19.6.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/THIRD_PARTY_LICENSES.md +39 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +34 -7
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +97 -95
- package/dist/cli.js.map +1 -1
- package/dist/cognitive/index.d.ts +5 -0
- package/dist/cognitive/index.d.ts.map +1 -1
- package/dist/cognitive/index.js +5 -0
- package/dist/cognitive/index.js.map +1 -1
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/daemon.d.ts +4 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +15 -7
- package/dist/daemon.js.map +1 -1
- package/dist/device-names.d.ts +10 -0
- package/dist/device-names.d.ts.map +1 -0
- package/dist/device-names.js +16 -0
- package/dist/device-names.js.map +1 -0
- package/dist/device-presets-devtools.d.ts +3 -0
- package/dist/device-presets-devtools.d.ts.map +1 -0
- package/dist/device-presets-devtools.js +823 -0
- package/dist/device-presets-devtools.js.map +1 -0
- package/dist/devices.d.ts +15 -0
- package/dist/devices.d.ts.map +1 -0
- package/dist/devices.js +196 -0
- package/dist/devices.js.map +1 -0
- package/dist/eval-script.d.ts +29 -0
- package/dist/eval-script.d.ts.map +1 -0
- package/dist/eval-script.js +46 -0
- package/dist/eval-script.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-tools/base/audit-tools.js +1 -1
- package/dist/mcp-tools/base/audit-tools.js.map +1 -1
- package/dist/testing/nl-test-suite.d.ts +7 -0
- package/dist/testing/nl-test-suite.d.ts.map +1 -1
- package/dist/testing/nl-test-suite.js +3 -1
- package/dist/testing/nl-test-suite.js.map +1 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -99
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,8 @@ import { capturePerformanceBaseline, listPerformanceBaselines, loadPerformanceBa
|
|
|
20
20
|
// Visual module imports
|
|
21
21
|
import { captureVisualBaseline, listVisualBaselines, getVisualBaseline, deleteVisualBaseline, runVisualRegression, runVisualRegressionSuite, formatVisualRegressionReport, generateVisualRegressionHtmlReport, runCrossBrowserTest, runCrossBrowserSuite, formatCrossBrowserReport, generateCrossBrowserHtmlReport, runResponsiveTest, runResponsiveSuite, formatResponsiveReport, generateResponsiveHtmlReport, listViewportPresets, runABComparison, runABSuite, formatABReport, generateABHtmlReport, crossBrowserDiff } from "./visual/index.js";
|
|
22
22
|
import { BUILTIN_PERSONAS, loadCustomPersonas, saveCustomPersona, deleteCustomPersona, isBuiltinPersona, generatePersonaFromDescription, listAccessibilityPersonas, } from "./personas.js";
|
|
23
|
-
import {
|
|
23
|
+
import { LOCATION_PRESETS } from "./types.js";
|
|
24
|
+
import { CBROWSER_DEVICE_KEYS, DEVICE_PRESETS, resolveDevice } from "./devices.js";
|
|
24
25
|
import { startMcpServer } from "./mcp-server.js";
|
|
25
26
|
import { startRemoteMcpServer } from "./mcp-server-remote.js";
|
|
26
27
|
import { startDaemon, stopDaemon, getDaemonStatus, isDaemonRunning, sendToDaemon, runDaemonServer } from "./daemon.js";
|
|
@@ -30,6 +31,7 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync,
|
|
|
30
31
|
import { resolveCookieDomain, hostFromUrl } from "./utils.js";
|
|
31
32
|
import { dirname, join, resolve } from "node:path";
|
|
32
33
|
import { guardConsole, guardTerminalOutput } from "./terminal-safe.js";
|
|
34
|
+
import { evaluateScript } from "./eval-script.js";
|
|
33
35
|
import { VideoCaptureSession } from "./recording/engine.js";
|
|
34
36
|
import { changeSignal, parseManifest } from "./recording/types.js";
|
|
35
37
|
import { printEnterpriseStatus } from "./stealth/index.js";
|
|
@@ -299,7 +301,7 @@ COOKIE MANAGEMENT
|
|
|
299
301
|
cookie clear Clear all cookies
|
|
300
302
|
|
|
301
303
|
DEVICE EMULATION
|
|
302
|
-
device list
|
|
304
|
+
device list [filter] List device presets, one per line (filter by name)
|
|
303
305
|
device set <name> Set device emulation for session
|
|
304
306
|
Example: cbrowser device set iphone-15
|
|
305
307
|
|
|
@@ -745,6 +747,8 @@ CLAUDE SKILL INSTALLATION & SYNC
|
|
|
745
747
|
DAEMON MODE (v6.4.0)
|
|
746
748
|
daemon start Start background daemon (keeps browser running)
|
|
747
749
|
--port <port> Daemon port (default: 9222)
|
|
750
|
+
--device <name> Device the daemon's browser emulates
|
|
751
|
+
--viewport <WxH> Page size of the daemon's browser
|
|
748
752
|
--timeout <min> Idle timeout in minutes (default: 30)
|
|
749
753
|
daemon stop Stop the running daemon
|
|
750
754
|
daemon status Check if daemon is running
|
|
@@ -796,7 +800,9 @@ STORAGE & CLEANUP
|
|
|
796
800
|
|
|
797
801
|
OPTIONS
|
|
798
802
|
--browser <type> Browser: chromium, firefox, webkit (default: chromium)
|
|
799
|
-
--device <name> Device preset: iphone-
|
|
803
|
+
--device <name> Device preset: iphone-16, pixel-9, ipad-pro-13, ... ('device list')
|
|
804
|
+
--viewport <WxH> Page size, e.g. 390x844; with --device it sets the size and
|
|
805
|
+
the device keeps its pixel ratio, touch and user agent
|
|
800
806
|
--geo <location> Location preset or lat,lon coordinates
|
|
801
807
|
--locale <locale> Browser locale (e.g., en-US, fr-FR)
|
|
802
808
|
--timezone <tz> Timezone (e.g., America/New_York)
|
|
@@ -975,10 +981,17 @@ const DRIVES_THE_PAGE = [
|
|
|
975
981
|
"keyboard", "press", "type", "evaluate", "eval", "drag", "upload",
|
|
976
982
|
"smart-click", "dismiss-overlay", "assert",
|
|
977
983
|
];
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
984
|
+
/**
|
|
985
|
+
* Flags the shared CBrowser construction consumes, for every command that uses
|
|
986
|
+
* it. One list, spread into each command's set below: when this list was copied
|
|
987
|
+
* into each set, --viewport (2026-10-21) reached some commands and was refused
|
|
988
|
+
* by evaluate and keyboard.
|
|
989
|
+
*/
|
|
990
|
+
const SHARED_BROWSER_FLAGS = [
|
|
991
|
+
"url", "browser", "device", "viewport", "headless", "persistent", "restore",
|
|
992
|
+
"locale", "timezone", "geo", "verbose",
|
|
981
993
|
];
|
|
994
|
+
const GLOBAL_FLAGS = [...SHARED_BROWSER_FLAGS, "help", "no-color", "quiet", "json"];
|
|
982
995
|
/**
|
|
983
996
|
* Declared options per command.
|
|
984
997
|
*
|
|
@@ -1146,29 +1159,24 @@ const CAPTURE_FLAGS = new Set([
|
|
|
1146
1159
|
"contact-sheet", "attention-overlay", "overlay-persona", "overlay-goal",
|
|
1147
1160
|
"after", "after-element", "after-delay",
|
|
1148
1161
|
"until-element", "until-element-gone", "until-idle", "timeout",
|
|
1149
|
-
|
|
1150
|
-
"url", "browser", "device", "headless", "persistent", "restore",
|
|
1151
|
-
"locale", "timezone", "geo", "verbose",
|
|
1162
|
+
...SHARED_BROWSER_FLAGS,
|
|
1152
1163
|
]);
|
|
1153
1164
|
/**
|
|
1154
1165
|
* Resolve `--device` to a real preset key, or exit naming the bad value.
|
|
1155
1166
|
*
|
|
1156
|
-
*
|
|
1157
|
-
*
|
|
1158
|
-
*
|
|
1159
|
-
*
|
|
1167
|
+
* `capture start` checks here, before it builds anything. Every other command
|
|
1168
|
+
* gets the same check from CBrowser.launch() (src/devices.ts resolveDevice);
|
|
1169
|
+
* before 2026-10-21 the browser skipped names it did not know and rendered
|
|
1170
|
+
* 1280x800 desktop. Case and spaces do not matter: "iPhone 15" is "iphone-15".
|
|
1160
1171
|
*/
|
|
1161
1172
|
function resolveDevicePreset(value) {
|
|
1162
|
-
const
|
|
1163
|
-
if (
|
|
1164
|
-
return
|
|
1165
|
-
const known = Object.keys(DEVICE_PRESETS);
|
|
1166
|
-
const stem = normalised.replace(/-?\d+.*$/, "");
|
|
1167
|
-
const close = known.filter((k) => k.startsWith(stem) && stem.length > 0);
|
|
1173
|
+
const resolved = resolveDevice(value);
|
|
1174
|
+
if ("key" in resolved)
|
|
1175
|
+
return resolved.key;
|
|
1168
1176
|
console.error(`Error: unknown --device "${value}"`);
|
|
1169
|
-
if (
|
|
1170
|
-
console.error(` Did you mean: ${
|
|
1171
|
-
console.error(`
|
|
1177
|
+
if (resolved.suggestions.length > 0)
|
|
1178
|
+
console.error(` Did you mean: ${resolved.suggestions.join(", ")}?`);
|
|
1179
|
+
console.error(` 'cbrowser device list' shows all ${Object.keys(DEVICE_PRESETS).length} presets; --viewport WIDTHxHEIGHT sets any size.`);
|
|
1172
1180
|
process.exit(1);
|
|
1173
1181
|
}
|
|
1174
1182
|
/** Reject any flag `capture start` cannot honour, naming it. */
|
|
@@ -1752,8 +1760,7 @@ function reportAutoCapture(capture) {
|
|
|
1752
1760
|
/** Flags `evaluate` understands; anything else is rejected rather than ignored. */
|
|
1753
1761
|
const EVALUATE_FLAGS = new Set([
|
|
1754
1762
|
"file", "arg", "json", "raw", "wait-for", "timeout", "expect-truthy", "force",
|
|
1755
|
-
|
|
1756
|
-
"locale", "timezone", "geo", "verbose",
|
|
1763
|
+
...SHARED_BROWSER_FLAGS,
|
|
1757
1764
|
]);
|
|
1758
1765
|
/**
|
|
1759
1766
|
* `--force` is a bare flag, but the parser gives any flag the next non-flag
|
|
@@ -1786,53 +1793,6 @@ function repeatedFlag(name) {
|
|
|
1786
1793
|
}
|
|
1787
1794
|
return values;
|
|
1788
1795
|
}
|
|
1789
|
-
/**
|
|
1790
|
-
* Evaluate a script in the page, accepting all three shapes people write.
|
|
1791
|
-
*
|
|
1792
|
-
* `--file` exists because multi-line scripts die in shell quoting, so it has to
|
|
1793
|
-
* accept real scripts - `const`, `let`, an explicit `return` - not only a
|
|
1794
|
-
* single expression. The source is compiled inside the page as a function
|
|
1795
|
-
* body: a bare expression gets an implicit return, a statement body keeps its
|
|
1796
|
-
* own, and a function/arrow literal is called with the arguments.
|
|
1797
|
-
*/
|
|
1798
|
-
async function evaluateScript(page, script, args) {
|
|
1799
|
-
const source = script.trim();
|
|
1800
|
-
// A function or arrow literal is called with the arguments directly.
|
|
1801
|
-
const isCallable = /^(async\s+)?function\b/.test(source) || /=>/.test(source);
|
|
1802
|
-
return page.evaluate((payload) => {
|
|
1803
|
-
if (payload.callable) {
|
|
1804
|
-
const fn = (0, eval)(`(${payload.src})`);
|
|
1805
|
-
return fn(...payload.a);
|
|
1806
|
-
}
|
|
1807
|
-
const compile = (src) => {
|
|
1808
|
-
// 1. A bare expression: implicit return.
|
|
1809
|
-
try {
|
|
1810
|
-
return new Function("args", `return (${src});`);
|
|
1811
|
-
}
|
|
1812
|
-
catch { /* not an expression */ }
|
|
1813
|
-
// 2. Statements with no explicit return: return the final expression,
|
|
1814
|
-
// which is what a REPL and the old one-arg form both did.
|
|
1815
|
-
if (!/\breturn\b/.test(src)) {
|
|
1816
|
-
const trimmed = src.replace(/;\s*$/, "");
|
|
1817
|
-
const split = Math.max(trimmed.lastIndexOf(";"), trimmed.lastIndexOf("\n"));
|
|
1818
|
-
if (split > -1) {
|
|
1819
|
-
const head = trimmed.slice(0, split + 1);
|
|
1820
|
-
const tail = trimmed.slice(split + 1).trim();
|
|
1821
|
-
if (tail) {
|
|
1822
|
-
try {
|
|
1823
|
-
return new Function("args", `${head}\nreturn (${tail});`);
|
|
1824
|
-
}
|
|
1825
|
-
catch { /* the tail is not an expression either */ }
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
}
|
|
1829
|
-
// 3. Statements as written. Any syntax error surfaces from here, which
|
|
1830
|
-
// is the error the user should actually see.
|
|
1831
|
-
return new Function("args", src);
|
|
1832
|
-
};
|
|
1833
|
-
return compile(payload.src)(payload.a);
|
|
1834
|
-
}, { src: source, a: args, callable: isCallable });
|
|
1835
|
-
}
|
|
1836
1796
|
/**
|
|
1837
1797
|
* Run JavaScript in the page.
|
|
1838
1798
|
*
|
|
@@ -2072,8 +2032,7 @@ function parseKeyboardToken(token, literal = false) {
|
|
|
2072
2032
|
/** Flags `keyboard` understands. */
|
|
2073
2033
|
const KEYBOARD_FLAGS = new Set([
|
|
2074
2034
|
"delay", "selector", "hold", "repeat", "text", "force",
|
|
2075
|
-
|
|
2076
|
-
"locale", "timezone", "geo", "verbose",
|
|
2035
|
+
...SHARED_BROWSER_FLAGS,
|
|
2077
2036
|
]);
|
|
2078
2037
|
/**
|
|
2079
2038
|
* Drive a keyboard sequence.
|
|
@@ -2868,9 +2827,28 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
2868
2827
|
const port = parseInt(options.port) || 9222;
|
|
2869
2828
|
switch (subCommand) {
|
|
2870
2829
|
case "start": {
|
|
2830
|
+
// The daemon's browser takes its device and size from here. Both are
|
|
2831
|
+
// checked now: a bad value inside the detached daemon would only show
|
|
2832
|
+
// up as a daemon that failed to start.
|
|
2833
|
+
const launchArgs = [];
|
|
2834
|
+
if (typeof options.device === "string")
|
|
2835
|
+
launchArgs.push("--device", resolveDevicePreset(options.device));
|
|
2836
|
+
if (typeof options.viewport === "string") {
|
|
2837
|
+
try {
|
|
2838
|
+
parseViewportList(options.viewport);
|
|
2839
|
+
}
|
|
2840
|
+
catch (e) {
|
|
2841
|
+
console.error(`Error: ${e.message}`);
|
|
2842
|
+
process.exit(1);
|
|
2843
|
+
}
|
|
2844
|
+
launchArgs.push("--viewport", options.viewport);
|
|
2845
|
+
}
|
|
2871
2846
|
console.log("š Starting CBrowser daemon...");
|
|
2872
|
-
const result = await startDaemon(port);
|
|
2847
|
+
const result = await startDaemon(port, launchArgs);
|
|
2873
2848
|
console.log(result.success ? `ā ${result.message}` : `ā ${result.message}`);
|
|
2849
|
+
if (result.alreadyRunning && launchArgs.length > 0) {
|
|
2850
|
+
console.error("ā --device and --viewport were not applied: the daemon was already running. Run cbrowser daemon stop first.");
|
|
2851
|
+
}
|
|
2874
2852
|
process.exit(result.success ? 0 : 1);
|
|
2875
2853
|
break;
|
|
2876
2854
|
}
|
|
@@ -2894,9 +2872,12 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
2894
2872
|
: options.browser === "webkit" ? "webkit"
|
|
2895
2873
|
: "chromium";
|
|
2896
2874
|
// runDaemonServer will merge with defaults internally
|
|
2875
|
+
const runViewport = typeof options.viewport === "string" ? parseViewportList(options.viewport)[0] : undefined;
|
|
2897
2876
|
await runDaemonServer({
|
|
2898
2877
|
browser: browserType,
|
|
2899
2878
|
headless: options.headless !== false && options.headless !== "false",
|
|
2879
|
+
...(typeof options.device === "string" ? { device: options.device } : {}),
|
|
2880
|
+
...(runViewport ? { viewportWidth: runViewport.width, viewportHeight: runViewport.height, viewportOverride: runViewport } : {}),
|
|
2900
2881
|
}, port);
|
|
2901
2882
|
return;
|
|
2902
2883
|
}
|
|
@@ -2919,6 +2900,12 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
2919
2900
|
];
|
|
2920
2901
|
if (daemonRunning && INTERACTION_COMMANDS.includes(command)) {
|
|
2921
2902
|
console.log("š Connected to running daemon");
|
|
2903
|
+
// The daemon's browser keeps the device and size it started with; a flag on
|
|
2904
|
+
// a command it runs cannot change them, so say so instead of ignoring it.
|
|
2905
|
+
if (typeof options.device === "string" || typeof options.viewport === "string") {
|
|
2906
|
+
console.error("ā --device and --viewport do not apply to commands the daemon runs: its browser keeps the device and size it started with.");
|
|
2907
|
+
console.error(" To change them: cbrowser daemon stop, then cbrowser daemon start --device <name> --viewport <WxH>");
|
|
2908
|
+
}
|
|
2922
2909
|
const daemonCommand = command;
|
|
2923
2910
|
let daemonArgs = {};
|
|
2924
2911
|
switch (command) {
|
|
@@ -2989,7 +2976,11 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
2989
2976
|
console.error(" Re-run with --force to run it anyway.");
|
|
2990
2977
|
process.exit(1);
|
|
2991
2978
|
}
|
|
2992
|
-
|
|
2979
|
+
// The raw script: the daemon compiles it with the same evaluateScript as the
|
|
2980
|
+
// local path. This hand-built body treated a "return" anywhere - even inside
|
|
2981
|
+
// a nested function - as statements, and an IIFE's value was dropped.
|
|
2982
|
+
// (2026-10-13)
|
|
2983
|
+
daemonArgs = { script, args: [], force: true };
|
|
2993
2984
|
break;
|
|
2994
2985
|
}
|
|
2995
2986
|
case "screenshot": {
|
|
@@ -3030,7 +3021,9 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
3030
3021
|
const result = await sendToDaemon(daemonCommand, daemonArgs);
|
|
3031
3022
|
if (result.success) {
|
|
3032
3023
|
console.log("ā Command executed via daemon");
|
|
3033
|
-
|
|
3024
|
+
// evaluate's value is the answer, so 0, false and "" print too; for other
|
|
3025
|
+
// commands an empty result means nothing to show. (2026-10-13)
|
|
3026
|
+
if (daemonCommand === "evaluate" ? result.result !== undefined : result.result) {
|
|
3034
3027
|
console.log(JSON.stringify(result.result, null, 2));
|
|
3035
3028
|
}
|
|
3036
3029
|
}
|
|
@@ -3096,7 +3089,7 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
3096
3089
|
browser: browserType,
|
|
3097
3090
|
headless,
|
|
3098
3091
|
device: options.device,
|
|
3099
|
-
...(viewportOverride ? { viewportWidth: viewportOverride.width, viewportHeight: viewportOverride.height } : {}),
|
|
3092
|
+
...(viewportOverride ? { viewportWidth: viewportOverride.width, viewportHeight: viewportOverride.height, viewportOverride } : {}),
|
|
3100
3093
|
geolocation,
|
|
3101
3094
|
locale: options.locale,
|
|
3102
3095
|
timezone: options.timezone,
|
|
@@ -4241,14 +4234,18 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
4241
4234
|
const subcommand = args[0];
|
|
4242
4235
|
switch (subcommand) {
|
|
4243
4236
|
case "list": {
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4237
|
+
// One line per preset: there are a few hundred now. An optional
|
|
4238
|
+
// filter matches the key or the display name.
|
|
4239
|
+
const filter = (args[1] ?? "").toLowerCase();
|
|
4240
|
+
const rows = Object.entries(DEVICE_PRESETS).filter(([key, d]) => !filter || key.includes(filter) || d.name.toLowerCase().includes(filter));
|
|
4241
|
+
const width = Math.max(...rows.map(([key]) => key.length), 4);
|
|
4242
|
+
console.log(`\nš± Device presets${filter ? ` matching "${filter}"` : ""}: ${rows.length}\n`);
|
|
4243
|
+
for (const [key, d] of rows) {
|
|
4244
|
+
const kind = d.isMobile ? "mobile" : "desktop";
|
|
4245
|
+
const touch = d.hasTouch ? ", touch" : "";
|
|
4246
|
+
console.log(` ${key.padEnd(width)} ${d.viewport.width}x${d.viewport.height} @${d.deviceScaleFactor}x ${kind}${touch} (${d.name})`);
|
|
4247
|
+
}
|
|
4248
|
+
console.log("\n Case and spaces do not matter: --device \"iPhone 16\" is iphone-16.");
|
|
4252
4249
|
break;
|
|
4253
4250
|
}
|
|
4254
4251
|
case "set": {
|
|
@@ -4257,15 +4254,11 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
4257
4254
|
console.error("Usage: cbrowser device set <name>");
|
|
4258
4255
|
process.exit(1);
|
|
4259
4256
|
}
|
|
4260
|
-
|
|
4261
|
-
console.error(`Unknown device: ${deviceName}`);
|
|
4262
|
-
console.error("Run 'cbrowser device list' to see available devices");
|
|
4263
|
-
process.exit(1);
|
|
4264
|
-
}
|
|
4257
|
+
const presetKey = resolveDevicePreset(deviceName);
|
|
4265
4258
|
// Save device setting to persistent session state
|
|
4266
4259
|
const deviceBrowser = new CBrowser({ persistent: true, headless: true, verbose: false });
|
|
4267
|
-
deviceBrowser.saveDeviceSetting(
|
|
4268
|
-
console.log(`ā Device set: ${
|
|
4260
|
+
deviceBrowser.saveDeviceSetting(presetKey);
|
|
4261
|
+
console.log(`ā Device set: ${presetKey}`);
|
|
4269
4262
|
console.log(" Device emulation will apply to all subsequent commands");
|
|
4270
4263
|
break;
|
|
4271
4264
|
}
|
|
@@ -5448,9 +5441,10 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
5448
5441
|
console.error("Usage: cbrowser parallel devices <url> [--devices <list>] [--concurrency <n>]");
|
|
5449
5442
|
process.exit(1);
|
|
5450
5443
|
}
|
|
5444
|
+
// Without --devices, cbrowser's own presets, not all few hundred.
|
|
5451
5445
|
const deviceList = options.devices
|
|
5452
|
-
? options.devices.split(",")
|
|
5453
|
-
:
|
|
5446
|
+
? options.devices.split(",").map((d) => resolveDevicePreset(d))
|
|
5447
|
+
: [...CBROWSER_DEVICE_KEYS];
|
|
5454
5448
|
const concurrency = options.concurrency ? parseInt(options.concurrency) : 3;
|
|
5455
5449
|
console.log(`\nš Running parallel device tests...`);
|
|
5456
5450
|
console.log(` URL: ${url}`);
|
|
@@ -5912,7 +5906,11 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
5912
5906
|
});
|
|
5913
5907
|
console.log(`\nš Step-through mode enabled. Press Enter to execute each step.\n`);
|
|
5914
5908
|
const { CBrowser } = await import("./browser.js");
|
|
5915
|
-
const stepBrowser = new CBrowser({
|
|
5909
|
+
const stepBrowser = new CBrowser({
|
|
5910
|
+
headless,
|
|
5911
|
+
...(typeof options.device === "string" ? { device: options.device } : {}),
|
|
5912
|
+
...(viewportOverride ? { viewportWidth: viewportOverride.width, viewportHeight: viewportOverride.height, viewportOverride } : {}),
|
|
5913
|
+
});
|
|
5916
5914
|
await stepBrowser.launch();
|
|
5917
5915
|
try {
|
|
5918
5916
|
const totalSteps = suite.tests.reduce((sum, t) => sum + t.steps.length, 0);
|
|
@@ -5993,6 +5991,8 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
5993
5991
|
continueOnFailure: options["continue-on-failure"] === true,
|
|
5994
5992
|
screenshotOnFailure: options["screenshot-on-failure"] !== false,
|
|
5995
5993
|
headless,
|
|
5994
|
+
device: typeof options.device === "string" ? options.device : undefined,
|
|
5995
|
+
viewport: viewportOverride,
|
|
5996
5996
|
fuzzyMatch: options["fuzzy-match"] === true,
|
|
5997
5997
|
capture: buildAutoCaptureSetting(options, `test-suite-${suite.name.replace(/[^a-zA-Z0-9-_]+/g, "-").toLowerCase()}-${Date.now()}`),
|
|
5998
5998
|
};
|
|
@@ -6567,6 +6567,8 @@ Documentation: https://github.com/alexandriashai/cbrowser/wiki
|
|
|
6567
6567
|
maxTime,
|
|
6568
6568
|
verbose,
|
|
6569
6569
|
headless,
|
|
6570
|
+
device: typeof options.device === "string" ? options.device : undefined,
|
|
6571
|
+
viewport: viewportOverride,
|
|
6570
6572
|
vision,
|
|
6571
6573
|
customTraits, // Pass custom traits from env var
|
|
6572
6574
|
location: locationOverride, // Pass location override
|