@keepsnappy/appium-driver 0.1.0-alpha.14 → 0.1.0-alpha.16

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/README.md CHANGED
@@ -41,3 +41,34 @@ One GC command at the end of every run, after the journey's last step and its re
41
41
  ## driver-log.json while the run goes
42
42
 
43
43
  `<out>/driver-log.json` is rewritten atomically at every top-level step and iteration boundary, so a killed run still leaves a valid log. `status` is `running`, `done`, `terminated` or `error`; `lastStep` names the last step started and `steps` times every step (`ms: null` while one runs). On SIGTERM or SIGINT (the farm's step timeout) the driver flushes the log as `terminated` with the signal, captures the failure screen and source (at most 20 s), ends the session as failed (at most 10 s) and exits 143 or 130, within 45 s in all; a second signal exits at once. `keepsnappy farm run` counts a driver that exits non-zero as infrastructure.
44
+
45
+ ## List and gesture helpers
46
+
47
+ Import standalone helpers and pass the journey's `Session` (`t`). Selectors, page boundaries and step names come from your journey. Selectors match whole values, so anchor a row label (`^Result 1$`) when another row starts with the same text:
48
+
49
+ ```ts
50
+ import { childOf, id, text, scrollCycles } from '@keepsnappy/appium-driver';
51
+
52
+ await scrollCycles(t, {
53
+ list: id('results.list'),
54
+ first: childOf(text('^Result 1$'), 'results.list'),
55
+ any: text('Result .*'),
56
+ pages: [text('^Result 21$'), text('^Result 41$')],
57
+ pageNumbers: [21, 41],
58
+ beforePages: [text('^Result 20$'), text('^Result 40$')],
59
+ lastPage: text('Result (41|42|43)'),
60
+ field: 'text',
61
+ scoped: true,
62
+ stepName: (toward, cycle) => `results.scroll-to-${toward}-${cycle}`,
63
+ });
64
+ ```
65
+
66
+ `longScroll` sends Android native flings at 12000 dp/s (density cached per driver) or iOS 150 ms swipes. `flingUntilShown` stops when any part of the target shows: three gestures maximum, five toward the top on iOS. `scrollUntilShown` exposes the same capped look/move loop for custom checks. `waitCoasted` waits for two matching row frames 300 ms apart (up to 3000 ms); `holdBeforeScroll` captures a row before an iOS gesture so coast checks can reuse its handle.
67
+
68
+ `scrollToEnd` performs three long scrolls. Each page marker starts a 3000 ms load wait after coast stops (6000 ms Android, 10000 ms iOS); scrolls without a page marker pause 500 ms. Supply `beforePages` to permit exactly one additional fling when the loaded end was missed. `scrollCycles` repeats three end/top pairs, waiting for page markers only in cycle one and checking `lastPage` loaded in every cycle. `pageNumbers` preserves numeric page labels in command summaries; otherwise summaries use selector descriptions. Page sizes and seed data stay in the journey's page selectors. Options expose the counts and timeouts; their defaults are exported constants. `frameTimeoutMs` (20000) bounds the wait for the list frame to stop moving, `checkTimeoutMs` (20000) bounds a step check (`passChecked`, `endChecked`; in `scrollCycles` the top check reads `fling.checkTimeoutMs`), and `lookWindowMs` (1000) is the window in which a look after a gesture is retried. The `lookMs` field in the log stays the cumulative time spent looking.
69
+
70
+ `passChecked` and `endChecked` create step checks; share a `ListCache` with `flingUntilShown`/`scrollToEnd` for each visit to a list. `passChecked` permits one repair using only the original pass's remaining budget. `scoped: false` searches globally when row selectors are unique. `field: 'text' | 'description'` limits Android row lookups to the attribute the app uses. `rowAlternatives(from, to, format)` builds page-marker alternations from journey data; `rowAtOrBelow` checks whether a row has moved under a boundary. `t.frameOf` and `t.settledFrame` expose the existing stale-safe frame readers for custom mechanics.
71
+
72
+ `androidScreenScroll` extends an inverted-list fling to the screen edge at 3000 dp/s. `scrollFromDrawnRow` uses that Android path or starts iOS gestures on drawn rows, avoiding masked padding. Toward the top on iOS it starts on the lowest drawn row that ends above `above` (e.g. a composer), chosen by frame: rows in a list's faded top zone pass touches through. Pass `missingMessage` to keep your own error text when no drawn row is found. `scrollToTarget` waits for an initial target, optionally taps a reveal indicator, then uses a caller-supplied move with coast waits and a three-gesture cap. `closeSwipe`, `closeSwipeAt` (window percentages) and `closeSwipeFrom` (element centre) use native Android sheet drags or 400 ms iOS drags. `tapAllAt` sends points in one W3C sequence with a 100 ms hold and 45 ms gap per point.
73
+
74
+ Helpers log the underlying WebDriver commands; list loops also record gesture counts, checks, durations and failure status. Re-scroll checks emit a `re-scrolled` event. Keep caller-specific log names and error messages by passing `cmd`, `field` and `missing` to the custom loop, or `cmd`/`missing` to `scrollToTarget`.
@@ -3,7 +3,7 @@ import type { FetchLike } from './webdriver.js';
3
3
  export declare const HUB = "https://hub-cloud.browserstack.com/wd/hub";
4
4
  export declare const BS_API = "https://api-cloud.browserstack.com/app-automate";
5
5
  export declare const basicAuth: (user: string, key: string) => string;
6
- /** `bs://` passes through; a file is uploaded, or the recent upload under `customId` is reused. */
6
+ /** `bs://` and `custom_id:` pass through; a file is uploaded, or the recent upload under `customId` is reused. */
7
7
  export declare function resolveApp(app: string, customId: string, auth: string, fetchImpl: FetchLike, platform?: Platform): Promise<string>;
8
8
  export interface CapsInput {
9
9
  appUrl: string;
@@ -5,9 +5,9 @@ import { IOS } from './platform.js';
5
5
  export const HUB = 'https://hub-cloud.browserstack.com/wd/hub';
6
6
  export const BS_API = 'https://api-cloud.browserstack.com/app-automate';
7
7
  export const basicAuth = (user, key) => `Basic ${Buffer.from(`${user}:${key}`).toString('base64')}`;
8
- /** `bs://` passes through; a file is uploaded, or the recent upload under `customId` is reused. */
8
+ /** `bs://` and `custom_id:` pass through; a file is uploaded, or the recent upload under `customId` is reused. */
9
9
  export async function resolveApp(app, customId, auth, fetchImpl, platform = IOS) {
10
- if (app.startsWith('bs://'))
10
+ if (app.startsWith('bs://') || app.startsWith('custom_id:'))
11
11
  return app;
12
12
  const headers = { Authorization: auth };
13
13
  if (customId) {
@@ -1 +1 @@
1
- {"version":3,"file":"browserstack.js","sourceRoot":"","sources":["../../src/browserstack.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browserstack.js","sourceRoot":"","sources":["../../src/browserstack.ts"],"names":[],"mappings":"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"}
package/dist/src/cli.js CHANGED
File without changes
@@ -0,0 +1,75 @@
1
+ import type { Session } from './session.js';
2
+ import { type SelectorLike } from './selectors.js';
3
+ import type { Rect } from './webdriver.js';
4
+ export type ScrollDirection = 'end' | 'top';
5
+ export interface Point {
6
+ x: number;
7
+ y: number;
8
+ }
9
+ /** Default scroll budget; the third gesture is logged as extra. */
10
+ export declare const LONG_SCROLL_CAP = 3;
11
+ export declare const LONG_SCROLL_PLANNED = 2;
12
+ /** Additional iOS gestures allowed when returning to the top. */
13
+ export declare const IOS_TOP_EXTRA = 2;
14
+ /** Native Android fling speeds (dp/s), list fractions, and iOS swipe fractions/duration. */
15
+ export declare const LONG_SCROLL: {
16
+ readonly android: {
17
+ readonly from: 0.3;
18
+ readonly to: 0.85;
19
+ readonly dpPerSecond: 12000;
20
+ };
21
+ readonly androidScreen: {
22
+ readonly top: readonly [0.1, 0.95];
23
+ readonly end: readonly [0.9, 0.1];
24
+ readonly dpPerSecond: 3000;
25
+ };
26
+ readonly ios: {
27
+ readonly end: readonly [0.85, 0.1];
28
+ readonly top: readonly [0.3, 0.95];
29
+ readonly ms: 150;
30
+ };
31
+ };
32
+ export declare const CLOSE_SWIPE_MS = 400;
33
+ export declare const TAP_HOLD_MS = 100;
34
+ export declare const BURST_GAP_MS = 45;
35
+ export declare const ROW_TOP_SWIPE_MS = 400;
36
+ export declare const ROW_TOP_SCREEN_FRACTION = 0.98;
37
+ export declare const ROW_END_SCREEN_FRACTION = 0.1;
38
+ /**
39
+ * One long scroll inside window rectangle `r`, after readiness. Android sends a native fling at 12000 dp/s over
40
+ * 30–85% of the list; iOS drags 85→10% to the end or 30→95% to the top in 150 ms. Logs WebDriver commands only.
41
+ */
42
+ export declare function longScroll(t: Session, r: Rect, toward: ScrollDirection): Promise<void>;
43
+ /**
44
+ * Android inverted-list scroll extending from `list` to the window at 3000 dp/s. Top: 10% of the list to 95% of the
45
+ * window, direction up; end: 90% to 10%, direction down. Reads the list frame after readiness; logs commands only.
46
+ */
47
+ export declare function androidScreenScroll(t: Session, list: SelectorLike, toward: ScrollDirection): Promise<void>;
48
+ /**
49
+ * Closes a sheet between window points. Android uses native dragGesture; iOS uses a 400 ms drag. Logs commands only.
50
+ * Does not consume readiness itself; use closeSwipeAt/From when readiness is pending.
51
+ */
52
+ export declare function closeSwipe(t: Session, from: Point, to: Point): Promise<void>;
53
+ /** Closes a sheet between percentage window points (0–100), after readiness. Logs commands only. */
54
+ export declare function closeSwipeAt(t: Session, [fx, fy]: readonly [number, number], [tx, ty]: readonly [number, number]): Promise<void>;
55
+ /** Closes a sheet from `sel`'s centre to 90% of window height, after readiness. Logs commands only. */
56
+ export declare function closeSwipeFrom(t: Session, sel: SelectorLike): Promise<void>;
57
+ /**
58
+ * Taps window `points` in one W3C request, then releases actions (release errors are ignored). Each tap holds for
59
+ * holdMs (100) and pauses gapMs (45). No readiness or element lookup; prepare points before the measured step.
60
+ * Logs commands only; callers may add their own burst summary.
61
+ */
62
+ export declare function tapAllAt(t: Session, points: readonly Point[], { holdMs, gapMs }?: {
63
+ holdMs?: number;
64
+ gapMs?: number;
65
+ }): Promise<void>;
66
+ /**
67
+ * Scrolls an inverted list using drawn `row` elements. Android delegates to androidScreenScroll. iOS top starts on the
68
+ * lowest row whose visible attribute is 'true' and that ends above `above` (or the lowest drawn row when none does or no
69
+ * `above` is given), chosen by frame, not tree order, dragging to 98% of the screen in 400 ms. End selects the bottommost visible
70
+ * row, clamps its centre inside `list` when found, and drags to 10% in 150 ms. Fails without a drawn row using missingMessage (default 'no drawn row to scroll from'). Logs commands only.
71
+ */
72
+ export declare function scrollFromDrawnRow(t: Session, list: SelectorLike, row: SelectorLike, toward: ScrollDirection, { missingMessage, above }?: {
73
+ missingMessage?: string;
74
+ above?: SelectorLike;
75
+ }): Promise<void>;
@@ -0,0 +1,157 @@
1
+ import { toSelector } from './selectors.js';
2
+ /** Default scroll budget; the third gesture is logged as extra. */
3
+ export const LONG_SCROLL_CAP = 3;
4
+ export const LONG_SCROLL_PLANNED = 2;
5
+ /** Additional iOS gestures allowed when returning to the top. */
6
+ export const IOS_TOP_EXTRA = 2;
7
+ /** Native Android fling speeds (dp/s), list fractions, and iOS swipe fractions/duration. */
8
+ export const LONG_SCROLL = {
9
+ android: { from: 0.3, to: 0.85, dpPerSecond: 12_000 },
10
+ androidScreen: { top: [0.1, 0.95], end: [0.9, 0.1], dpPerSecond: 3_000 },
11
+ ios: { end: [0.85, 0.1], top: [0.3, 0.95], ms: 150 },
12
+ };
13
+ export const CLOSE_SWIPE_MS = 400;
14
+ export const TAP_HOLD_MS = 100;
15
+ export const BURST_GAP_MS = 45;
16
+ export const ROW_TOP_SWIPE_MS = 400;
17
+ export const ROW_TOP_SCREEN_FRACTION = 0.98;
18
+ export const ROW_END_SCREEN_FRACTION = 0.1;
19
+ const densities = new WeakMap();
20
+ /** Reads pixels per dp once per driver; logs the native density command and rejects invalid dpi. */
21
+ async function density(t) {
22
+ if (!densities.has(t.driver)) {
23
+ const dpi = Number(await t.driver.execute('mobile: getDisplayDensity'));
24
+ if (!(dpi > 0))
25
+ throw new Error(`mobile: getDisplayDensity answered ${dpi}, not a density`);
26
+ densities.set(t.driver, dpi / 160);
27
+ }
28
+ return densities.get(t.driver);
29
+ }
30
+ /**
31
+ * One long scroll inside window rectangle `r`, after readiness. Android sends a native fling at 12000 dp/s over
32
+ * 30–85% of the list; iOS drags 85→10% to the end or 30→95% to the top in 150 ms. Logs WebDriver commands only.
33
+ */
34
+ export async function longScroll(t, r, toward) {
35
+ await t.ready();
36
+ if (t.driver.platform.name === 'android') {
37
+ const { from, to, dpPerSecond } = LONG_SCROLL.android;
38
+ const top = Math.round(r.y + r.height * from);
39
+ await t.driver.execute('mobile: flingGesture', {
40
+ left: r.x, top, width: r.width, height: Math.round(r.y + r.height * to) - top,
41
+ direction: toward === 'end' ? 'down' : 'up', speed: Math.round(dpPerSecond * (await density(t))),
42
+ });
43
+ }
44
+ else {
45
+ const [from, to] = LONG_SCROLL.ios[toward];
46
+ const x = Math.round(r.x + r.width / 2);
47
+ await t.driver.drag({ x, y: Math.round(r.y + r.height * from) }, { x, y: Math.round(r.y + r.height * to) }, LONG_SCROLL.ios.ms);
48
+ }
49
+ }
50
+ /**
51
+ * Android inverted-list scroll extending from `list` to the window at 3000 dp/s. Top: 10% of the list to 95% of the
52
+ * window, direction up; end: 90% to 10%, direction down. Reads the list frame after readiness; logs commands only.
53
+ */
54
+ export async function androidScreenScroll(t, list, toward) {
55
+ await t.ready();
56
+ const r = (await t.frameOf(list, await t.waitVisible(list))).rect;
57
+ const w = await t.driver.screen();
58
+ const [inList, onScreen] = LONG_SCROLL.androidScreen[toward];
59
+ const start = Math.round(r.y + r.height * inList);
60
+ const end = Math.round(w.height * onScreen);
61
+ await t.driver.execute('mobile: flingGesture', {
62
+ left: r.x, top: Math.min(start, end), width: r.width, height: Math.abs(end - start),
63
+ direction: toward === 'top' ? 'up' : 'down', speed: Math.round(LONG_SCROLL.androidScreen.dpPerSecond * (await density(t))),
64
+ });
65
+ }
66
+ /**
67
+ * Closes a sheet between window points. Android uses native dragGesture; iOS uses a 400 ms drag. Logs commands only.
68
+ * Does not consume readiness itself; use closeSwipeAt/From when readiness is pending.
69
+ */
70
+ export async function closeSwipe(t, from, to) {
71
+ if (t.driver.platform.name === 'android') {
72
+ await t.driver.execute('mobile: dragGesture', { startX: from.x, startY: from.y, endX: to.x, endY: to.y });
73
+ }
74
+ else {
75
+ await t.driver.drag(from, to, CLOSE_SWIPE_MS);
76
+ }
77
+ }
78
+ /** Closes a sheet between percentage window points (0–100), after readiness. Logs commands only. */
79
+ export async function closeSwipeAt(t, [fx, fy], [tx, ty]) {
80
+ await t.ready();
81
+ const w = await t.driver.screen();
82
+ const pt = (x, y) => ({ x: Math.round(w.width * x / 100), y: Math.round(w.height * y / 100) });
83
+ await closeSwipe(t, pt(fx, fy), pt(tx, ty));
84
+ }
85
+ /** Closes a sheet from `sel`'s centre to 90% of window height, after readiness. Logs commands only. */
86
+ export async function closeSwipeFrom(t, sel) {
87
+ await t.ready();
88
+ const el = await t.waitVisible(sel);
89
+ const r = await t.driver.cmd('GET', `/element/${el}/rect`);
90
+ const w = await t.driver.screen();
91
+ const x = Math.round(r.x + r.width / 2);
92
+ await closeSwipe(t, { x, y: Math.round(r.y + r.height / 2) }, { x, y: Math.round(w.height * 0.9) });
93
+ }
94
+ /**
95
+ * Taps window `points` in one W3C request, then releases actions (release errors are ignored). Each tap holds for
96
+ * holdMs (100) and pauses gapMs (45). No readiness or element lookup; prepare points before the measured step.
97
+ * Logs commands only; callers may add their own burst summary.
98
+ */
99
+ export async function tapAllAt(t, points, { holdMs = TAP_HOLD_MS, gapMs = BURST_GAP_MS } = {}) {
100
+ await t.driver.cmd('POST', '/actions', { actions: [{ type: 'pointer', id: 'finger1', parameters: { pointerType: 'touch' }, actions: points.flatMap(({ x, y }) => [
101
+ { type: 'pointerMove', duration: 0, x, y }, { type: 'pointerDown', button: 0 }, { type: 'pause', duration: holdMs },
102
+ { type: 'pointerUp', button: 0 }, { type: 'pause', duration: gapMs },
103
+ ]) }] });
104
+ await t.driver.cmd('DELETE', '/actions').catch(() => null);
105
+ }
106
+ /**
107
+ * Scrolls an inverted list using drawn `row` elements. Android delegates to androidScreenScroll. iOS top starts on the
108
+ * lowest row whose visible attribute is 'true' and that ends above `above` (or the lowest drawn row when none does or no
109
+ * `above` is given), chosen by frame, not tree order, dragging to 98% of the screen in 400 ms. End selects the bottommost visible
110
+ * row, clamps its centre inside `list` when found, and drags to 10% in 150 ms. Fails without a drawn row using missingMessage (default 'no drawn row to scroll from'). Logs commands only.
111
+ */
112
+ export async function scrollFromDrawnRow(t, list, row, toward, { missingMessage = 'no drawn row to scroll from', above } = {}) {
113
+ if (t.driver.platform.name !== 'ios') {
114
+ await androidScreenScroll(t, list, toward);
115
+ return;
116
+ }
117
+ await t.ready();
118
+ const { using, value } = t.driver.platform.locators(toSelector(row))[0];
119
+ let start = null;
120
+ if (toward === 'top') {
121
+ // iOS lists rows outside the visible area too, and a drawn row in the list's faded top zone passes touches through:
122
+ // start on the lowest drawn row that ends above `above` (or the lowest drawn row), chosen by frame, never by tree order.
123
+ const limit = above ? (await t.driver.cmd('GET', `/element/${await t.waitVisible(toSelector(above))}/rect`)).y : Infinity;
124
+ let lowest = null;
125
+ for (const el of await t.driver.findAll(using, value)) {
126
+ if ((await t.driver.cmd('GET', `/element/${el}/attribute/visible`)) !== 'true')
127
+ continue;
128
+ const r = await t.driver.cmd('GET', `/element/${el}/rect`);
129
+ if (!lowest || r.y > lowest.y)
130
+ lowest = r;
131
+ if (r.y + r.height <= limit && (!start || r.y > start.y))
132
+ start = r;
133
+ }
134
+ start ??= lowest;
135
+ }
136
+ else {
137
+ for (const el of await t.driver.findAll(using, `${value} AND visible == 1`)) {
138
+ const r = await t.driver.cmd('GET', `/element/${el}/rect`);
139
+ if (!start || r.y > start.y)
140
+ start = r;
141
+ }
142
+ }
143
+ if (!start)
144
+ throw new Error(missingMessage);
145
+ const w = await t.driver.screen();
146
+ const x = Math.round(start.x + start.width / 2);
147
+ let y = Math.round(start.y + start.height / 2);
148
+ if (toward === 'end') {
149
+ const listEl = await t.driver.findShown(toSelector(list));
150
+ if (listEl) {
151
+ const l = await t.driver.cmd('GET', `/element/${listEl}/rect`);
152
+ y = Math.min(Math.max(y, l.y + 1), l.y + l.height - 1);
153
+ }
154
+ }
155
+ await t.driver.drag({ x, y }, { x, y: Math.round(w.height * (toward === 'top' ? ROW_TOP_SCREEN_FRACTION : ROW_END_SCREEN_FRACTION)) }, toward === 'top' ? ROW_TOP_SWIPE_MS : LONG_SCROLL.ios.ms);
156
+ }
157
+ //# sourceMappingURL=gestures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gestures.js","sourceRoot":"","sources":["../../src/gestures.ts"],"names":[],"mappings":"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"}
@@ -7,4 +7,6 @@ export { FLUSH_END_RUN_FAILED, FLUSH_ERROR, FLUSH_OFF, FLUSH_NOT_ACKNOWLEDGED, F
7
7
  export { loadElements, parseTokensJs, plan, type PlannedRun } from './tokens.js';
8
8
  export { runDriver, DRIVER_OPTIONS, type IterationRecord, type RunDeps } from './run.js';
9
9
  export { loadJourneyModule, main } from './load.js';
10
- export { driverVersion, journeyVersion } from './version.js';
10
+ export { DRIVER_NAME, driverVersion, journeyVersion } from './version.js';
11
+ export { longScroll, androidScreenScroll, closeSwipe, closeSwipeAt, closeSwipeFrom, tapAllAt, scrollFromDrawnRow, LONG_SCROLL, LONG_SCROLL_CAP, LONG_SCROLL_PLANNED, IOS_TOP_EXTRA, CLOSE_SWIPE_MS, TAP_HOLD_MS, BURST_GAP_MS, ROW_TOP_SWIPE_MS, ROW_TOP_SCREEN_FRACTION, ROW_END_SCREEN_FRACTION, type ScrollDirection, type Point } from './gestures.js';
12
+ export { rowAlternatives, rowAtOrBelow, scrollUntilShown, waitCoasted, holdBeforeScroll, scrollCap, flingUntilShown, passChecked, scrollToEnd, endChecked, scrollCycles, scrollToTarget, END_SCROLLS, SCROLL_CYCLES, PAGE_PAUSE_MS, PAGE_WAIT_MS, PAGE_POLL_MS, PAGE_COAST_MAX_MS, PAGE_COAST_MAX_MS_IOS, COAST_POLL_MS, COAST_MAX_MS, LOOK_MS, LOOK_POLL_MS, CHECK_TIMEOUT_MS, TARGET_SETTLE_MS, type RowField, type ListHandle, type RowHold, type ListCache, type UntilShownOptions, type FlingUntilShownOptions, type ScrollToEndOptions, type EndCheckOptions, type PassCheckOptions, type ScrollCyclesOptions, type ScrollToTargetOptions } from './lists.js';
package/dist/src/index.js CHANGED
@@ -7,5 +7,7 @@ export { FLUSH_END_RUN_FAILED, FLUSH_ERROR, FLUSH_OFF, FLUSH_NOT_ACKNOWLEDGED, F
7
7
  export { loadElements, parseTokensJs, plan } from './tokens.js';
8
8
  export { runDriver, DRIVER_OPTIONS } from './run.js';
9
9
  export { loadJourneyModule, main } from './load.js';
10
- export { driverVersion, journeyVersion } from './version.js';
10
+ export { DRIVER_NAME, driverVersion, journeyVersion } from './version.js';
11
+ export { longScroll, androidScreenScroll, closeSwipe, closeSwipeAt, closeSwipeFrom, tapAllAt, scrollFromDrawnRow, LONG_SCROLL, LONG_SCROLL_CAP, LONG_SCROLL_PLANNED, IOS_TOP_EXTRA, CLOSE_SWIPE_MS, TAP_HOLD_MS, BURST_GAP_MS, ROW_TOP_SWIPE_MS, ROW_TOP_SCREEN_FRACTION, ROW_END_SCREEN_FRACTION } from './gestures.js';
12
+ export { rowAlternatives, rowAtOrBelow, scrollUntilShown, waitCoasted, holdBeforeScroll, scrollCap, flingUntilShown, passChecked, scrollToEnd, endChecked, scrollCycles, scrollToTarget, END_SCROLLS, SCROLL_CYCLES, PAGE_PAUSE_MS, PAGE_WAIT_MS, PAGE_POLL_MS, PAGE_COAST_MAX_MS, PAGE_COAST_MAX_MS_IOS, COAST_POLL_MS, COAST_MAX_MS, LOOK_MS, LOOK_POLL_MS, CHECK_TIMEOUT_MS, TARGET_SETTLE_MS } from './lists.js';
11
13
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAmD,MAAM,gBAAgB,CAAC;AAC7G,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAkB,MAAM,YAAY,CAAC;AAC5J,OAAO,EAAE,aAAa,EAA2D,MAAM,cAAc,CAAC;AACtG,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,UAAU,EAAoC,MAAM,eAAe,CAAC;AACtG,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,gBAAgB,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAoC,MAAM,gBAAgB,CAAC;AACrJ,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,SAAS,EAAE,sBAAsB,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAsB,gBAAgB,EAAE,OAAO,EAAE,WAAW,EAA8D,MAAM,cAAc,CAAC;AAC9Q,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,IAAI,EAAmB,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,cAAc,EAAsC,MAAM,UAAU,CAAC;AACzF,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAmD,MAAM,gBAAgB,CAAC;AAC7G,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAkB,MAAM,YAAY,CAAC;AAC5J,OAAO,EAAE,aAAa,EAA2D,MAAM,cAAc,CAAC;AACtG,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,UAAU,EAAoC,MAAM,eAAe,CAAC;AACtG,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,gBAAgB,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAoC,MAAM,gBAAgB,CAAC;AACrJ,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,SAAS,EAAE,sBAAsB,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAsB,gBAAgB,EAAE,OAAO,EAAE,WAAW,EAA8D,MAAM,cAAc,CAAC;AAC9Q,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,IAAI,EAAmB,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,cAAc,EAAsC,MAAM,UAAU,CAAC;AACzF,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,kBAAkB,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,uBAAuB,EAAoC,MAAM,eAAe,CAAC;AAC3V,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,aAAa,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAiP,MAAM,YAAY,CAAC"}
@@ -0,0 +1,182 @@
1
+ import type { Session } from './session.js';
2
+ import { type SelectorLike } from './selectors.js';
3
+ import type { Rect } from './webdriver.js';
4
+ import { type ScrollDirection } from './gestures.js';
5
+ /** Default fixed end-pass gesture count. */
6
+ export declare const END_SCROLLS = 3;
7
+ /** Default end/top cycle count. */
8
+ export declare const SCROLL_CYCLES = 3;
9
+ /** Pause after an end scroll with no page marker. */
10
+ export declare const PAGE_PAUSE_MS = 500;
11
+ /** Page-load budget measured after coasting stops. */
12
+ export declare const PAGE_WAIT_MS = 3000;
13
+ export declare const PAGE_POLL_MS = 300;
14
+ /** Android page-loading coast bound. */
15
+ export declare const PAGE_COAST_MAX_MS = 6000;
16
+ /** iOS page-loading coast bound. */
17
+ export declare const PAGE_COAST_MAX_MS_IOS = 10000;
18
+ export declare const COAST_POLL_MS = 300;
19
+ /** Default non-page coast bound; a timeout returns false rather than failing. */
20
+ export declare const COAST_MAX_MS = 3000;
21
+ export declare const LOOK_MS = 1000;
22
+ export declare const LOOK_POLL_MS = 100;
23
+ /** Default settled-frame and pass-check timeout. */
24
+ export declare const CHECK_TIMEOUT_MS = 20000;
25
+ /** Initial visibility wait before trying an indicator or fallback scroll. */
26
+ export declare const TARGET_SETTLE_MS = 10000;
27
+ export type RowField = 'text' | 'description';
28
+ export interface ListHandle {
29
+ sel: SelectorLike;
30
+ el?: string;
31
+ }
32
+ export interface RowHold {
33
+ el?: string | null;
34
+ }
35
+ /** Mutable frame/row cache for one visit to a list; share it with pass checks to share the gesture budget. */
36
+ export interface ListCache {
37
+ at?: {
38
+ el: string;
39
+ rect: Rect;
40
+ };
41
+ hold?: RowHold;
42
+ used?: number;
43
+ }
44
+ export interface UntilShownOptions {
45
+ cmd: string;
46
+ field: string;
47
+ cap: number;
48
+ blind?: number;
49
+ planned?: number;
50
+ settle?: () => Promise<unknown>;
51
+ look: () => Promise<boolean>;
52
+ move: () => Promise<unknown>;
53
+ missing: (cap: number) => string;
54
+ extra?: () => Record<string, unknown>;
55
+ lookWindowMs?: number;
56
+ lookPollMs?: number;
57
+ }
58
+ export interface FlingUntilShownOptions {
59
+ scoped?: boolean;
60
+ blind?: number;
61
+ cache?: ListCache;
62
+ row?: SelectorLike;
63
+ used?: number;
64
+ cap?: number;
65
+ planned?: number;
66
+ iosTopExtra?: number;
67
+ frameTimeoutMs?: number;
68
+ coastMaxMs?: number;
69
+ lookWindowMs?: number;
70
+ lookPollMs?: number;
71
+ }
72
+ export interface ScrollToEndOptions {
73
+ pages: readonly SelectorLike[];
74
+ /** Values for the summary's pages field; defaults to selector descriptions. */
75
+ pageNumbers?: readonly number[];
76
+ beforePages?: readonly SelectorLike[];
77
+ scoped?: boolean;
78
+ field?: RowField;
79
+ any: SelectorLike;
80
+ scrolls?: number;
81
+ pauseMs?: number;
82
+ pageWaitMs?: number;
83
+ pagePollMs?: number;
84
+ coastMaxMs?: number;
85
+ coastMaxMsIos?: number;
86
+ frameTimeoutMs?: number;
87
+ }
88
+ export interface EndCheckOptions {
89
+ checkTimeoutMs?: number;
90
+ pagePollMs?: number;
91
+ scrolls?: number;
92
+ }
93
+ export interface PassCheckOptions extends Omit<FlingUntilShownOptions, 'cache' | 'scoped' | 'row' | 'used'> {
94
+ checkTimeoutMs?: number;
95
+ }
96
+ export interface ScrollCyclesOptions extends ScrollToEndOptions {
97
+ checkTimeoutMs?: number;
98
+ list: SelectorLike;
99
+ first: SelectorLike;
100
+ lastPage: SelectorLike;
101
+ cycles?: number;
102
+ stepName: (toward: ScrollDirection, cycle: number) => string;
103
+ fling?: PassCheckOptions;
104
+ }
105
+ export interface ScrollToTargetOptions {
106
+ target: SelectorLike;
107
+ row: SelectorLike;
108
+ indicator?: SelectorLike;
109
+ move: () => Promise<unknown>;
110
+ settleMs?: number;
111
+ cap?: number;
112
+ planned?: number;
113
+ cmd?: string;
114
+ missing?: (cap: number) => string;
115
+ }
116
+ /**
117
+ * Looks before each move until shown, failing at cap. Blind moves run first. A post-move look retries until a lookup
118
+ * begins at least lookWindowMs (1000) after it; lookPollMs defaults to 100. Optional settle runs before those looks.
119
+ * Logs cmd with the gesture count under field, blind/cap, looks/lookMs, status, duration, optional extra and planned overrun.
120
+ */
121
+ export declare function scrollUntilShown(t: Session, { cmd, field, cap, blind, planned, settle, look: once, move, missing, extra, lookWindowMs, lookPollMs }: UntilShownOptions): Promise<number>;
122
+ /**
123
+ * Captures a drawn row before a scroll when hold is provided and empty; lookup failures clear the hold. Use for iOS
124
+ * where visible-row queries during momentum can consume the coast budget. Logs lookup commands only.
125
+ */
126
+ export declare function holdBeforeScroll(t: Session, row: SelectorLike, list?: ListHandle, field?: RowField, hold?: RowHold): Promise<void>;
127
+ /**
128
+ * Waits for two matching row frames 300 ms apart, up to maxMs (3000), always attempting at least two reads.
129
+ * Optional list scopes the row and is re-found at most twice on missing/stale reads; field selects an Android text
130
+ * attribute. An optional hold reuses one row across calls until stale. Returns false when still moving. Logs commands only.
131
+ */
132
+ export declare function waitCoasted(t: Session, row: SelectorLike, list?: ListHandle, field?: RowField, maxMs?: number, hold?: RowHold): Promise<boolean>;
133
+ /** Platform gesture budget: cap (3), plus iosTopExtra (2) only when returning to the top on iOS. No commands or logs. */
134
+ export declare const scrollCap: (t: Session, toward: ScrollDirection, cap?: number, iosTopExtra?: number) => number;
135
+ /**
136
+ * Long-scrolls list toward a partly visible target; scoped defaults true. Refetches missing/stale list handles before
137
+ * moving and rereads the frame before every gesture. A shared cache stores the settled frame, held iOS row and moves used.
138
+ * Defaults: 3 gestures (5 to top on iOS), 2 planned, 20 s frameTimeoutMs, 3 s coast. used subtracts the prior pass budget.
139
+ * Logs fling-to-end/top with flings, blind/cap, looks/lookMs, extra, relists, settled, status and duration, including failures.
140
+ */
141
+ export declare function flingUntilShown(t: Session, list: SelectorLike, target: SelectorLike, toward: ScrollDirection, { scoped, blind, cache, row, used, cap, planned, iosTopExtra, frameTimeoutMs, coastMaxMs, lookWindowMs, lookPollMs }?: FlingUntilShownOptions): Promise<void>;
142
+ /**
143
+ * Creates a step check. On its first call only, a missing target triggers a re-scroll sharing cache.used; subsequent calls
144
+ * only wait for visibility (checkTimeoutMs defaults 20000). Logs re-scrolled with toward plus the nested fling summary.
145
+ * The re-scrolled event carries no ms/status fields. options overrides the original pass's fling settings.
146
+ */
147
+ export declare function passChecked(t: Session, list: SelectorLike, target: SelectorLike, toward: ScrollDirection, scoped: boolean, cache: ListCache, row?: SelectorLike, options?: PassCheckOptions): () => Promise<void>;
148
+ /**
149
+ * Performs scrolls (3) end gestures. A gesture with no page marker pauses pauseMs (500). Otherwise it waits for coast
150
+ * (Android 6000 ms, iOS 10000), optionally adds one gesture if beforePages was not reached, then waits pageWaitMs (3000)
151
+ * for the page's first loaded row, polling every pagePollMs (300). scoped defaults true; field narrows Android lookups.
152
+ * Logs fling-to-end: flings, supplied pageNumbers (or descriptions), coastMs, waitMs, per-page extra, pauseMs, settled,
153
+ * status and duration. Page checks do not require visibility. All limits and frameTimeoutMs (20000) are configurable.
154
+ */
155
+ export declare function scrollToEnd(t: Session, list: SelectorLike, cache: ListCache, { pages, pageNumbers, beforePages, scoped, field, any, scrolls, pauseMs, pageWaitMs, pagePollMs, coastMaxMs, coastMaxMsIos, frameTimeoutMs }: ScrollToEndOptions): Promise<void>;
156
+ /**
157
+ * Creates an end-pass check requiring a loaded lastPage row, even off screen. Retries a stale lookup once, then polls
158
+ * every 300 ms for up to 20000 ms. checkTimeoutMs, pagePollMs and scrolls override the defaults in errors. Logs commands only.
159
+ */
160
+ export declare function endChecked(t: Session, list: SelectorLike, lastPage: SelectorLike, scoped: boolean, field?: RowField, { checkTimeoutMs, pagePollMs, scrolls }?: EndCheckOptions): () => Promise<void>;
161
+ /**
162
+ * Runs cycles (3) end/top measured step pairs with names from stepName(toward, oneBasedCycle). Only cycle one waits for
163
+ * page markers; every end check requires lastPage loaded. Shares frame/row cache and each top pass's budget with its
164
+ * check. frameTimeoutMs feeds the end frame wait; checkTimeoutMs feeds its end check. fling configures top passes/checks. Logs step markers and nested helper summaries.
165
+ */
166
+ export declare function scrollCycles(t: Session, { list, first, lastPage, cycles, stepName, fling, ...options }: ScrollCyclesOptions): Promise<void>;
167
+ /**
168
+ * Waits settleMs (10000) for target, then taps an optional indicator and/or runs move until visible, at most cap (3)
169
+ * times, coasting on row after each action. Logs cmd (scroll-to-target) with tapped, swipes, extra above planned (2),
170
+ * status and duration. An initially visible target returns without a summary. missing customizes the failure message.
171
+ */
172
+ export declare function scrollToTarget(t: Session, { target, indicator, row, move, settleMs, cap, planned, cmd, missing }: ScrollToTargetOptions): Promise<void>;
173
+ /**
174
+ * Builds a regex alternation from the inclusive from/to row numbers. The journey supplies row's formatting and regex
175
+ * escaping; an empty range returns ''. No commands or logs.
176
+ */
177
+ export declare const rowAlternatives: (from: number, to: number, row: (n: number) => string) => string;
178
+ /**
179
+ * Whether row is absent from the screen or its top is at/below boundary's top. Reads boundary first, then finds row and
180
+ * compares its frame. Use as a custom scrollUntilShown look for inverted lists. No readiness wait; logs commands only.
181
+ */
182
+ export declare function rowAtOrBelow(t: Session, row: SelectorLike, boundary: SelectorLike): Promise<boolean>;