Skip to content

Commit 3c2ebd7

Browse files
committed
fix: derive Android Maestro scroll viewport
1 parent 9de54b1 commit 3c2ebd7

2 files changed

Lines changed: 48 additions & 2 deletions

File tree

src/compat/maestro/__tests__/runtime-port-geometry.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,36 @@ test('excludes an in-viewport Android scrollable that is hidden from the user',
137137
});
138138
});
139139

140+
test('derives an Android viewport from visible scroll containers when roots are omitted', () => {
141+
const snapshot = {
142+
createdAt: 0,
143+
nodes: [
144+
{
145+
index: 0,
146+
ref: '@e1',
147+
type: 'android.widget.ScrollView',
148+
visibleToUser: true,
149+
rect: { x: 0, y: 0, width: 1344, height: 2992 },
150+
},
151+
{
152+
index: 1,
153+
ref: '@e2',
154+
parentIndex: 0,
155+
type: 'android.widget.ScrollView',
156+
visibleToUser: true,
157+
rect: { x: 54, y: 159, width: 1236, height: 2449 },
158+
},
159+
],
160+
};
161+
162+
expect(
163+
resolveMaestroScrollableGesture(snapshot, { id: 'home-open-form' }, 'down', 600, 'android'),
164+
).toEqual({
165+
gesture: { from: { x: 672, y: 1496 }, to: { x: 672, y: 299 }, durationMs: 600 },
166+
viewport: { x: 0, y: 0, width: 1344, height: 2992 },
167+
});
168+
});
169+
140170
test('does not associate a selector in an Android hidden scroll subtree', () => {
141171
const snapshot = {
142172
createdAt: 0,

src/compat/maestro/runtime-port-geometry.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,15 @@ function selectMaestroScrollableViewport(
6262
direction: MaestroDirection,
6363
platform: MaestroPlatform,
6464
): Rect | undefined {
65-
const applicationViewport = findLargestViewportRect(snapshot.nodes);
66-
if (!applicationViewport || !isPositiveFiniteRect(applicationViewport)) return undefined;
6765
const vertical = direction === 'up' || direction === 'down';
6866
const scrollable = filterVisibleMaestroMatches({
6967
nodes: snapshot.nodes,
7068
matches: snapshot.nodes.filter((node) => isScrollableSnapshotType(node.type)),
7169
platform,
7270
});
71+
const applicationViewport =
72+
findLargestViewportRect(snapshot.nodes) ?? findLargestPositiveRect(scrollable);
73+
if (!applicationViewport || !isPositiveFiniteRect(applicationViewport)) return undefined;
7374
const candidates = scrollable.flatMap((node) => {
7475
if (!isPositiveFiniteRect(node.rect)) return [];
7576
const viewport = intersectRects(node.rect, applicationViewport);
@@ -95,6 +96,21 @@ function selectMaestroScrollableViewport(
9596
return candidates.sort(compareViewportAreaDescending)[0]?.viewport;
9697
}
9798

99+
function findLargestPositiveRect(
100+
nodes: readonly SnapshotState['nodes'][number][],
101+
): Rect | undefined {
102+
let largest: Rect | undefined;
103+
for (const node of nodes) {
104+
if (
105+
isPositiveFiniteRect(node.rect) &&
106+
(!largest || node.rect.width * node.rect.height > largest.width * largest.height)
107+
) {
108+
largest = node.rect;
109+
}
110+
}
111+
return largest;
112+
}
113+
98114
function intersectRects(left: Rect, right: Rect): Rect | undefined {
99115
const x = Math.max(left.x, right.x);
100116
const y = Math.max(left.y, right.y);

0 commit comments

Comments
 (0)