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

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.
@@ -0,0 +1,330 @@
1
+ import { describeSelector, toSelector } from './selectors.js';
2
+ import { LONG_SCROLL, ROW_END_SCREEN_FRACTION, ROW_TOP_SCREEN_FRACTION, ROW_TOP_SWIPE_MS } from './gestures.js';
3
+ export const COAST_POLL_MS = 300;
4
+ /** Default non-page coast bound; a timeout returns false rather than failing. */
5
+ export const COAST_MAX_MS = 3_000;
6
+ /**
7
+ * UiAutomator's fling waits up to 5 s (UiObject2.FLING_TIMEOUT) for a view-scrolled event and answers false when none
8
+ * came, so a false that took at least this long means the list did not move.
9
+ */
10
+ export const FLING_UNMOVED_MS = 4_500;
11
+ /** A checked list gesture moved nothing twice: the device took no scroll. Never a page or target failure. */
12
+ export class GestureNotTaken extends Error {
13
+ name = 'GestureNotTaken';
14
+ }
15
+ /**
16
+ * The one way the driver scrolls a list: reads the list frame (from `at`, the pass cache or a fresh lookup), optionally holds a
17
+ * drawn row, then sends the platform's fixed gesture. A list gesture is a native Android fling at 12000 dp/s over 30–85% of the
18
+ * list, or an iOS 150 ms drag over 85→10% (end) or 30→95% (top). A `screen` gesture is the Android fling from the list out to
19
+ * the window at 3000 dp/s, or the iOS drag from a drawn row (see scrollFromDrawnRow).
20
+ *
21
+ * With `mustMove` (and `list` and `row`) the gesture is checked. It is suspect when Android UiAutomator answered false after
22
+ * FLING_UNMOVED_MS, or when the held iOS row reads the same frame right before and right after it. A suspect gesture waits for
23
+ * the list to be still and reads the ids of the rows inside the list's frame; when the rows leave no room toward `toward` it is
24
+ * `at-edge`. Otherwise it is sent once more, identical, counted in `t.defects`, and the ids after it decide: changed is
25
+ * `moved`, the same throws GestureNotTaken. Healthy Android flings add no command; a checked iOS drag adds two frame reads of
26
+ * the held row. Logs WebDriver commands and defects only.
27
+ */
28
+ export async function scrollList(t, spec, toward, { at, cache, hold, mustMove = false, coastMaxMs = COAST_MAX_MS } = {}) {
29
+ if (spec.screen) {
30
+ if (t.driver.platform.name === 'ios' && spec.row !== undefined)
31
+ await drawnRowDrag(t, spec, toward);
32
+ else
33
+ await screenFling(t, spec.list, toward);
34
+ return { outcome: 'moved', retried: false, sentAt: t.now() };
35
+ }
36
+ if (at)
37
+ return { ...(await listGesture(t, at, toward)), retried: false };
38
+ const c = cache ?? {};
39
+ const checked = mustMove && spec.row !== undefined;
40
+ const ios = t.driver.platform.name === 'ios';
41
+ const held = checked && ios ? (hold ?? (c.hold ??= {})) : hold;
42
+ const send = async (first) => {
43
+ c.at = await t.frameOf(spec.list, c.at?.el ?? (await t.waitVisible(spec.list)));
44
+ if (first && held)
45
+ await holdBeforeScroll(t, spec.row, handle(spec, c), spec.field, held);
46
+ const before = checked && ios ? await heldFrame(t, held) : null;
47
+ const g = await listGesture(t, c.at.rect, toward);
48
+ const after = before ? await heldFrame(t, held) : null;
49
+ const suspect = ios ? before !== null && before === after : g.outcome === 'unmoved';
50
+ return { ...g, suspect };
51
+ };
52
+ const first = await send(true);
53
+ if (!checked)
54
+ return { outcome: first.outcome, answer: first.answer, answerMs: first.answerMs, sentAt: first.sentAt, retried: false };
55
+ if (!first.suspect)
56
+ return { outcome: 'moved', answer: first.answer, answerMs: first.answerMs, sentAt: first.sentAt, retried: false };
57
+ await waitCoasted(t, spec.row, handle(spec, c), spec.field, coastMaxMs, held);
58
+ const before = await rowsInList(t, spec, c);
59
+ if (!room(t, before, toward))
60
+ return { outcome: 'at-edge', answer: first.answer, answerMs: first.answerMs, sentAt: first.sentAt, retried: false };
61
+ const label = listLabel(spec.list);
62
+ const defect = {
63
+ ...t.driver.context, step: t.driver.context.step ?? null, t: t.now(), list: label, toward, reason: 'unmoved',
64
+ before: before.ids, after: null, answer: ios ? null : first.answer, answerMs: ios ? null : (first.answerMs ?? null),
65
+ };
66
+ t.defects.gestureRetry++;
67
+ t.defects.items.push(defect);
68
+ const again = await send(false);
69
+ await waitCoasted(t, spec.row, handle(spec, c), spec.field, coastMaxMs, held);
70
+ const after = await rowsInList(t, spec, c);
71
+ defect.after = after.ids;
72
+ if (after.ids.join('\n') === before.ids.join('\n'))
73
+ throw new GestureNotTaken(`list ${label} did not move after 2 identical gestures (the device took no scroll)`);
74
+ return { outcome: 'moved', answer: again.answer, answerMs: again.answerMs, sentAt: again.sentAt, retried: true };
75
+ }
76
+ const handle = (spec, c) => ((spec.scoped ?? true) ? { sel: spec.list, el: c.at?.el } : undefined);
77
+ const listLabel = (list) => {
78
+ const s = toSelector(list);
79
+ return s.id ?? s.text ?? describeSelector(s);
80
+ };
81
+ /** The held row's frame as text, or null when there is none or it went stale (the hold is then dropped). */
82
+ async function heldFrame(t, hold) {
83
+ if (!hold.el)
84
+ return null;
85
+ try {
86
+ const r = await t.driver.cmd('GET', `/element/${hold.el}/rect`);
87
+ return `${r.x},${r.y},${r.width},${r.height}`;
88
+ }
89
+ catch {
90
+ hold.el = null;
91
+ return null;
92
+ }
93
+ }
94
+ /**
95
+ * The rows `spec.row` matches through the list's handle: their frames, and the ids (text, iOS label; or the resource id / name
96
+ * of an id selector) of those with a part inside the list's frame, top to bottom. Never asks iOS `visible`. Logs commands only.
97
+ */
98
+ async function rowsInList(t, spec, c) {
99
+ c.at = await t.frameOf(spec.list, c.at?.el ?? (await t.waitVisible(spec.list)));
100
+ const list = c.at.rect;
101
+ const { childOf: _list, ...sel } = toSelector(spec.row);
102
+ const all = t.driver.platform.locators(sel);
103
+ const android = t.driver.platform.name === 'android';
104
+ const call = spec.field === 'description' ? /descriptionMatches/ : /textMatches/;
105
+ const own = android && spec.field ? all.filter((l) => call.test(l.value)) : all;
106
+ const attr = sel.text !== undefined ? (android ? (spec.field === 'description' ? 'content-desc' : 'text') : 'label') : android ? 'resource-id' : 'name';
107
+ const frames = [];
108
+ const inside = [];
109
+ const parent = (spec.scoped ?? true) ? c.at.el : undefined;
110
+ for (const { using, value } of own.length ? own : all) {
111
+ for (const el of await t.driver.findAll(using, value, parent)) {
112
+ let r;
113
+ try {
114
+ r = await t.driver.cmd('GET', `/element/${el}/rect`);
115
+ }
116
+ catch {
117
+ continue;
118
+ }
119
+ if (r.width <= 0 || r.height <= 0)
120
+ continue;
121
+ frames.push(r);
122
+ if (r.y < list.y + list.height && r.y + r.height > list.y)
123
+ inside.push({ y: r.y, el });
124
+ }
125
+ if (frames.length)
126
+ break;
127
+ }
128
+ const ids = [];
129
+ for (const { el } of inside.sort((a, b) => a.y - b.y))
130
+ ids.push(String((await t.driver.attribute(el, attr).catch(() => null)) ?? ''));
131
+ return { ids, frames, list };
132
+ }
133
+ /**
134
+ * Whether the rows leave the list room toward `toward`. iOS reports full frames: some row reaches past that edge of the list.
135
+ * Android reports frames clipped to the list: a row touches that edge. No rows: no room.
136
+ */
137
+ function room(t, { frames, list }, toward) {
138
+ if (frames.length === 0)
139
+ return false;
140
+ const android = t.driver.platform.name === 'android';
141
+ const bottom = list.y + list.height;
142
+ if (toward === 'end') {
143
+ const reach = Math.max(...frames.map((r) => r.y + r.height));
144
+ return android ? reach >= bottom - 1 : reach > bottom;
145
+ }
146
+ const reach = Math.min(...frames.map((r) => r.y));
147
+ return android ? reach <= list.y + 1 : reach < list.y;
148
+ }
149
+ async function listGesture(t, r, toward) {
150
+ await t.ready();
151
+ if (t.driver.platform.name === 'android') {
152
+ const { from, to, dpPerSecond } = LONG_SCROLL.android;
153
+ const top = Math.round(r.y + r.height * from);
154
+ const bottom = Math.min(Math.round(r.y + r.height * to), await t.driver.safeBottomY());
155
+ const speed = Math.round(dpPerSecond * (await t.driver.density()));
156
+ const sent = t.now();
157
+ const answer = await t.driver.execute('mobile: flingGesture', {
158
+ left: r.x, top, width: r.width, height: bottom - top,
159
+ direction: toward === 'end' ? 'down' : 'up', speed,
160
+ });
161
+ const answerMs = t.now() - sent;
162
+ return { outcome: answer === false && answerMs >= FLING_UNMOVED_MS ? 'unmoved' : 'moved', answer, answerMs, sentAt: t.now() };
163
+ }
164
+ const [from, to] = LONG_SCROLL.ios[toward];
165
+ const x = Math.round(r.x + r.width / 2);
166
+ 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);
167
+ return { outcome: 'moved', sentAt: t.now() };
168
+ }
169
+ async function screenFling(t, list, toward) {
170
+ await t.ready();
171
+ const r = (await t.frameOf(list, await t.waitVisible(list))).rect;
172
+ const w = await t.driver.screen();
173
+ const [inList, onScreen] = LONG_SCROLL.androidScreen[toward];
174
+ const start = Math.round(r.y + r.height * inList);
175
+ const end = Math.round(w.height * onScreen);
176
+ const limit = await t.driver.safeBottomY();
177
+ const top = Math.min(start, end);
178
+ const bottom = Math.max(top, Math.min(Math.max(start, end), limit));
179
+ await t.driver.execute('mobile: flingGesture', {
180
+ left: r.x, top, width: r.width, height: bottom - top,
181
+ direction: toward === 'top' ? 'up' : 'down', speed: Math.round(LONG_SCROLL.androidScreen.dpPerSecond * (await t.driver.density())),
182
+ });
183
+ }
184
+ async function drawnRowDrag(t, { list, row, above, missingMessage = 'no drawn row to scroll from' }, toward) {
185
+ await t.ready();
186
+ const { using, value } = t.driver.platform.locators(toSelector(row))[0];
187
+ let start = null;
188
+ if (toward === 'top') {
189
+ // iOS lists rows outside the visible area too, and a drawn row in the list's faded top zone passes touches through:
190
+ // start on the lowest drawn row that ends above `above` (or the lowest drawn row), chosen by frame, never by tree order.
191
+ const limit = above ? (await t.driver.cmd('GET', `/element/${await t.waitVisible(toSelector(above))}/rect`)).y : Infinity;
192
+ let lowest = null;
193
+ for (const el of await t.driver.findAll(using, value)) {
194
+ if ((await t.driver.cmd('GET', `/element/${el}/attribute/visible`)) !== 'true')
195
+ continue;
196
+ const r = await t.driver.cmd('GET', `/element/${el}/rect`);
197
+ if (!lowest || r.y > lowest.y)
198
+ lowest = r;
199
+ if (r.y + r.height <= limit && (!start || r.y > start.y))
200
+ start = r;
201
+ }
202
+ start ??= lowest;
203
+ }
204
+ else {
205
+ for (const el of await t.driver.findAll(using, `${value} AND visible == 1`)) {
206
+ const r = await t.driver.cmd('GET', `/element/${el}/rect`);
207
+ if (!start || r.y > start.y)
208
+ start = r;
209
+ }
210
+ }
211
+ if (!start)
212
+ throw new Error(missingMessage);
213
+ const w = await t.driver.screen();
214
+ const x = Math.round(start.x + start.width / 2);
215
+ let y = Math.round(start.y + start.height / 2);
216
+ if (toward === 'end') {
217
+ const listEl = await t.driver.findShown(toSelector(list));
218
+ if (listEl) {
219
+ const l = await t.driver.cmd('GET', `/element/${listEl}/rect`);
220
+ y = Math.min(Math.max(y, l.y + 1), l.y + l.height - 1);
221
+ }
222
+ }
223
+ 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);
224
+ }
225
+ /**
226
+ * One long scroll inside window rectangle `r` (scrollList's list gesture), after readiness. Resolves false only for an
227
+ * Android fling that moved nothing (FLING_UNMOVED_MS); iOS resolves true. Logs WebDriver commands only.
228
+ */
229
+ export async function longScroll(t, r, toward) {
230
+ return (await scrollList(t, {}, toward, { at: r })).outcome !== 'unmoved';
231
+ }
232
+ /**
233
+ * Android inverted-list scroll extending from `list` to the window at 3000 dp/s. Top: 10% of the list to 95% of the
234
+ * window, direction up; end: 90% to 10%, direction down. Reads the list frame after readiness; logs commands only.
235
+ */
236
+ export async function androidScreenScroll(t, list, toward) {
237
+ await scrollList(t, { list, screen: true }, toward);
238
+ }
239
+ /**
240
+ * Scrolls an inverted list using drawn `row` elements. Android delegates to androidScreenScroll. iOS top starts on the
241
+ * lowest row whose visible attribute is 'true' and that ends above `above` (or the lowest drawn row when none does or no
242
+ * `above` is given), chosen by frame, not tree order, dragging to 98% of the screen in 400 ms. End selects the bottommost visible
243
+ * 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.
244
+ */
245
+ export async function scrollFromDrawnRow(t, list, row, toward, { missingMessage, above } = {}) {
246
+ await scrollList(t, { list, row, screen: true, above, missingMessage }, toward);
247
+ }
248
+ /** First drawn row; Android can restrict its text attribute, iOS predicates ask visible == 1. Logs lookups only. */
249
+ async function firstDrawn(t, row, listEl, field, scopedOrField) {
250
+ if (!scopedOrField) {
251
+ return t.driver.findShown(toSelector(row));
252
+ }
253
+ const { childOf: _list, ...inList } = toSelector(row);
254
+ const all = t.driver.platform.locators(inList);
255
+ const call = field === 'text' ? /textMatches/ : /descriptionMatches/;
256
+ const own = t.driver.platform.name === 'android' && field ? all.filter(l => call.test(l.value)) : all;
257
+ for (const { using, value } of own.length ? own : all) {
258
+ const drawn = t.driver.platform.name === 'ios' && using === '-ios predicate string' ? `${value} AND visible == 1` : value;
259
+ const found = await t.driver.find(using, drawn, listEl);
260
+ if (found) {
261
+ return found;
262
+ }
263
+ }
264
+ return null;
265
+ }
266
+ /**
267
+ * Captures a drawn row before a scroll when hold is provided and empty; lookup failures clear the hold. Use for iOS
268
+ * where visible-row queries during momentum can consume the coast budget. Logs lookup commands only.
269
+ */
270
+ export async function holdBeforeScroll(t, row, list, field, hold) {
271
+ if (!hold || hold.el) {
272
+ return;
273
+ }
274
+ try {
275
+ hold.el = await firstDrawn(t, row, list?.el, field, Boolean(list || field));
276
+ }
277
+ catch {
278
+ hold.el = null;
279
+ }
280
+ }
281
+ /**
282
+ * Waits for two matching row frames 300 ms apart, up to maxMs (3000), always attempting at least two reads.
283
+ * Optional list scopes the row and is re-found at most twice on missing/stale reads; field selects an Android text
284
+ * attribute. An optional hold reuses one row across calls until stale. Returns false when still moving. Logs commands only.
285
+ */
286
+ export async function waitCoasted(t, row, list, field, maxMs = COAST_MAX_MS, hold) {
287
+ const until = t.now() + maxMs;
288
+ let el = list?.el;
289
+ let refinds = 0;
290
+ const read = async () => {
291
+ if (hold?.el) {
292
+ try {
293
+ return await t.driver.cmd('GET', `/element/${hold.el}/rect`);
294
+ }
295
+ catch {
296
+ hold.el = null;
297
+ }
298
+ }
299
+ const found = await firstDrawn(t, row, el, field, Boolean(list || field));
300
+ if (found && hold) {
301
+ hold.el = found;
302
+ }
303
+ return found ? t.driver.cmd('GET', `/element/${found}/rect`) : null;
304
+ };
305
+ let last = null;
306
+ let reads = 0;
307
+ for (;;) {
308
+ let r = null;
309
+ reads++;
310
+ try {
311
+ r = await read();
312
+ }
313
+ catch {
314
+ r = null;
315
+ }
316
+ if (r === null && list && refinds < 2) {
317
+ refinds++;
318
+ el = await t.waitVisible(list.sel);
319
+ continue;
320
+ }
321
+ const frame = r && `${r.x},${r.y},${r.width},${r.height}`;
322
+ if (frame && frame === last)
323
+ return true;
324
+ if (t.now() >= until && reads >= 2)
325
+ return false;
326
+ last = frame;
327
+ await t.driver.sleep(COAST_POLL_MS);
328
+ }
329
+ }
330
+ //# sourceMappingURL=scroll.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll.js","sourceRoot":"","sources":["../../src/scroll.ts"],"names":[],"mappings":"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"}
@@ -31,6 +31,8 @@ export interface SendOptions {
31
31
  driverVersion?: () => string | null;
32
32
  /** Raw new-session UDID, reported as evidence without changing the judged device. */
33
33
  deviceUnit?: string;
34
+ /** The session's BrowserStack capture setting (video and device logs), reported so runs with and without it are told apart. */
35
+ capture?: 'on' | 'off';
34
36
  /** Every run token the session opened: one with no marker is told apart on /v1/clock, so no run stays at the API's guess. */
35
37
  tokens?: readonly string[];
36
38
  /** Tokens already reported, or whose standalone report exhausted its retry sequence; kept across the session's runs. */
@@ -60,6 +62,12 @@ export declare const FLUSH_RESEND_MS = 90000;
60
62
  */
61
63
  export declare const ACTIVATION_SETTLE_MS = 2000;
62
64
  export declare const FLUSH_NOT_ACKNOWLEDGED = "not acknowledged (re-sent once)";
65
+ /**
66
+ * What a flush that answered `error` lost, read from the SDK's detail (packages/react-native js/sdk/core.ts `flushOnce` and
67
+ * push.ts `flushAll`; the driver cannot import the SDK): lines not sent or dropped, line batches the ingest refused, profile
68
+ * files not uploaded, commit-stream segments not written. The detail itself when it names none of them.
69
+ */
70
+ export declare const flushLoss: (detail: string | null | undefined) => string;
63
71
  /** The flush of one run, host clock: when the link went, when it went again, when the SDK's ack was read or the driver gave up. */
64
72
  export interface FlushEvidence {
65
73
  /** null: the end-run failed before its link went. */
@@ -108,8 +116,25 @@ export interface SessionOptions {
108
116
  * memory reading after a GC (REQ-MEAS-35). On by default (`END_GC_DEFAULT`); the journey should end on its home screen.
109
117
  */
110
118
  endGc?: boolean;
119
+ /** The session's capture setting, reported with the driver on each run's first request; none when the caller does not say. */
120
+ capture?: 'on' | 'off';
111
121
  }
112
122
  /** One declared step as the driver log keeps it: `ms` is null while the step is still running. */
123
+ /** A list gesture that moved nothing although the list had room, sent once more. `before`/`after`: row ids inside the list's frame around the retry. */
124
+ export interface GestureDefect {
125
+ journey?: string;
126
+ iteration?: number;
127
+ step: string | null;
128
+ t: number;
129
+ list: string;
130
+ toward: 'end' | 'top';
131
+ reason: 'unmoved';
132
+ before: string[];
133
+ after: string[] | null;
134
+ /** UiAutomator's fling answer and how long it took (Android); null on iOS. */
135
+ answer: unknown;
136
+ answerMs: number | null;
137
+ }
113
138
  export interface StepRecord {
114
139
  journey: unknown;
115
140
  iteration: unknown;
@@ -137,12 +162,18 @@ export declare class Session {
137
162
  readonly readyWaits: number[];
138
163
  actionGc: ActionGc;
139
164
  readonly endGc: boolean;
165
+ readonly capture: 'on' | 'off' | undefined;
140
166
  /** Unique for the whole session; the SDK keeps its answers by run token and n. */
141
167
  gcN: number;
142
168
  /** Steps open right now: a step inside another step's action sends no GC, which would land inside the outer step. */
143
169
  stepDepth: number;
144
170
  /** Every declared step in start order, for the driver log. */
145
171
  readonly stepLog: StepRecord[];
172
+ /** Harness defects over the session, for the driver log: every gesture the device did not take and the driver sent again. */
173
+ readonly defects: {
174
+ gestureRetry: number;
175
+ items: GestureDefect[];
176
+ };
146
177
  /** Called when a top-level step starts and when it ends, outside its start and end markers (the driver log is rewritten then). */
147
178
  onStepChange: (() => void) | null;
148
179
  /** Elements looked up again after going stale, over the session (see `frameOf`). */
@@ -77,7 +77,7 @@ export async function sendMarkers(fetchImpl, api, markers, opts = {}) {
77
77
  const url = `${base}/v1/markers`;
78
78
  const version = (opts.driverVersion ?? ownVersion)();
79
79
  const reported = opts.reported ?? new Set();
80
- const driverField = { driver: { name: DRIVER_NAME, ...(version ? { version } : {}), ...(opts.deviceUnit ? { unit: opts.deviceUnit.slice(0, 200) } : {}) } };
80
+ const driverField = { driver: { name: DRIVER_NAME, ...(version ? { version } : {}), ...(opts.deviceUnit ? { unit: opts.deviceUnit.slice(0, 200) } : {}), ...(opts.capture ? { capture: opts.capture } : {}) } };
81
81
  let sent = 0;
82
82
  for (const [token, list] of byToken) {
83
83
  // A trip belongs to the run whose token carried it.
@@ -150,6 +150,31 @@ export const FLUSH_RESEND_MS = 90_000;
150
150
  */
151
151
  export const ACTIVATION_SETTLE_MS = 2000;
152
152
  export const FLUSH_NOT_ACKNOWLEDGED = 'not acknowledged (re-sent once)';
153
+ /**
154
+ * What a flush that answered `error` lost, read from the SDK's detail (packages/react-native js/sdk/core.ts `flushOnce` and
155
+ * push.ts `flushAll`; the driver cannot import the SDK): lines not sent or dropped, line batches the ingest refused, profile
156
+ * files not uploaded, commit-stream segments not written. The detail itself when it names none of them.
157
+ */
158
+ export const flushLoss = (detail) => {
159
+ if (!detail)
160
+ return 'no detail';
161
+ const count = (re) => Number(re.exec(detail)?.[1] ?? 0);
162
+ const lost = [];
163
+ const rejected = count(/^rejected \d+: (\d+) batch/);
164
+ if (!detail.startsWith('batches=') && !rejected && !detail.startsWith('lines of the run dropped before sending')) {
165
+ lost.push(`lines not sent (${detail.replace(/ (?:dropped|artifacts)=.*$/, '')})`);
166
+ }
167
+ if (count(/ dropped=(\d+)/))
168
+ lost.push(`lines dropped=${count(/ dropped=(\d+)/)}`);
169
+ if (rejected)
170
+ lost.push(`line batches rejected=${rejected}`);
171
+ const files = /failed=(\d+) pending=(\d+) local=(\d+) rejected=(\d+)/.exec(detail);
172
+ if (files)
173
+ lost.push(`profile files not uploaded=${files.slice(1).reduce((n, x) => n + Number(x), 0)}`);
174
+ if (count(/ lost stream=(\d+)/))
175
+ lost.push(`stream segments lost=${count(/ lost stream=(\d+)/)}`);
176
+ return lost.length ? lost.join(', ') : detail;
177
+ };
153
178
  /** An answer whose status could not be read, or is none of ok / error / off: no confirmed upload. */
154
179
  export const FLUSH_UNREADABLE = 'not acknowledged (unreadable answer)';
155
180
  const FLUSH_STATUSES = new Set(['ok', 'error', 'off']);
@@ -183,12 +208,15 @@ export class Session {
183
208
  readyWaits = [];
184
209
  actionGc;
185
210
  endGc;
211
+ capture;
186
212
  /** Unique for the whole session; the SDK keeps its answers by run token and n. */
187
213
  gcN = 0;
188
214
  /** Steps open right now: a step inside another step's action sends no GC, which would land inside the outer step. */
189
215
  stepDepth = 0;
190
216
  /** Every declared step in start order, for the driver log. */
191
217
  stepLog = [];
218
+ /** Harness defects over the session, for the driver log: every gesture the device did not take and the driver sent again. */
219
+ defects = { gestureRetry: 0, items: [] };
192
220
  /** Called when a top-level step starts and when it ends, outside its start and end markers (the driver log is rewritten then). */
193
221
  onStepChange = null;
194
222
  /** Elements looked up again after going stale, over the session (see `frameOf`). */
@@ -203,6 +231,7 @@ export class Session {
203
231
  this.api = o.api ?? '';
204
232
  this.actionGc = o.actionGc ? 'step' : 'off';
205
233
  this.endGc = o.endGc ?? END_GC_DEFAULT;
234
+ this.capture = o.capture;
206
235
  }
207
236
  /**
208
237
  * One GC command; returns its number, or null when none was sent or none ran. Any answer but `gc:<n> ok` (none, a native
@@ -665,7 +694,7 @@ export class Session {
665
694
  // The memory read follows the GC's answer and is answered before the markers and the flush link, which background the app.
666
695
  if (this.markers.some((m) => m.phase === 'start'))
667
696
  await this.memRead(await this.gc(true));
668
- await sendMarkers(this.fetch, this.api, this.markers, { sleep: (ms) => this.driver.sleep(ms), now: () => this.now(), deviceUnit: this.driver.deviceUnit, tokens: [...this.opened], reported: this.told, note: (line) => this.driver.log.push({ ...this.driver.context, cmd: 'driver-report', t: this.now(), ms: 0, status: 1, answer: line }) });
697
+ await sendMarkers(this.fetch, this.api, this.markers, { sleep: (ms) => this.driver.sleep(ms), now: () => this.now(), deviceUnit: this.driver.deviceUnit, capture: this.capture, tokens: [...this.opened], reported: this.told, note: (line) => this.driver.log.push({ ...this.driver.context, cmd: 'driver-report', t: this.now(), ms: 0, status: 1, answer: line }) });
669
698
  const evidence = { linkAt: this.now(), resentAt: null, ackAt: null, gaveUpAt: null };
670
699
  this.lastFlush = evidence;
671
700
  // The answer element stays up until the next answer replaces it: only an answer echoing one of this end-run's links counts.