@xbrowser/cli 1.2.2 → 1.3.1

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.
@@ -20,8 +20,8 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-SLQR57XZ.js";
24
- import "./chunk-QFROODUU.js";
23
+ } from "./chunk-7OGKQWXE.js";
24
+ import "./chunk-N4PIGZDG.js";
25
25
  import "./chunk-TNEN6VQ2.js";
26
26
  import "./chunk-GDKLH7ZY.js";
27
27
  import "./chunk-KFQGP6VL.js";
@@ -20,7 +20,7 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-MNFOCOL6.js";
23
+ } from "./chunk-TLOHV3FP.js";
24
24
  import "./chunk-TNEN6VQ2.js";
25
25
  import "./chunk-GDKLH7ZY.js";
26
26
  import "./chunk-KFQGP6VL.js";
@@ -20,8 +20,8 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-6V57JME6.js";
24
- import "./chunk-QFROODUU.js";
23
+ } from "./chunk-UZQYMXR5.js";
24
+ import "./chunk-N4PIGZDG.js";
25
25
  import "./chunk-TNEN6VQ2.js";
26
26
  import "./chunk-GDKLH7ZY.js";
27
27
  import "./chunk-ABXMBNQ6.js";
@@ -14,7 +14,7 @@ import {
14
14
  scrollIntoView,
15
15
  waitForActionable,
16
16
  waitForNetworkIdle
17
- } from "./chunk-QFROODUU.js";
17
+ } from "./chunk-N4PIGZDG.js";
18
18
  import {
19
19
  connectToCDP,
20
20
  findChrome,
@@ -1358,7 +1358,26 @@ Last error: ${lastError.message}` : "";
1358
1358
  return this.locator(`xpath=//*[contains(text(),'${escaped}')]`);
1359
1359
  }
1360
1360
  getByRole(role, opts) {
1361
- let sel = `[role="${role}"]`;
1361
+ const ROLE_TO_TAGS = {
1362
+ button: ["button"],
1363
+ link: ["a[href]"],
1364
+ heading: ["h1", "h2", "h3", "h4", "h5", "h6"],
1365
+ textbox: ['input[type="text"]', "input:not([type])", "textarea"],
1366
+ checkbox: ['input[type="checkbox"]'],
1367
+ radio: ['input[type="radio"]'],
1368
+ searchbox: ['input[type="search"]'],
1369
+ combobox: ["select"],
1370
+ img: ["img"],
1371
+ navigation: ["nav"],
1372
+ article: ["article"],
1373
+ banner: ["header"],
1374
+ contentinfo: ["footer"],
1375
+ main: ["main"],
1376
+ complementary: ["aside"]
1377
+ };
1378
+ const tags = ROLE_TO_TAGS[role] || [];
1379
+ const tagSel = tags.length > 0 ? tags.join(",") + "," : "";
1380
+ let sel = `${tagSel}[role="${role}"]`;
1362
1381
  if (opts?.name) {
1363
1382
  sel += opts.exact ? `[aria-label="${opts.name}"]` : `[aria-label*="${opts.name}"]`;
1364
1383
  }
@@ -1601,6 +1620,35 @@ Last error: ${lastError.message}` : "";
1601
1620
  frames() {
1602
1621
  return [this.mainFrame()];
1603
1622
  }
1623
+ /**
1624
+ * Discover all frames (main + iframes) via CDP Page.getFrameTree.
1625
+ * This is async because CDP doesn't maintain a frame list client-side.
1626
+ */
1627
+ async discoverFrames() {
1628
+ try {
1629
+ const result = await this.conn.send("Page.getFrameTree", void 0, this.sessionId);
1630
+ const frames = [];
1631
+ const collect = (node) => {
1632
+ frames.push({
1633
+ name: () => node.frame.name || "",
1634
+ url: () => node.frame.url,
1635
+ // Helper for frame.ts to pick by index
1636
+ childFrames: () => (node.childFrames || []).map((c) => ({
1637
+ name: () => c.frame.name || "",
1638
+ url: () => c.frame.url,
1639
+ childFrames: () => []
1640
+ }))
1641
+ });
1642
+ for (const child of node.childFrames || []) {
1643
+ collect({ frame: child.frame, childFrames: [] });
1644
+ }
1645
+ };
1646
+ collect(result.frameTree);
1647
+ return frames;
1648
+ } catch {
1649
+ return [this.mainFrame()];
1650
+ }
1651
+ }
1604
1652
  // ── CDP helpers exposed for locator/element ─────────────────
1605
1653
  /** Query a single element, returns CDP nodeId or 0 if not found */
1606
1654
  async querySelector(selector) {
@@ -399,8 +399,10 @@ var XBrowserPluginLoader = class {
399
399
  const instance = await this.loadPlugin(indexPath, entry.name);
400
400
  loaded.push(instance);
401
401
  } catch (err) {
402
- if (process.env.XBROWSER_DEBUG) {
403
- console.warn(`\u26A0\uFE0F Plugin "${entry.name}" load failed: ${err instanceof Error ? err.message : String(err)}`);
402
+ const errMsg = err instanceof Error ? err.message : String(err);
403
+ console.warn(`\u26A0\uFE0F Plugin "${entry.name}" load failed: ${errMsg}`);
404
+ if (errMsg.includes("Cannot find module") && errMsg.includes("shared/")) {
405
+ console.warn(` \u{1F4A1} This plugin needs shared/ dependencies. Try: xbrowser plugin install shared`);
404
406
  }
405
407
  }
406
408
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  launch
3
- } from "./chunk-QFROODUU.js";
3
+ } from "./chunk-N4PIGZDG.js";
4
4
  import {
5
5
  errMsg
6
6
  } from "./chunk-GDKLH7ZY.js";
@@ -24,9 +24,9 @@ var DEFAULT_EXCLUDE_TYPES = [
24
24
  function filterRecording(inputPath, outputPath, excludeTypes) {
25
25
  const content = fs.readFileSync(inputPath, "utf-8");
26
26
  const recording = yaml.parse(content);
27
- const exclude = excludeTypes || DEFAULT_EXCLUDE_TYPES;
28
- const events = recording.events || [];
27
+ const events = recording.actions || recording.events || [];
29
28
  const originalCount = events.length;
29
+ const exclude = excludeTypes || DEFAULT_EXCLUDE_TYPES;
30
30
  const filteredEvents = events.filter((event) => {
31
31
  return !exclude.includes(event.type);
32
32
  });
@@ -4,9 +4,10 @@ import * as yaml from "yaml";
4
4
  function extractRecording(filePath) {
5
5
  const content = fs.readFileSync(filePath, "utf-8");
6
6
  const recording = yaml.parse(content);
7
+ const events = recording.actions || recording.events || [];
7
8
  const keyEvents = [];
8
9
  const eventTypes = {};
9
- for (const event of recording.events || []) {
10
+ for (const event of events) {
10
11
  const type = event.type;
11
12
  eventTypes[type] = (eventTypes[type] || 0) + 1;
12
13
  if (["click", "input", "type", "keydown", "keypress", "hover", "hover_enter", "hover_leave"].includes(type)) {
@@ -25,7 +26,7 @@ function extractRecording(filePath) {
25
26
  }
26
27
  return {
27
28
  startUrl: recording.startUrl,
28
- totalEvents: (recording.events || []).length,
29
+ totalEvents: events.length,
29
30
  keyEventsCount: keyEvents.length,
30
31
  eventTypes,
31
32
  operations: keyEvents.map((e, i) => ({
@@ -1357,7 +1357,26 @@ Last error: ${lastError.message}` : "";
1357
1357
  return this.locator(`xpath=//*[contains(text(),'${escaped}')]`);
1358
1358
  }
1359
1359
  getByRole(role, opts) {
1360
- let sel = `[role="${role}"]`;
1360
+ const ROLE_TO_TAGS = {
1361
+ button: ["button"],
1362
+ link: ["a[href]"],
1363
+ heading: ["h1", "h2", "h3", "h4", "h5", "h6"],
1364
+ textbox: ['input[type="text"]', "input:not([type])", "textarea"],
1365
+ checkbox: ['input[type="checkbox"]'],
1366
+ radio: ['input[type="radio"]'],
1367
+ searchbox: ['input[type="search"]'],
1368
+ combobox: ["select"],
1369
+ img: ["img"],
1370
+ navigation: ["nav"],
1371
+ article: ["article"],
1372
+ banner: ["header"],
1373
+ contentinfo: ["footer"],
1374
+ main: ["main"],
1375
+ complementary: ["aside"]
1376
+ };
1377
+ const tags = ROLE_TO_TAGS[role] || [];
1378
+ const tagSel = tags.length > 0 ? tags.join(",") + "," : "";
1379
+ let sel = `${tagSel}[role="${role}"]`;
1361
1380
  if (opts?.name) {
1362
1381
  sel += opts.exact ? `[aria-label="${opts.name}"]` : `[aria-label*="${opts.name}"]`;
1363
1382
  }
@@ -1600,6 +1619,35 @@ Last error: ${lastError.message}` : "";
1600
1619
  frames() {
1601
1620
  return [this.mainFrame()];
1602
1621
  }
1622
+ /**
1623
+ * Discover all frames (main + iframes) via CDP Page.getFrameTree.
1624
+ * This is async because CDP doesn't maintain a frame list client-side.
1625
+ */
1626
+ async discoverFrames() {
1627
+ try {
1628
+ const result = await this.conn.send("Page.getFrameTree", void 0, this.sessionId);
1629
+ const frames = [];
1630
+ const collect = (node) => {
1631
+ frames.push({
1632
+ name: () => node.frame.name || "",
1633
+ url: () => node.frame.url,
1634
+ // Helper for frame.ts to pick by index
1635
+ childFrames: () => (node.childFrames || []).map((c) => ({
1636
+ name: () => c.frame.name || "",
1637
+ url: () => c.frame.url,
1638
+ childFrames: () => []
1639
+ }))
1640
+ });
1641
+ for (const child of node.childFrames || []) {
1642
+ collect({ frame: child.frame, childFrames: [] });
1643
+ }
1644
+ };
1645
+ collect(result.frameTree);
1646
+ return frames;
1647
+ } catch {
1648
+ return [this.mainFrame()];
1649
+ }
1650
+ }
1603
1651
  // ── CDP helpers exposed for locator/element ─────────────────
1604
1652
  /** Query a single element, returns CDP nodeId or 0 if not found */
1605
1653
  async querySelector(selector) {
@@ -1363,7 +1363,26 @@ Last error: ${lastError.message}` : "";
1363
1363
  return this.locator(`xpath=//*[contains(text(),'${escaped}')]`);
1364
1364
  }
1365
1365
  getByRole(role, opts) {
1366
- let sel = `[role="${role}"]`;
1366
+ const ROLE_TO_TAGS = {
1367
+ button: ["button"],
1368
+ link: ["a[href]"],
1369
+ heading: ["h1", "h2", "h3", "h4", "h5", "h6"],
1370
+ textbox: ['input[type="text"]', "input:not([type])", "textarea"],
1371
+ checkbox: ['input[type="checkbox"]'],
1372
+ radio: ['input[type="radio"]'],
1373
+ searchbox: ['input[type="search"]'],
1374
+ combobox: ["select"],
1375
+ img: ["img"],
1376
+ navigation: ["nav"],
1377
+ article: ["article"],
1378
+ banner: ["header"],
1379
+ contentinfo: ["footer"],
1380
+ main: ["main"],
1381
+ complementary: ["aside"]
1382
+ };
1383
+ const tags = ROLE_TO_TAGS[role] || [];
1384
+ const tagSel = tags.length > 0 ? tags.join(",") + "," : "";
1385
+ let sel = `${tagSel}[role="${role}"]`;
1367
1386
  if (opts?.name) {
1368
1387
  sel += opts.exact ? `[aria-label="${opts.name}"]` : `[aria-label*="${opts.name}"]`;
1369
1388
  }
@@ -1606,6 +1625,35 @@ Last error: ${lastError.message}` : "";
1606
1625
  frames() {
1607
1626
  return [this.mainFrame()];
1608
1627
  }
1628
+ /**
1629
+ * Discover all frames (main + iframes) via CDP Page.getFrameTree.
1630
+ * This is async because CDP doesn't maintain a frame list client-side.
1631
+ */
1632
+ async discoverFrames() {
1633
+ try {
1634
+ const result = await this.conn.send("Page.getFrameTree", void 0, this.sessionId);
1635
+ const frames = [];
1636
+ const collect = (node) => {
1637
+ frames.push({
1638
+ name: () => node.frame.name || "",
1639
+ url: () => node.frame.url,
1640
+ // Helper for frame.ts to pick by index
1641
+ childFrames: () => (node.childFrames || []).map((c) => ({
1642
+ name: () => c.frame.name || "",
1643
+ url: () => c.frame.url,
1644
+ childFrames: () => []
1645
+ }))
1646
+ });
1647
+ for (const child of node.childFrames || []) {
1648
+ collect({ frame: child.frame, childFrames: [] });
1649
+ }
1650
+ };
1651
+ collect(result.frameTree);
1652
+ return frames;
1653
+ } catch {
1654
+ return [this.mainFrame()];
1655
+ }
1656
+ }
1609
1657
  // ── CDP helpers exposed for locator/element ─────────────────
1610
1658
  /** Query a single element, returns CDP nodeId or 0 if not found */
1611
1659
  async querySelector(selector) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  launch
3
- } from "./chunk-QFROODUU.js";
3
+ } from "./chunk-N4PIGZDG.js";
4
4
  import {
5
5
  errMsg
6
6
  } from "./chunk-GDKLH7ZY.js";