browsecraft 0.6.0 → 0.6.2

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.
@@ -519,7 +519,7 @@ async function locateElement(session, contextId, target, options) {
519
519
  description,
520
520
  async () => {
521
521
  const strategies = buildStrategies(opts);
522
- for (const { locator, strategy, isLabelLookup } of strategies) {
522
+ for (const { locator, strategy, isLabelLookup, caseInsensitiveName } of strategies) {
523
523
  try {
524
524
  const result = await session.browsingContext.locateNodes({
525
525
  context: contextId,
@@ -531,6 +531,19 @@ async function locateElement(session, contextId, target, options) {
531
531
  (n) => !HEAD_ONLY_ELEMENTS.has(n.value?.localName ?? "")
532
532
  );
533
533
  if (nodes.length > 0) {
534
+ if (caseInsensitiveName) {
535
+ const matched = await filterByCIAccessibleName(
536
+ session,
537
+ contextId,
538
+ nodes,
539
+ caseInsensitiveName,
540
+ opts.exact ?? false
541
+ );
542
+ if (matched) {
543
+ return { node: matched, strategy };
544
+ }
545
+ continue;
546
+ }
534
547
  if (isLabelLookup) {
535
548
  const resolved = await resolveLabelsToInputs(session, contextId, nodes);
536
549
  if (resolved) {
@@ -565,7 +578,7 @@ async function locateElement(session, contextId, target, options) {
565
578
  async function locateAllElements(session, contextId, target) {
566
579
  const opts = normalizeTarget(target);
567
580
  const strategies = buildStrategies(opts);
568
- for (const { locator, strategy } of strategies) {
581
+ for (const { locator, strategy, caseInsensitiveName } of strategies) {
569
582
  try {
570
583
  const result = await session.browsingContext.locateNodes({
571
584
  context: contextId,
@@ -574,6 +587,19 @@ async function locateAllElements(session, contextId, target) {
574
587
  });
575
588
  const nodes = result.nodes.filter((n) => !HEAD_ONLY_ELEMENTS.has(n.value?.localName ?? ""));
576
589
  if (nodes.length > 0) {
590
+ if (caseInsensitiveName) {
591
+ const matched = await filterByCIAccessibleName(
592
+ session,
593
+ contextId,
594
+ nodes,
595
+ caseInsensitiveName,
596
+ opts.exact ?? false
597
+ );
598
+ if (matched) {
599
+ return [{ node: matched, strategy }];
600
+ }
601
+ continue;
602
+ }
577
603
  return nodes.map((node) => ({ node, strategy }));
578
604
  }
579
605
  } catch {
@@ -638,6 +664,24 @@ function buildStrategies(opts) {
638
664
  });
639
665
  }
640
666
  }
667
+ if (!opts.exact) {
668
+ const ciSelector = [
669
+ "button",
670
+ "a",
671
+ '[role="button"]',
672
+ '[role="link"]',
673
+ '[role="menuitem"]',
674
+ '[role="tab"]',
675
+ 'input[type="submit"]',
676
+ 'input[type="button"]',
677
+ 'input[type="reset"]'
678
+ ].join(", ");
679
+ strategies.push({
680
+ locator: { type: "css", value: ciSelector },
681
+ strategy: `accessible-name-ci("${name}")`,
682
+ caseInsensitiveName: name
683
+ });
684
+ }
641
685
  strategies.push({
642
686
  locator: {
643
687
  type: "innerText",
@@ -730,6 +774,48 @@ async function resolveLabelsToInputs(session, contextId, nodes) {
730
774
  }
731
775
  return null;
732
776
  }
777
+ async function filterByCIAccessibleName(session, contextId, nodes, name, exact) {
778
+ const nodeArgs = nodes.filter((n) => n.sharedId);
779
+ if (nodeArgs.length === 0) return null;
780
+ try {
781
+ const result = await session.script.callFunction({
782
+ functionDeclaration: `function(name, exact, ...elements) {
783
+ const lower = name.toLowerCase();
784
+ for (const el of elements) {
785
+ if (!el) continue;
786
+ const candidates = [
787
+ el.getAttribute('aria-label') || '',
788
+ el.getAttribute('value') || '',
789
+ (el.innerText || el.textContent || '').trim(),
790
+ el.getAttribute('title') || '',
791
+ el.getAttribute('placeholder') || '',
792
+ ];
793
+ for (const c of candidates) {
794
+ if (!c) continue;
795
+ const cl = c.toLowerCase();
796
+ if (exact ? cl === lower : cl.includes(lower) || lower.includes(cl)) {
797
+ return el;
798
+ }
799
+ }
800
+ }
801
+ return null;
802
+ }`,
803
+ target: { context: contextId },
804
+ arguments: [
805
+ { type: "string", value: name },
806
+ { type: "boolean", value: exact },
807
+ ...nodeArgs.map((n) => ({ sharedId: n.sharedId, handle: n.handle }))
808
+ ],
809
+ awaitPromise: false,
810
+ resultOwnership: "root"
811
+ });
812
+ if (result.type === "success" && result.result?.type === "node" && result.result.sharedId) {
813
+ return result.result;
814
+ }
815
+ } catch {
816
+ }
817
+ return null;
818
+ }
733
819
  async function findSimilarElements(session, contextId, target) {
734
820
  const searchText = typeof target === "string" ? target : target.name ?? target.text ?? target.label ?? "";
735
821
  if (!searchText) return [];
@@ -2999,5 +3085,5 @@ exports.sleep = sleep;
2999
3085
  exports.test = test;
3000
3086
  exports.testRegistry = testRegistry;
3001
3087
  exports.waitFor = waitFor;
3002
- //# sourceMappingURL=chunk-24ATWNQR.cjs.map
3003
- //# sourceMappingURL=chunk-24ATWNQR.cjs.map
3088
+ //# sourceMappingURL=chunk-CEPH65VF.cjs.map
3089
+ //# sourceMappingURL=chunk-CEPH65VF.cjs.map