@wdio/nightwatch-devtools 1.5.0 → 1.5.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.
package/dist/index.js CHANGED
@@ -554,10 +554,45 @@ function isSameElement(node1, node2) {
554
554
  }
555
555
  return false;
556
556
  }
557
+ function sanitizePageSource(sourceXML) {
558
+ let sanitized = sourceXML.replace(
559
+ /&(?!(?:amp|lt|gt|quot|apos|#\d+|#x[0-9a-fA-F]+);)/g,
560
+ "&"
561
+ );
562
+ const VOID_ELEMENTS = [
563
+ "link",
564
+ "meta",
565
+ "br",
566
+ "hr",
567
+ "img",
568
+ "input",
569
+ "source",
570
+ "area",
571
+ "base",
572
+ "col",
573
+ "embed",
574
+ "track",
575
+ "wbr"
576
+ ];
577
+ for (const tag of VOID_ELEMENTS) {
578
+ sanitized = sanitized.replace(
579
+ // eslint-disable-next-line security/detect-non-literal-regexp -- tag from hardcoded list
580
+ new RegExp(`<${tag}(\\s[^>]*?[^/])>`, "gi"),
581
+ `<${tag}$1/>`
582
+ );
583
+ sanitized = sanitized.replace(
584
+ // eslint-disable-next-line security/detect-non-literal-regexp -- tag from hardcoded list
585
+ new RegExp(`<${tag}>`, "gi"),
586
+ `<${tag}/>`
587
+ );
588
+ }
589
+ return sanitized;
590
+ }
557
591
  function xmlToJSON(sourceXML) {
558
592
  try {
593
+ const sanitized = sanitizePageSource(sourceXML);
559
594
  const parser = new DOMParser();
560
- const sourceDoc = parser.parseFromString(sourceXML, "text/xml");
595
+ const sourceDoc = parser.parseFromString(sanitized, "text/xml");
561
596
  const parseErrors = sourceDoc.getElementsByTagName("parsererror");
562
597
  if (parseErrors.length > 0) {
563
598
  console.error(
@@ -576,8 +611,9 @@ function xmlToJSON(sourceXML) {
576
611
  }
577
612
  function xmlToDOM(sourceXML) {
578
613
  try {
614
+ const sanitized = sanitizePageSource(sourceXML);
579
615
  const parser = new DOMParser();
580
- const doc = parser.parseFromString(sourceXML, "text/xml");
616
+ const doc = parser.parseFromString(sanitized, "text/xml");
581
617
  const parseErrors = doc.getElementsByTagName("parsererror");
582
618
  if (parseErrors.length > 0) {
583
619
  console.error("[xmlToDOM] XML parsing error:", parseErrors[0].textContent);
@@ -834,7 +870,7 @@ function isValidValue(value) {
834
870
  return value !== void 0 && value !== null && value !== "null" && value.trim() !== "";
835
871
  }
836
872
  function escapeText(text) {
837
- return text.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
873
+ return text.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
838
874
  }
839
875
  function escapeXPathValue(value) {
840
876
  if (!value.includes("'")) {
@@ -1395,6 +1431,20 @@ function classifyMobileRole(tagName, platform) {
1395
1431
  }
1396
1432
  return IOS_ROLE_MAP[tagName] || simplifyTag(tagName);
1397
1433
  }
1434
+ function getFirstChildText(element) {
1435
+ const queue = [...element.children || []];
1436
+ while (queue.length > 0) {
1437
+ const el = queue.shift();
1438
+ const text = el.attributes?.text?.trim();
1439
+ if (text) {
1440
+ return text;
1441
+ }
1442
+ if (el.children) {
1443
+ queue.push(...el.children);
1444
+ }
1445
+ }
1446
+ return void 0;
1447
+ }
1398
1448
  function getBestAndroidLocator(attrs) {
1399
1449
  if (attrs._selector) {
1400
1450
  return attrs._selector;
@@ -1480,7 +1530,7 @@ function isMobileInViewport(element, platform, viewport) {
1480
1530
  function collectMobileNodes(element, platform, depth, nodes, walkOpts) {
1481
1531
  const attrs = element.attributes;
1482
1532
  const role = classifyMobileRole(element.tagName, platform);
1483
- const name = getMobileNodeIdentity(attrs, platform);
1533
+ let name = getMobileNodeIdentity(attrs, platform);
1484
1534
  const explicit = isExplicitlyInteractive(attrs, platform);
1485
1535
  const interactive = isMobileInteractive(element, platform);
1486
1536
  const inViewport = isMobileInViewport(element, platform, walkOpts.viewport);
@@ -1525,6 +1575,12 @@ function collectMobileNodes(element, platform, depth, nodes, walkOpts) {
1525
1575
  if (!locator) {
1526
1576
  locator = (platform === "android" ? getBestAndroidLocator(attrs) : getBestIOSLocator(attrs)) ?? "";
1527
1577
  }
1578
+ if (!name && platform === "android" && (locator.startsWith("class:") || locator.startsWith('android=new UiSelector().className("'))) {
1579
+ const childText = getFirstChildText(element);
1580
+ if (childText && childText.length < 100) {
1581
+ name = childText;
1582
+ }
1583
+ }
1528
1584
  }
1529
1585
  nodes.push({
1530
1586
  role,