react-native-ai-devtools 1.4.30 → 1.4.32
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/build/__tests__/unit/fiberMatch.test.d.ts +2 -0
- package/build/__tests__/unit/fiberMatch.test.d.ts.map +1 -0
- package/build/__tests__/unit/fiberMatch.test.js +114 -0
- package/build/__tests__/unit/fiberMatch.test.js.map +1 -0
- package/build/__tests__/unit/tap.test.js +113 -1
- package/build/__tests__/unit/tap.test.js.map +1 -1
- package/build/core/executor.d.ts +1 -0
- package/build/core/executor.d.ts.map +1 -1
- package/build/core/executor.js +286 -55
- package/build/core/executor.js.map +1 -1
- package/build/core/fiberMatch.d.ts +25 -0
- package/build/core/fiberMatch.d.ts.map +1 -0
- package/build/core/fiberMatch.js +35 -0
- package/build/core/fiberMatch.js.map +1 -0
- package/build/core/telemetry.d.ts +1 -1
- package/build/core/telemetry.d.ts.map +1 -1
- package/build/core/telemetry.js +12 -3
- package/build/core/telemetry.js.map +1 -1
- package/build/index.js +65 -13
- package/build/index.js.map +1 -1
- package/build/pro/tap.d.ts +13 -0
- package/build/pro/tap.d.ts.map +1 -1
- package/build/pro/tap.js +43 -14
- package/build/pro/tap.js.map +1 -1
- package/package.json +1 -1
package/build/core/executor.js
CHANGED
|
@@ -1523,9 +1523,28 @@ export async function getPressableElements(options = {}) {
|
|
|
1523
1523
|
return null;
|
|
1524
1524
|
}
|
|
1525
1525
|
|
|
1526
|
-
function
|
|
1526
|
+
function findHostsInSubtree(fiber, depth, hosts, limit) {
|
|
1527
|
+
if (!fiber || depth > 20 || hosts.length >= limit) return;
|
|
1528
|
+
if (typeof fiber.type === 'string' && getMeasurable(fiber)) {
|
|
1529
|
+
hosts.push(fiber);
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
var child = fiber.child;
|
|
1533
|
+
while (child && hosts.length < limit) {
|
|
1534
|
+
findHostsInSubtree(child, depth + 1, hosts, limit);
|
|
1535
|
+
child = child.sibling;
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
function collectText(fiber, d, isRoot) {
|
|
1527
1540
|
if (!fiber || d > 30) return '';
|
|
1528
1541
|
var props = fiber.memoizedProps;
|
|
1542
|
+
// Stop descent at nested pressable/input boundaries — their text belongs to them, not to the outer wrapper.
|
|
1543
|
+
if (!isRoot && props && (typeof props.onPress === 'function' ||
|
|
1544
|
+
typeof props.onChangeText === 'function' ||
|
|
1545
|
+
typeof props.onFocus === 'function')) {
|
|
1546
|
+
return '';
|
|
1547
|
+
}
|
|
1529
1548
|
if (props) {
|
|
1530
1549
|
var ch = props.children;
|
|
1531
1550
|
if (typeof ch === 'string') return ch;
|
|
@@ -1542,7 +1561,7 @@ export async function getPressableElements(options = {}) {
|
|
|
1542
1561
|
var parts = [];
|
|
1543
1562
|
var child = fiber.child;
|
|
1544
1563
|
while (child) {
|
|
1545
|
-
var t = collectText(child, d + 1);
|
|
1564
|
+
var t = collectText(child, d + 1, false);
|
|
1546
1565
|
if (t) parts.push(t);
|
|
1547
1566
|
child = child.sibling;
|
|
1548
1567
|
}
|
|
@@ -1610,22 +1629,28 @@ export async function getPressableElements(options = {}) {
|
|
|
1610
1629
|
var isInput = !isPressable && props && (typeof props.onChangeText === 'function' || typeof props.onFocus === 'function');
|
|
1611
1630
|
|
|
1612
1631
|
if (isPressable || isInput) {
|
|
1613
|
-
var
|
|
1614
|
-
|
|
1615
|
-
|
|
1632
|
+
var hostsForThis = [];
|
|
1633
|
+
findHostsInSubtree(fiber, 0, hostsForThis, 16);
|
|
1634
|
+
if (hostsForThis.length > 0) {
|
|
1635
|
+
var text = collectText(fiber, 0, true);
|
|
1616
1636
|
var componentName = findMeaningfulAncestorName(fiber) || name || 'Unknown';
|
|
1617
1637
|
var path = buildPath(fiber);
|
|
1618
1638
|
var testID = (props && (props.testID || props.nativeID)) || null;
|
|
1619
1639
|
var accessibilityLabel = (props && props.accessibilityLabel) || null;
|
|
1620
1640
|
|
|
1621
|
-
|
|
1641
|
+
var hostIndices = [];
|
|
1642
|
+
for (var hi = 0; hi < hostsForThis.length; hi++) {
|
|
1643
|
+
hostIndices.push(hostFibers.length);
|
|
1644
|
+
hostFibers.push(hostsForThis[hi]);
|
|
1645
|
+
}
|
|
1622
1646
|
fiberMeta.push({
|
|
1623
1647
|
component: componentName,
|
|
1624
1648
|
path: path,
|
|
1625
1649
|
text: text ? text.slice(0, 100) : '',
|
|
1626
1650
|
testID: testID,
|
|
1627
1651
|
accessibilityLabel: accessibilityLabel,
|
|
1628
|
-
isInput: !!isInput
|
|
1652
|
+
isInput: !!isInput,
|
|
1653
|
+
hostIndices: hostIndices
|
|
1629
1654
|
});
|
|
1630
1655
|
}
|
|
1631
1656
|
}
|
|
@@ -1641,16 +1666,18 @@ export async function getPressableElements(options = {}) {
|
|
|
1641
1666
|
|
|
1642
1667
|
if (hostFibers.length === 0) return { error: 'No pressable elements found on screen.' };
|
|
1643
1668
|
|
|
1644
|
-
// Also measure the root view for viewport detection
|
|
1669
|
+
// Also measure the root view for viewport detection (appended; tracked by explicit index to preserve hostIndices)
|
|
1645
1670
|
var rootHost = findFirstHost(roots[0].current, 0);
|
|
1671
|
+
var rootIdx = -1;
|
|
1646
1672
|
if (rootHost) {
|
|
1647
|
-
hostFibers.
|
|
1648
|
-
|
|
1673
|
+
rootIdx = hostFibers.length;
|
|
1674
|
+
hostFibers.push(rootHost);
|
|
1649
1675
|
}
|
|
1650
1676
|
|
|
1651
1677
|
globalThis.__pressableFibers = hostFibers;
|
|
1652
1678
|
globalThis.__pressableMeta = fiberMeta;
|
|
1653
1679
|
globalThis.__pressableMeasurements = new Array(hostFibers.length).fill(null);
|
|
1680
|
+
globalThis.__pressableRootIdx = rootIdx;
|
|
1654
1681
|
|
|
1655
1682
|
for (var i = 0; i < hostFibers.length; i++) {
|
|
1656
1683
|
try {
|
|
@@ -1684,34 +1711,53 @@ export async function getPressableElements(options = {}) {
|
|
|
1684
1711
|
var fibers = globalThis.__pressableFibers;
|
|
1685
1712
|
var meta = globalThis.__pressableMeta;
|
|
1686
1713
|
var measurements = globalThis.__pressableMeasurements;
|
|
1714
|
+
var rootIdx = globalThis.__pressableRootIdx;
|
|
1687
1715
|
globalThis.__pressableFibers = null;
|
|
1688
1716
|
globalThis.__pressableMeta = null;
|
|
1689
1717
|
globalThis.__pressableMeasurements = null;
|
|
1718
|
+
globalThis.__pressableRootIdx = null;
|
|
1690
1719
|
|
|
1691
1720
|
if (!fibers || !measurements || !meta) {
|
|
1692
1721
|
return { error: 'No measurement data. Run get_pressable_elements again.' };
|
|
1693
1722
|
}
|
|
1694
1723
|
|
|
1695
|
-
// Get viewport dimensions
|
|
1724
|
+
// Get viewport dimensions from the explicit root measurement (fallback to scanning)
|
|
1696
1725
|
var viewportW = 9999, viewportH = 9999;
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1726
|
+
var rootM = (rootIdx != null && rootIdx >= 0) ? measurements[rootIdx] : null;
|
|
1727
|
+
if (rootM && rootM.width > 0 && rootM.height > 0) {
|
|
1728
|
+
viewportW = rootM.width;
|
|
1729
|
+
viewportH = rootM.height + (rootM.y > 0 ? rootM.y : 0);
|
|
1730
|
+
} else {
|
|
1731
|
+
for (var v = 0; v < measurements.length; v++) {
|
|
1732
|
+
if (measurements[v] && measurements[v].x === 0 && measurements[v].y <= 0 &&
|
|
1733
|
+
measurements[v].width > 0 && measurements[v].height > 0) {
|
|
1734
|
+
viewportW = measurements[v].width;
|
|
1735
|
+
viewportH = measurements[v].height + measurements[v].y;
|
|
1736
|
+
break;
|
|
1737
|
+
}
|
|
1703
1738
|
}
|
|
1704
1739
|
}
|
|
1705
1740
|
|
|
1706
1741
|
var elements = [];
|
|
1707
1742
|
|
|
1708
|
-
for (var i = 0; i <
|
|
1709
|
-
var m = measurements[i];
|
|
1710
|
-
if (!m) continue;
|
|
1711
|
-
|
|
1743
|
+
for (var i = 0; i < meta.length; i++) {
|
|
1712
1744
|
var info = meta[i];
|
|
1713
|
-
|
|
1714
|
-
|
|
1745
|
+
|
|
1746
|
+
// Union all host measurements for this pressable to get its true bounds
|
|
1747
|
+
var uMinX = Infinity, uMinY = Infinity, uMaxX = -Infinity, uMaxY = -Infinity;
|
|
1748
|
+
var hasValid = false;
|
|
1749
|
+
var indices = info.hostIndices || [];
|
|
1750
|
+
for (var hi2 = 0; hi2 < indices.length; hi2++) {
|
|
1751
|
+
var mm = measurements[indices[hi2]];
|
|
1752
|
+
if (!mm || mm.width <= 0 || mm.height <= 0) continue;
|
|
1753
|
+
hasValid = true;
|
|
1754
|
+
if (mm.x < uMinX) uMinX = mm.x;
|
|
1755
|
+
if (mm.y < uMinY) uMinY = mm.y;
|
|
1756
|
+
if (mm.x + mm.width > uMaxX) uMaxX = mm.x + mm.width;
|
|
1757
|
+
if (mm.y + mm.height > uMaxY) uMaxY = mm.y + mm.height;
|
|
1758
|
+
}
|
|
1759
|
+
if (!hasValid) continue;
|
|
1760
|
+
var m = { x: uMinX, y: uMinY, width: uMaxX - uMinX, height: uMaxY - uMinY };
|
|
1715
1761
|
|
|
1716
1762
|
// Filter: only visible within viewport
|
|
1717
1763
|
if (m.width <= 0 || m.height <= 0) continue;
|
|
@@ -1742,8 +1788,19 @@ export async function getPressableElements(options = {}) {
|
|
|
1742
1788
|
}
|
|
1743
1789
|
|
|
1744
1790
|
// Deduplicate: multiple nested pressables (View > TouchableOpacity > TouchableOpacity)
|
|
1745
|
-
// often share the same frame. Keep the one with the most meaningful component name
|
|
1791
|
+
// often share the same frame. Keep the one with the most meaningful component name,
|
|
1792
|
+
// but merge text/testID/accessibilityLabel from the loser so stopping collectText
|
|
1793
|
+
// at nested-pressable boundaries does not drop labels across the merge.
|
|
1746
1794
|
var HOST_NAMES = /^(View|TouchableOpacity|TouchableHighlight|TouchableWithoutFeedback|Pressable|TouchableNativeFeedback|Text|RCTView|RCTText)$/;
|
|
1795
|
+
function mergeFields(winner, loser) {
|
|
1796
|
+
if (!winner.text && loser.text) {
|
|
1797
|
+
winner.text = loser.text;
|
|
1798
|
+
winner.hasLabel = winner.text.length > 0;
|
|
1799
|
+
}
|
|
1800
|
+
if (!winner.testID && loser.testID) winner.testID = loser.testID;
|
|
1801
|
+
if (!winner.accessibilityLabel && loser.accessibilityLabel) winner.accessibilityLabel = loser.accessibilityLabel;
|
|
1802
|
+
return winner;
|
|
1803
|
+
}
|
|
1747
1804
|
var deduped = {};
|
|
1748
1805
|
for (var di = 0; di < elements.length; di++) {
|
|
1749
1806
|
var el = elements[di];
|
|
@@ -1752,16 +1809,20 @@ export async function getPressableElements(options = {}) {
|
|
|
1752
1809
|
if (!existing) {
|
|
1753
1810
|
deduped[key] = el;
|
|
1754
1811
|
} else {
|
|
1755
|
-
// Prefer non-host/non-primitive name
|
|
1756
1812
|
var existingIsGeneric = HOST_NAMES.test(existing.component);
|
|
1757
1813
|
var newIsGeneric = HOST_NAMES.test(el.component);
|
|
1814
|
+
var winner, loser;
|
|
1758
1815
|
if (existingIsGeneric && !newIsGeneric) {
|
|
1759
|
-
|
|
1760
|
-
} else if (existingIsGeneric && newIsGeneric) {
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1816
|
+
winner = el; loser = existing;
|
|
1817
|
+
} else if (!existingIsGeneric && newIsGeneric) {
|
|
1818
|
+
winner = existing; loser = el;
|
|
1819
|
+
} else {
|
|
1820
|
+
// Both generic or both meaningful — prefer the one with more identifiers.
|
|
1821
|
+
if (!existing.testID && el.testID) { winner = el; loser = existing; }
|
|
1822
|
+
else if (!existing.accessibilityLabel && el.accessibilityLabel) { winner = el; loser = existing; }
|
|
1823
|
+
else { winner = existing; loser = el; }
|
|
1764
1824
|
}
|
|
1825
|
+
deduped[key] = mergeFields(winner, loser);
|
|
1765
1826
|
}
|
|
1766
1827
|
}
|
|
1767
1828
|
elements = [];
|
|
@@ -1769,6 +1830,30 @@ export async function getPressableElements(options = {}) {
|
|
|
1769
1830
|
elements.push(deduped[dk]);
|
|
1770
1831
|
}
|
|
1771
1832
|
|
|
1833
|
+
// Tag wrappers: pressables that cover >=50% of viewport AND geometrically contain another pressable.
|
|
1834
|
+
// These are typically keyboard-dismiss/full-screen Touchable wrappers — agents should skip them.
|
|
1835
|
+
var viewportArea = (viewportW > 0 && viewportH > 0 && viewportW < 9999 && viewportH < 9999)
|
|
1836
|
+
? viewportW * viewportH : 0;
|
|
1837
|
+
if (viewportArea > 0) {
|
|
1838
|
+
for (var wi = 0; wi < elements.length; wi++) {
|
|
1839
|
+
var we = elements[wi];
|
|
1840
|
+
var weArea = we.frame.width * we.frame.height;
|
|
1841
|
+
if (weArea < viewportArea * 0.5) continue;
|
|
1842
|
+
for (var wj = 0; wj < elements.length; wj++) {
|
|
1843
|
+
if (wj === wi) continue;
|
|
1844
|
+
var other = elements[wj];
|
|
1845
|
+
if (other.frame.x >= we.frame.x &&
|
|
1846
|
+
other.frame.y >= we.frame.y &&
|
|
1847
|
+
other.frame.x + other.frame.width <= we.frame.x + we.frame.width &&
|
|
1848
|
+
other.frame.y + other.frame.height <= we.frame.y + we.frame.height &&
|
|
1849
|
+
other.frame.width * other.frame.height < weArea) {
|
|
1850
|
+
we.isWrapper = true;
|
|
1851
|
+
break;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1772
1857
|
// Sort top-to-bottom, left-to-right
|
|
1773
1858
|
elements.sort(function(a, b) {
|
|
1774
1859
|
if (a.center.y !== b.center.y) return a.center.y - b.center.y;
|
|
@@ -1808,7 +1893,8 @@ export async function getPressableElements(options = {}) {
|
|
|
1808
1893
|
ids.push(`a11y="${el.accessibilityLabel}"`);
|
|
1809
1894
|
const idStr = ids.length > 0 ? ` [${ids.join(", ")}]` : "";
|
|
1810
1895
|
const inputStr = el.isInput ? " (input)" : "";
|
|
1811
|
-
|
|
1896
|
+
const wrapperStr = el.isWrapper ? " [wrapper — skip unless dismissing keyboard]" : "";
|
|
1897
|
+
lines.push(`${num}. ${el.component} ${label} — center:(${el.center.x},${el.center.y}) frame:(${el.frame.x},${el.frame.y} ${el.frame.width}x${el.frame.height})${idStr}${inputStr}${wrapperStr}`);
|
|
1812
1898
|
if (el.path)
|
|
1813
1899
|
lines.push(` path: ${el.path}`);
|
|
1814
1900
|
}
|
|
@@ -2360,6 +2446,51 @@ export async function pressElement(options) {
|
|
|
2360
2446
|
return null;
|
|
2361
2447
|
}
|
|
2362
2448
|
|
|
2449
|
+
var RN_PRIMITIVES = /^(Animated\\(.*|withAnimated.*|AnimatedComponent.*|ForwardRef.*|memo\\(.*|Context\\.Consumer|Context\\.Provider|RCT.*|RNS.*|RNC.*|ViewManagerAdapter_.*|VirtualizedList.*|CellRenderer.*|FrameSizeProvider.*|MaybeScreenContainer|MaybeScreen|Navigation.*|Screen$|ScreenStack|ScreenContainer|ScreenContentWrapper|SceneView|DelayedFreeze|Freeze|Suspender|DebugContainer|StaticContainer|SafeAreaProvider.*|SafeAreaFrameContext|SafeAreaInsetsContext|ExpoRoot|ExpoRootComponent|GestureHandler.*|NativeViewGestureHandler|GestureDetector|PanGestureHandler|Reanimated.*|BottomTabNavigator|TabLayout|RouteNode|Route$|KeyboardProvider|PortalProviderComponent|BottomSheetModalProviderWrapper|ThemeContext|ThemeProvider|TextAncestorContext|PressabilityDebugView|TouchableHighlightImpl|StatusBarOverlay|BottomSheetHostingContainerComponent|BottomSheetGestureHandlersProvider|BottomSheetBackdropContainerComponent|BottomSheetContainerComponent|BottomSheetDraggableViewComponent|BottomSheetHandleContainerComponent|BottomSheetBackgroundContainerComponent|DebuggingOverlay|InspectorDeferred|Inspector|InspectorOverlay|InspectorPanel|StyleInspector|BoxInspector|BoxContainer|ElementBox|BorderBox|InspectorPanelButton)$/;
|
|
2450
|
+
|
|
2451
|
+
function isScreenHidden(name, props) {
|
|
2452
|
+
if (!props) return false;
|
|
2453
|
+
if (name === 'RNSScreen' && props['aria-hidden'] === true) return true;
|
|
2454
|
+
if (name === 'MaybeScreen' && props.active === 0) return true;
|
|
2455
|
+
if (name === 'SceneView' && props.focused === false) return true;
|
|
2456
|
+
return false;
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
function findMeaningfulAncestorName(fiber) {
|
|
2460
|
+
var cur = fiber.return;
|
|
2461
|
+
var depth = 0;
|
|
2462
|
+
var fallbackName = null;
|
|
2463
|
+
while (cur && depth < 20) {
|
|
2464
|
+
var aname = getComponentName(cur);
|
|
2465
|
+
if (aname && typeof cur.type !== 'string') {
|
|
2466
|
+
if (!fallbackName) fallbackName = aname;
|
|
2467
|
+
if (!RN_PRIMITIVES.test(aname)) return aname;
|
|
2468
|
+
}
|
|
2469
|
+
cur = cur.return;
|
|
2470
|
+
depth++;
|
|
2471
|
+
}
|
|
2472
|
+
return fallbackName;
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
// Walk UP collecting testID/nativeID from ancestors. Stop at screen boundaries.
|
|
2476
|
+
function collectAncestorTestIDs(fiber, maxUp) {
|
|
2477
|
+
var ids = [];
|
|
2478
|
+
var cur = fiber.return;
|
|
2479
|
+
var d = 0;
|
|
2480
|
+
while (cur && d < maxUp) {
|
|
2481
|
+
var cname = getComponentName(cur);
|
|
2482
|
+
if (cname === 'RNSScreen' || cname === 'MaybeScreen' || cname === 'SceneView') break;
|
|
2483
|
+
var cp = cur.memoizedProps;
|
|
2484
|
+
if (cp) {
|
|
2485
|
+
if (typeof cp.testID === 'string' && cp.testID) ids.push(cp.testID);
|
|
2486
|
+
if (typeof cp.nativeID === 'string' && cp.nativeID) ids.push(cp.nativeID);
|
|
2487
|
+
}
|
|
2488
|
+
cur = cur.return;
|
|
2489
|
+
d++;
|
|
2490
|
+
}
|
|
2491
|
+
return ids;
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2363
2494
|
// Find the first measurable host descendant of a fiber.
|
|
2364
2495
|
// For inputs, prefer TextInput-specific hosts over generic RCTView.
|
|
2365
2496
|
function findFirstHost(fiber, depth, isInput) {
|
|
@@ -2402,10 +2533,7 @@ export async function pressElement(options) {
|
|
|
2402
2533
|
var name = getComponentName(fiber);
|
|
2403
2534
|
var props = fiber.memoizedProps;
|
|
2404
2535
|
|
|
2405
|
-
|
|
2406
|
-
if (name === 'RNSScreen' && props && props['aria-hidden'] === true) return;
|
|
2407
|
-
if (name === 'MaybeScreen' && props && props.active === 0) return;
|
|
2408
|
-
if (name === 'SceneView' && props && props.focused === false) return;
|
|
2536
|
+
if (isScreenHidden(name, props)) return;
|
|
2409
2537
|
|
|
2410
2538
|
var isPressable = props && typeof props.onPress === 'function';
|
|
2411
2539
|
var isInput = !isPressable && props && (typeof props.onChangeText === 'function' || typeof props.onFocus === 'function');
|
|
@@ -2421,14 +2549,18 @@ export async function pressElement(options) {
|
|
|
2421
2549
|
text = extractText(fiber, 0) || val || defVal || ph;
|
|
2422
2550
|
}
|
|
2423
2551
|
var tid = props.testID || props.nativeID || null;
|
|
2552
|
+
var meaningful = findMeaningfulAncestorName(fiber);
|
|
2553
|
+
var ancestorIDs = collectAncestorTestIDs(fiber, maxTraversalUp);
|
|
2424
2554
|
|
|
2425
2555
|
var host = findFirstHost(fiber, 0, isInput);
|
|
2426
2556
|
if (host) {
|
|
2427
2557
|
hostFibers.push(host);
|
|
2428
2558
|
tapMeta.push({
|
|
2429
2559
|
name: name || '(anonymous)',
|
|
2560
|
+
meaningfulComponentName: meaningful || null,
|
|
2430
2561
|
text: text.substring(0, 100),
|
|
2431
2562
|
testID: tid,
|
|
2563
|
+
ancestorTestIDs: ancestorIDs,
|
|
2432
2564
|
path: path.join(' > '),
|
|
2433
2565
|
isInput: isInput,
|
|
2434
2566
|
isPressable: isPressable,
|
|
@@ -2449,21 +2581,40 @@ export async function pressElement(options) {
|
|
|
2449
2581
|
walkFiber(roots[ri].current, 0, []);
|
|
2450
2582
|
}
|
|
2451
2583
|
|
|
2452
|
-
// Phase 2a: testID on non-pressable wrapper — walk UP to pressable/input
|
|
2584
|
+
// Phase 2a: testID on non-pressable wrapper — walk UP or DOWN to pressable/input.
|
|
2585
|
+
// Skipped if Phase 1 already matched via own testID or ancestor testID.
|
|
2453
2586
|
if (searchTestID !== null) {
|
|
2454
|
-
var
|
|
2587
|
+
var hasEnrichedTestIDMatch = false;
|
|
2455
2588
|
for (var di = 0; di < tapMeta.length; di++) {
|
|
2456
|
-
if (tapMeta[di].testID === searchTestID) {
|
|
2457
|
-
|
|
2589
|
+
if (tapMeta[di].testID === searchTestID) { hasEnrichedTestIDMatch = true; break; }
|
|
2590
|
+
var aids = tapMeta[di].ancestorTestIDs || [];
|
|
2591
|
+
for (var ai = 0; ai < aids.length; ai++) {
|
|
2592
|
+
if (aids[ai] === searchTestID) { hasEnrichedTestIDMatch = true; break; }
|
|
2593
|
+
}
|
|
2594
|
+
if (hasEnrichedTestIDMatch) break;
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
if (!hasEnrichedTestIDMatch) {
|
|
2598
|
+
function findDescendantPressable(fiber, d) {
|
|
2599
|
+
if (!fiber || d > 10) return null;
|
|
2600
|
+
var fp = fiber.memoizedProps;
|
|
2601
|
+
var dIsPressable = fp && typeof fp.onPress === 'function';
|
|
2602
|
+
var dIsInput = !dIsPressable && fp && (typeof fp.onChangeText === 'function' || typeof fp.onFocus === 'function');
|
|
2603
|
+
if (dIsPressable || dIsInput) return { fiber: fiber, isPressable: dIsPressable, isInput: dIsInput };
|
|
2604
|
+
var c = fiber.child;
|
|
2605
|
+
while (c) {
|
|
2606
|
+
var r = findDescendantPressable(c, d + 1);
|
|
2607
|
+
if (r) return r;
|
|
2608
|
+
c = c.sibling;
|
|
2609
|
+
}
|
|
2610
|
+
return null;
|
|
2611
|
+
}
|
|
2458
2612
|
|
|
2459
|
-
if (!hasDirectTestIDMatch) {
|
|
2460
2613
|
function findByTestID2a(fiber, path) {
|
|
2461
2614
|
if (!fiber) return;
|
|
2462
2615
|
var name = getComponentName(fiber);
|
|
2463
2616
|
var props = fiber.memoizedProps;
|
|
2464
|
-
if (name
|
|
2465
|
-
if (name === 'MaybeScreen' && props && props.active === 0) return;
|
|
2466
|
-
if (name === 'SceneView' && props && props.focused === false) return;
|
|
2617
|
+
if (isScreenHidden(name, props)) return;
|
|
2467
2618
|
|
|
2468
2619
|
var tid = props && (props.testID || props.nativeID || null);
|
|
2469
2620
|
if (tid === searchTestID) {
|
|
@@ -2477,8 +2628,10 @@ export async function pressElement(options) {
|
|
|
2477
2628
|
hostFibers.push(host);
|
|
2478
2629
|
tapMeta.push({
|
|
2479
2630
|
name: name || '(anonymous)',
|
|
2631
|
+
meaningfulComponentName: findMeaningfulAncestorName(fiber) || null,
|
|
2480
2632
|
text: text.substring(0, 100),
|
|
2481
2633
|
testID: searchTestID,
|
|
2634
|
+
ancestorTestIDs: [],
|
|
2482
2635
|
path: path.join(' > '),
|
|
2483
2636
|
isInput: nIsInput,
|
|
2484
2637
|
isPressable: nIsPressable,
|
|
@@ -2486,6 +2639,7 @@ export async function pressElement(options) {
|
|
|
2486
2639
|
});
|
|
2487
2640
|
}
|
|
2488
2641
|
} else {
|
|
2642
|
+
var foundAncestor = false;
|
|
2489
2643
|
var parent = fiber.return;
|
|
2490
2644
|
var d = 0;
|
|
2491
2645
|
while (parent && d < maxTraversalUp) {
|
|
@@ -2499,19 +2653,45 @@ export async function pressElement(options) {
|
|
|
2499
2653
|
hostFibers.push(host);
|
|
2500
2654
|
tapMeta.push({
|
|
2501
2655
|
name: name || '(anonymous)',
|
|
2656
|
+
meaningfulComponentName: findMeaningfulAncestorName(parent) || null,
|
|
2502
2657
|
text: pText.substring(0, 100),
|
|
2503
2658
|
testID: pp.testID || pp.nativeID || searchTestID,
|
|
2659
|
+
ancestorTestIDs: [],
|
|
2504
2660
|
path: path.join(' > '),
|
|
2505
2661
|
isInput: pIsInput,
|
|
2506
2662
|
isPressable: pIsPressable,
|
|
2507
2663
|
source: 'testID-ancestor'
|
|
2508
2664
|
});
|
|
2665
|
+
foundAncestor = true;
|
|
2509
2666
|
}
|
|
2510
2667
|
break;
|
|
2511
2668
|
}
|
|
2512
2669
|
parent = parent.return;
|
|
2513
2670
|
d++;
|
|
2514
2671
|
}
|
|
2672
|
+
|
|
2673
|
+
if (!foundAncestor) {
|
|
2674
|
+
var desc = findDescendantPressable(fiber, 0);
|
|
2675
|
+
if (desc) {
|
|
2676
|
+
var dp = desc.fiber.memoizedProps;
|
|
2677
|
+
var dText = desc.isPressable ? extractText(desc.fiber, 0) : (extractText(desc.fiber, 0) || (typeof dp.value === 'string' ? dp.value : '') || (typeof dp.defaultValue === 'string' ? dp.defaultValue : '') || (typeof dp.placeholder === 'string' ? dp.placeholder : ''));
|
|
2678
|
+
var dhost = findFirstHost(desc.fiber, 0, desc.isInput);
|
|
2679
|
+
if (dhost) {
|
|
2680
|
+
hostFibers.push(dhost);
|
|
2681
|
+
tapMeta.push({
|
|
2682
|
+
name: getComponentName(desc.fiber) || '(anonymous)',
|
|
2683
|
+
meaningfulComponentName: findMeaningfulAncestorName(desc.fiber) || null,
|
|
2684
|
+
text: dText.substring(0, 100),
|
|
2685
|
+
testID: dp.testID || dp.nativeID || searchTestID,
|
|
2686
|
+
ancestorTestIDs: [searchTestID],
|
|
2687
|
+
path: path.join(' > '),
|
|
2688
|
+
isInput: desc.isInput,
|
|
2689
|
+
isPressable: desc.isPressable,
|
|
2690
|
+
source: 'testID-descendant'
|
|
2691
|
+
});
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2515
2695
|
}
|
|
2516
2696
|
}
|
|
2517
2697
|
var child = fiber.child;
|
|
@@ -2527,25 +2707,41 @@ export async function pressElement(options) {
|
|
|
2527
2707
|
}
|
|
2528
2708
|
}
|
|
2529
2709
|
|
|
2530
|
-
// Phase 2b: component name on non-pressable node — walk UP to pressable parent
|
|
2710
|
+
// Phase 2b: component name on non-pressable node — walk UP or DOWN to pressable parent.
|
|
2711
|
+
// Skipped if Phase 1 already matched via own name or meaningfulComponentName.
|
|
2531
2712
|
if (searchComponent !== null) {
|
|
2532
|
-
var
|
|
2713
|
+
var scLower = searchComponent.toLowerCase();
|
|
2714
|
+
var hasEnrichedComponentMatch = false;
|
|
2533
2715
|
for (var ci = 0; ci < tapMeta.length; ci++) {
|
|
2534
|
-
|
|
2535
|
-
|
|
2716
|
+
var cn = (tapMeta[ci].name || '').toLowerCase();
|
|
2717
|
+
var cm = (tapMeta[ci].meaningfulComponentName || '').toLowerCase();
|
|
2718
|
+
if (cn.indexOf(scLower) !== -1 || cm.indexOf(scLower) !== -1) {
|
|
2719
|
+
hasEnrichedComponentMatch = true; break;
|
|
2536
2720
|
}
|
|
2537
2721
|
}
|
|
2538
2722
|
|
|
2539
|
-
if (!
|
|
2723
|
+
if (!hasEnrichedComponentMatch) {
|
|
2724
|
+
function findDescendantPressableOnly(fiber, d) {
|
|
2725
|
+
if (!fiber || d > 10) return null;
|
|
2726
|
+
var fp = fiber.memoizedProps;
|
|
2727
|
+
if (fp && typeof fp.onPress === 'function') return fiber;
|
|
2728
|
+
var c = fiber.child;
|
|
2729
|
+
while (c) {
|
|
2730
|
+
var r = findDescendantPressableOnly(c, d + 1);
|
|
2731
|
+
if (r) return r;
|
|
2732
|
+
c = c.sibling;
|
|
2733
|
+
}
|
|
2734
|
+
return null;
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2540
2737
|
function findByName2b(fiber, path) {
|
|
2541
2738
|
if (!fiber) return;
|
|
2542
2739
|
var name = getComponentName(fiber);
|
|
2543
2740
|
var props = fiber.memoizedProps;
|
|
2544
|
-
if (name
|
|
2545
|
-
if (name === 'MaybeScreen' && props && props.active === 0) return;
|
|
2546
|
-
if (name === 'SceneView' && props && props.focused === false) return;
|
|
2741
|
+
if (isScreenHidden(name, props)) return;
|
|
2547
2742
|
|
|
2548
|
-
if (name && name.toLowerCase().indexOf(
|
|
2743
|
+
if (name && name.toLowerCase().indexOf(scLower) !== -1) {
|
|
2744
|
+
var foundAncestor = false;
|
|
2549
2745
|
var parent = fiber.return;
|
|
2550
2746
|
var d = 0;
|
|
2551
2747
|
while (parent && d < maxTraversalUp) {
|
|
@@ -2557,19 +2753,45 @@ export async function pressElement(options) {
|
|
|
2557
2753
|
hostFibers.push(host);
|
|
2558
2754
|
tapMeta.push({
|
|
2559
2755
|
name: name,
|
|
2756
|
+
meaningfulComponentName: findMeaningfulAncestorName(parent) || null,
|
|
2560
2757
|
text: text.substring(0, 100),
|
|
2561
2758
|
testID: pp.testID || pp.nativeID || null,
|
|
2759
|
+
ancestorTestIDs: [],
|
|
2562
2760
|
path: path.join(' > '),
|
|
2563
2761
|
isInput: false,
|
|
2564
2762
|
isPressable: true,
|
|
2565
2763
|
source: 'component-ancestor'
|
|
2566
2764
|
});
|
|
2765
|
+
foundAncestor = true;
|
|
2567
2766
|
}
|
|
2568
2767
|
break;
|
|
2569
2768
|
}
|
|
2570
2769
|
parent = parent.return;
|
|
2571
2770
|
d++;
|
|
2572
2771
|
}
|
|
2772
|
+
|
|
2773
|
+
if (!foundAncestor) {
|
|
2774
|
+
var descFiber = findDescendantPressableOnly(fiber, 0);
|
|
2775
|
+
if (descFiber) {
|
|
2776
|
+
var dp = descFiber.memoizedProps;
|
|
2777
|
+
var dText = extractText(descFiber, 0);
|
|
2778
|
+
var dhost = findFirstHost(descFiber, 0, false);
|
|
2779
|
+
if (dhost) {
|
|
2780
|
+
hostFibers.push(dhost);
|
|
2781
|
+
tapMeta.push({
|
|
2782
|
+
name: getComponentName(descFiber) || '(anonymous)',
|
|
2783
|
+
meaningfulComponentName: name,
|
|
2784
|
+
text: dText.substring(0, 100),
|
|
2785
|
+
testID: dp.testID || dp.nativeID || null,
|
|
2786
|
+
ancestorTestIDs: [],
|
|
2787
|
+
path: path.join(' > '),
|
|
2788
|
+
isInput: false,
|
|
2789
|
+
isPressable: true,
|
|
2790
|
+
source: 'component-descendant'
|
|
2791
|
+
});
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2573
2795
|
}
|
|
2574
2796
|
var child = fiber.child;
|
|
2575
2797
|
while (child) {
|
|
@@ -2666,16 +2888,25 @@ export async function pressElement(options) {
|
|
|
2666
2888
|
|
|
2667
2889
|
var info = meta[i];
|
|
2668
2890
|
|
|
2669
|
-
// Match by query
|
|
2891
|
+
// Match by query — OR across own and enriched identifiers
|
|
2670
2892
|
var matched = true;
|
|
2671
2893
|
if (searchText !== null) {
|
|
2672
2894
|
matched = matched && info.text.toLowerCase().indexOf(searchText.toLowerCase()) !== -1;
|
|
2673
2895
|
}
|
|
2674
2896
|
if (searchTestID !== null) {
|
|
2675
|
-
|
|
2897
|
+
var ownTidMatch = info.testID === searchTestID;
|
|
2898
|
+
var aTids = info.ancestorTestIDs || [];
|
|
2899
|
+
var ancestorTidMatch = false;
|
|
2900
|
+
for (var ti = 0; ti < aTids.length; ti++) {
|
|
2901
|
+
if (aTids[ti] === searchTestID) { ancestorTidMatch = true; break; }
|
|
2902
|
+
}
|
|
2903
|
+
matched = matched && (ownTidMatch || ancestorTidMatch);
|
|
2676
2904
|
}
|
|
2677
2905
|
if (searchComponent !== null) {
|
|
2678
|
-
|
|
2906
|
+
var scq = searchComponent.toLowerCase();
|
|
2907
|
+
var ownNameMatch = (info.name || '').toLowerCase().indexOf(scq) !== -1;
|
|
2908
|
+
var meaningfulMatch = (info.meaningfulComponentName || '').toLowerCase().indexOf(scq) !== -1;
|
|
2909
|
+
matched = matched && (ownNameMatch || meaningfulMatch);
|
|
2679
2910
|
}
|
|
2680
2911
|
|
|
2681
2912
|
if (matched) {
|