@keepsnappy/appium-driver 0.0.0-stage → 0.1.0-alpha.11
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 +37 -2
- package/dist/src/browserstack.d.ts +24 -0
- package/dist/src/browserstack.js +54 -0
- package/dist/src/browserstack.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +8 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +11 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/journey.d.ts +43 -0
- package/dist/src/journey.js +2 -0
- package/dist/src/journey.js.map +1 -0
- package/dist/src/links.d.ts +35 -0
- package/dist/src/links.js +87 -0
- package/dist/src/links.js.map +1 -0
- package/dist/src/load.d.ts +4 -0
- package/dist/src/load.js +23 -0
- package/dist/src/load.js.map +1 -0
- package/dist/src/platform.d.ts +63 -0
- package/dist/src/platform.js +145 -0
- package/dist/src/platform.js.map +1 -0
- package/dist/src/run.d.ts +92 -0
- package/dist/src/run.js +157 -0
- package/dist/src/run.js.map +1 -0
- package/dist/src/selectors.d.ts +25 -0
- package/dist/src/selectors.js +57 -0
- package/dist/src/selectors.js.map +1 -0
- package/dist/src/session.d.ts +156 -0
- package/dist/src/session.js +449 -0
- package/dist/src/session.js.map +1 -0
- package/dist/src/snapshot.d.ts +64 -0
- package/dist/src/snapshot.js +197 -0
- package/dist/src/snapshot.js.map +1 -0
- package/dist/src/tokens.d.ts +14 -0
- package/dist/src/tokens.js +28 -0
- package/dist/src/tokens.js.map +1 -0
- package/dist/src/version.d.ts +14 -0
- package/dist/src/version.js +69 -0
- package/dist/src/version.js.map +1 -0
- package/dist/src/webdriver.d.ts +83 -0
- package/dist/src/webdriver.js +233 -0
- package/dist/src/webdriver.js.map +1 -0
- package/package.json +37 -4
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { REGEX_CHARS } from './selectors.js';
|
|
2
|
+
const ENTITIES = { amp: '&', lt: '<', gt: '>', quot: '"', apos: "'" };
|
|
3
|
+
const unescapeXml = (s) => s.replace(/&(#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos);/gi, (_, e) => e[0] === '#' ? String.fromCodePoint(e[1].toLowerCase() === 'x' ? parseInt(e.slice(2), 16) : parseInt(e.slice(1), 10)) : ENTITIES[e.toLowerCase()]);
|
|
4
|
+
// A value that holds a double quote is written in single quotes (text='"quoted"'), and the other way round.
|
|
5
|
+
const TAG = /<(\/?)([\w.$:-]+)((?:\s+[\w:.-]+=(?:"[^"]*"|'[^']*'))*)\s*(\/?)>/g;
|
|
6
|
+
const ATTR = /([\w:.-]+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
7
|
+
const BOUNDS = /^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/;
|
|
8
|
+
/** The nodes of a UiAutomator2 `GET /source`, in document order, each with the index of its parent (-1 for the root). */
|
|
9
|
+
export function parseAndroidSource(xml) {
|
|
10
|
+
const nodes = [];
|
|
11
|
+
const open = [];
|
|
12
|
+
for (const m of xml.matchAll(TAG)) {
|
|
13
|
+
if (m[1] === '/') {
|
|
14
|
+
open.pop();
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const attrs = {};
|
|
18
|
+
for (const a of m[3].matchAll(ATTR))
|
|
19
|
+
attrs[a[1]] = unescapeXml(a[2] ?? a[3]);
|
|
20
|
+
const b = BOUNDS.exec(attrs.bounds ?? '');
|
|
21
|
+
const node = {
|
|
22
|
+
parent: open.length ? open[open.length - 1] : -1,
|
|
23
|
+
id: attrs['resource-id'] ?? '',
|
|
24
|
+
text: attrs.text ?? '',
|
|
25
|
+
desc: attrs['content-desc'] ?? '',
|
|
26
|
+
displayed: attrs.displayed === 'true',
|
|
27
|
+
scrollable: attrs.scrollable === 'true',
|
|
28
|
+
left: b ? +b[1] : 0,
|
|
29
|
+
top: b ? +b[2] : 0,
|
|
30
|
+
right: b ? +b[3] : 0,
|
|
31
|
+
bottom: b ? +b[4] : 0,
|
|
32
|
+
};
|
|
33
|
+
nodes.push(node);
|
|
34
|
+
if (m[4] !== '/')
|
|
35
|
+
open.push(nodes.length - 1);
|
|
36
|
+
}
|
|
37
|
+
return nodes;
|
|
38
|
+
}
|
|
39
|
+
const inverted = (n) => n.bottom < n.top;
|
|
40
|
+
/**
|
|
41
|
+
* Whether the node counts as on screen, from what the platform says about it. `displayed` is kept: a node it calls not
|
|
42
|
+
* displayed is hidden. A node with inverted bounds (top below bottom) is never shown by this test; `Screen` rescues some
|
|
43
|
+
* of them, see below. An empty or off-screen frame never counts, and with `full` the whole frame must be inside the window.
|
|
44
|
+
*/
|
|
45
|
+
export function nodeShown(n, w, full = false) {
|
|
46
|
+
if (!n.displayed || inverted(n))
|
|
47
|
+
return false;
|
|
48
|
+
if (n.right - n.left <= 0 || n.bottom - n.top <= 0)
|
|
49
|
+
return false;
|
|
50
|
+
if (full)
|
|
51
|
+
return n.left >= 0 && n.top >= 0 && n.right <= w.width && n.bottom <= w.height;
|
|
52
|
+
return n.left < w.width && n.top < w.height && n.right > 0 && n.bottom > 0;
|
|
53
|
+
}
|
|
54
|
+
/** A Java regex as the selectors write it (`(?is)` is added, a match is a whole-value match) read by JavaScript; null when it cannot be read faithfully. */
|
|
55
|
+
function wholeMatch(re) {
|
|
56
|
+
if (/\(\?[^:=!<]/.test(re) || /\\[AZzGhHRVXpPk]|\\Q|[*+?}]\+/.test(re))
|
|
57
|
+
return null;
|
|
58
|
+
try {
|
|
59
|
+
return new RegExp(`^(?:${re})$`, 'is');
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** The node test of a selector without its parent, or null when the selector uses a regex JavaScript would read differently. */
|
|
66
|
+
export function nodeMatcher(sel) {
|
|
67
|
+
let idTest = null;
|
|
68
|
+
if (sel.id !== undefined) {
|
|
69
|
+
if (REGEX_CHARS.test(sel.id)) {
|
|
70
|
+
const re = wholeMatch(sel.id);
|
|
71
|
+
if (!re)
|
|
72
|
+
return null;
|
|
73
|
+
idTest = (s) => re.test(s);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const want = sel.id;
|
|
77
|
+
idTest = (s) => s === want;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
let textRe = null;
|
|
81
|
+
if (sel.text !== undefined) {
|
|
82
|
+
textRe = wholeMatch(sel.text);
|
|
83
|
+
if (!textRe)
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return (n) => (idTest ? idTest(n.id) : true) && (textRe ? textRe.test(n.text) || textRe.test(n.desc) : true);
|
|
87
|
+
}
|
|
88
|
+
/** A test of a resource id the way a selector's `id` reads it, or null when JavaScript would read it differently from UiAutomator2. */
|
|
89
|
+
export function idMatcher(id) {
|
|
90
|
+
const m = nodeMatcher({ id });
|
|
91
|
+
return m && ((resourceId) => m({ parent: -1, id: resourceId, text: '', desc: '', displayed: true, scrollable: false, left: 0, top: 0, right: 0, bottom: 0 }));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* One page source, ready to be asked "is this selector shown".
|
|
95
|
+
*
|
|
96
|
+
* Rescue of inverted nodes. Cause, measured on a Galaxy S23 (TapIn live room): when the keyboard opens the app translates the
|
|
97
|
+
* chat up. The accessibility bounds of the chat's nodes are still cut by the container's untranslated rect, whose top stays
|
|
98
|
+
* where it was (y 1083), so a node drawn on screen reports top 1083 and its real bottom (846, 948 ...): top below bottom and
|
|
99
|
+
* `displayed=false`, and the scroll container that holds them is inverted too. Nodes the real clip hides (the list scrolled
|
|
100
|
+
* past them) all report the same bottom, the clip's own edge (y 201 there). So, per scroll container that is itself inverted,
|
|
101
|
+
* a node inside it that is inverted counts as shown when its real bottom is on screen and below that edge. Outside such a
|
|
102
|
+
* container (a normal list, a node at opacity 0, the keyboard's own views) an inverted or hidden node never counts.
|
|
103
|
+
*/
|
|
104
|
+
export class Screen {
|
|
105
|
+
nodes;
|
|
106
|
+
window;
|
|
107
|
+
/** Per scroll container (a node index) that is inverted: the bottom edge the clipped-away nodes collapse onto. */
|
|
108
|
+
edges = new Map();
|
|
109
|
+
container;
|
|
110
|
+
constructor(nodes, window) {
|
|
111
|
+
this.nodes = nodes;
|
|
112
|
+
this.window = window;
|
|
113
|
+
this.container = nodes.map((_, i) => this.scrollAncestor(i));
|
|
114
|
+
const byContainer = new Map();
|
|
115
|
+
nodes.forEach((n, i) => {
|
|
116
|
+
const c = this.container[i];
|
|
117
|
+
if (c >= 0 && inverted(nodes[c]) && inverted(n))
|
|
118
|
+
(byContainer.get(c) ?? byContainer.set(c, []).get(c)).push(i);
|
|
119
|
+
});
|
|
120
|
+
for (const [c, members] of byContainer) {
|
|
121
|
+
const edge = this.clipEdge(members);
|
|
122
|
+
if (edge !== null)
|
|
123
|
+
this.edges.set(c, edge);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
scrollAncestor(i) {
|
|
127
|
+
let p = this.nodes[i].parent;
|
|
128
|
+
while (p >= 0 && !this.nodes[p].scrollable)
|
|
129
|
+
p = this.nodes[p].parent;
|
|
130
|
+
return p;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The bottom shared by the most clipped-away subtrees of one container, or null. A subtree is counted once, by its head: a
|
|
134
|
+
* node whose parent does not have the same bottom, so the nested nodes of one message are one, two clipped messages are
|
|
135
|
+
* two. The edge needs at least two subtrees; with fewer than two clipped messages there is none and nothing is rescued
|
|
136
|
+
* (the step then fails loudly). On a tie the larger bottom wins, which shows fewer nodes.
|
|
137
|
+
*/
|
|
138
|
+
clipEdge(members) {
|
|
139
|
+
const heads = new Map();
|
|
140
|
+
for (const i of members) {
|
|
141
|
+
const n = this.nodes[i];
|
|
142
|
+
const parent = this.nodes[n.parent];
|
|
143
|
+
if (parent && inverted(parent) && parent.bottom === n.bottom)
|
|
144
|
+
continue;
|
|
145
|
+
heads.set(n.bottom, (heads.get(n.bottom) ?? 0) + 1);
|
|
146
|
+
}
|
|
147
|
+
let edge = null;
|
|
148
|
+
let most = 1;
|
|
149
|
+
for (const [bottom, count] of heads) {
|
|
150
|
+
if (count > most || (count === most && edge !== null && bottom > edge)) {
|
|
151
|
+
edge = bottom;
|
|
152
|
+
most = count;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return edge;
|
|
156
|
+
}
|
|
157
|
+
nodeShown(i, full) {
|
|
158
|
+
const n = this.nodes[i];
|
|
159
|
+
if (!inverted(n))
|
|
160
|
+
return nodeShown(n, this.window, full);
|
|
161
|
+
const edge = this.edges.get(this.container[i]);
|
|
162
|
+
if (full || edge === undefined)
|
|
163
|
+
return false;
|
|
164
|
+
return n.bottom > edge && n.bottom <= this.window.height && n.right > n.left && n.left < this.window.width && n.right > 0;
|
|
165
|
+
}
|
|
166
|
+
/** The indexes of the nodes that match and are on screen, inside the shown parent when `childOf` is given, or null when the selector cannot be answered from a source. */
|
|
167
|
+
shown(sel, full = false) {
|
|
168
|
+
let inside;
|
|
169
|
+
if (sel.childOf !== undefined) {
|
|
170
|
+
const parent = this.shown({ id: sel.childOf });
|
|
171
|
+
if (parent === null)
|
|
172
|
+
return null;
|
|
173
|
+
if (parent.length === 0)
|
|
174
|
+
return [];
|
|
175
|
+
inside = parent[0];
|
|
176
|
+
}
|
|
177
|
+
const match = nodeMatcher({ ...(sel.id !== undefined ? { id: sel.id } : {}), ...(sel.text !== undefined ? { text: sel.text } : {}) });
|
|
178
|
+
if (!match)
|
|
179
|
+
return null;
|
|
180
|
+
const hits = [];
|
|
181
|
+
this.nodes.forEach((n, i) => {
|
|
182
|
+
if (!match(n) || !this.nodeShown(i, full))
|
|
183
|
+
return;
|
|
184
|
+
if (inside !== undefined && !this.within(i, inside))
|
|
185
|
+
return;
|
|
186
|
+
hits.push(i);
|
|
187
|
+
});
|
|
188
|
+
return hits;
|
|
189
|
+
}
|
|
190
|
+
within(i, ancestor) {
|
|
191
|
+
for (let p = this.nodes[i].parent; p >= 0; p = this.nodes[p].parent)
|
|
192
|
+
if (p === ancestor)
|
|
193
|
+
return true;
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** tokens.js from `perflab farm open`: plain assignments on `output`, read without evaluating the file. */
|
|
2
|
+
export declare function parseTokensJs(text: string): {
|
|
3
|
+
api: string;
|
|
4
|
+
tokens: Record<string, string[]>;
|
|
5
|
+
};
|
|
6
|
+
export interface PlannedRun {
|
|
7
|
+
journey: string;
|
|
8
|
+
iteration: number;
|
|
9
|
+
token: string;
|
|
10
|
+
}
|
|
11
|
+
/** Iteration-major, journeys in the given order inside each: the order `farm open` numbers its runs in. */
|
|
12
|
+
export declare function plan(journeys: readonly string[], repeat: number, tokens: Record<string, string[]>): PlannedRun[];
|
|
13
|
+
/** Page-object files that each assign onto `output.elements` (the shape a Maestro suite already uses), evaluated in a sandbox. */
|
|
14
|
+
export declare function loadElements(dir: string, names: readonly string[]): Record<string, any>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { runInNewContext } from 'node:vm';
|
|
4
|
+
/** tokens.js from `perflab farm open`: plain assignments on `output`, read without evaluating the file. */
|
|
5
|
+
export function parseTokensJs(text) {
|
|
6
|
+
const pick = (name) => {
|
|
7
|
+
const m = text.match(new RegExp(`output\\.${name}\\s*=\\s*(.+);\\s*$`, 'm'));
|
|
8
|
+
return m ? JSON.parse(m[1]) : undefined;
|
|
9
|
+
};
|
|
10
|
+
return { api: pick('perflabApi') ?? '', tokens: pick('perflabTokens') ?? {} };
|
|
11
|
+
}
|
|
12
|
+
/** Iteration-major, journeys in the given order inside each: the order `farm open` numbers its runs in. */
|
|
13
|
+
export function plan(journeys, repeat, tokens) {
|
|
14
|
+
const out = [];
|
|
15
|
+
for (let i = 0; i < repeat; i++) {
|
|
16
|
+
for (const journey of journeys)
|
|
17
|
+
out.push({ journey, iteration: i + 1, token: tokens[journey]?.[i] ?? '' });
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
/** Page-object files that each assign onto `output.elements` (the shape a Maestro suite already uses), evaluated in a sandbox. */
|
|
22
|
+
export function loadElements(dir, names) {
|
|
23
|
+
const output = {};
|
|
24
|
+
for (const name of names)
|
|
25
|
+
runInNewContext(readFileSync(join(dir, `${name}.js`), 'utf8'), { output });
|
|
26
|
+
return output.elements ?? {};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,2GAA2G;AAC3G,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,IAAI,GAAG,CAAC,IAAY,EAAW,EAAE;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,YAAY,IAAI,qBAAqB,EAAE,GAAG,CAAC,CAAC,CAAC;QAC7E,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3C,CAAC,CAAC;IACF,OAAO,EAAE,GAAG,EAAG,IAAI,CAAC,YAAY,CAAwB,IAAI,EAAE,EAAE,MAAM,EAAG,IAAI,CAAC,eAAe,CAA0C,IAAI,EAAE,EAAE,CAAC;AAClJ,CAAC;AAQD,2GAA2G;AAC3G,MAAM,UAAU,IAAI,CAAC,QAA2B,EAAE,MAAc,EAAE,MAAgC;IAChG,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,KAAK,MAAM,OAAO,IAAI,QAAQ;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,kIAAkI;AAClI,MAAM,UAAU,YAAY,CAAC,GAAW,EAAE,KAAwB;IAChE,MAAM,MAAM,GAAuC,EAAE,CAAC;IACtD,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,eAAe,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,KAAK,CAAC,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACrG,OAAO,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The driver's version as a run's setup records it (REQ-VER-35): the package's own `version`, read from the package.json that sits
|
|
3
|
+
* above this file (`dist/src/version.js` in a published or built package, `src/version.ts` in the checkout). A bundle that has no
|
|
4
|
+
* package.json next to it (the agent CLI, `bun build`) is given the version at build time by `PERFLAB_DRIVER_VERSION`
|
|
5
|
+
* (scripts/release/stage.ts). Null when neither is known: the run then records no driver version (unknown, never a made-up one).
|
|
6
|
+
*/
|
|
7
|
+
export declare function driverVersion(): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* The journey's version: a content hash (12 hex) of the journey module and of every module it imports by a relative path, in a
|
|
10
|
+
* stable order, so editing a page object or the module itself changes it and moving the checkout does not (REQ-VER-35). Line endings
|
|
11
|
+
* are normalised (CRLF = LF), file keys are paths relative to the module. Not followed: path aliases (`@/x`, tsconfig `paths`), package
|
|
12
|
+
* imports and non-literal imports; a change that only reaches the journey through them does not change the version.
|
|
13
|
+
*/
|
|
14
|
+
export declare function journeyVersion(modulePath: string): string;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
/**
|
|
6
|
+
* The driver's version as a run's setup records it (REQ-VER-35): the package's own `version`, read from the package.json that sits
|
|
7
|
+
* above this file (`dist/src/version.js` in a published or built package, `src/version.ts` in the checkout). A bundle that has no
|
|
8
|
+
* package.json next to it (the agent CLI, `bun build`) is given the version at build time by `PERFLAB_DRIVER_VERSION`
|
|
9
|
+
* (scripts/release/stage.ts). Null when neither is known: the run then records no driver version (unknown, never a made-up one).
|
|
10
|
+
*/
|
|
11
|
+
export function driverVersion() {
|
|
12
|
+
const built = (process.env.KEEPSNAPPY_DRIVER_VERSION ?? process.env.PERFLAB_DRIVER_VERSION);
|
|
13
|
+
if (built)
|
|
14
|
+
return built;
|
|
15
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
for (let i = 0; i < 3; i++, dir = dirname(dir)) {
|
|
17
|
+
const file = join(dir, 'package.json');
|
|
18
|
+
if (!existsSync(file))
|
|
19
|
+
continue;
|
|
20
|
+
try {
|
|
21
|
+
const pkg = JSON.parse(readFileSync(file, 'utf8'));
|
|
22
|
+
if ((pkg.name === '@keepsnappy/appium-driver' || pkg.name === '@perf-labs/appium-driver') && pkg.version)
|
|
23
|
+
return pkg.version;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
/* not the package file: look one level up */
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const RELATIVE_IMPORT = /(?:from\s*|import\s*\(\s*|import\s+|require\s*\(\s*)['"](\.{1,2}\/[^'"]+)['"]/g;
|
|
32
|
+
const EXTENSIONS = ['', '.ts', '.mjs', '.js', '/index.ts', '/index.mjs', '/index.js'];
|
|
33
|
+
function resolveImport(from, spec) {
|
|
34
|
+
const base = resolve(dirname(from), spec);
|
|
35
|
+
for (const ext of EXTENSIONS) {
|
|
36
|
+
const p = base + ext;
|
|
37
|
+
if (existsSync(p) && statSync(p).isFile())
|
|
38
|
+
return p;
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The journey's version: a content hash (12 hex) of the journey module and of every module it imports by a relative path, in a
|
|
44
|
+
* stable order, so editing a page object or the module itself changes it and moving the checkout does not (REQ-VER-35). Line endings
|
|
45
|
+
* are normalised (CRLF = LF), file keys are paths relative to the module. Not followed: path aliases (`@/x`, tsconfig `paths`), package
|
|
46
|
+
* imports and non-literal imports; a change that only reaches the journey through them does not change the version.
|
|
47
|
+
*/
|
|
48
|
+
export function journeyVersion(modulePath) {
|
|
49
|
+
const seen = new Map();
|
|
50
|
+
const visit = (file) => {
|
|
51
|
+
if (seen.has(file))
|
|
52
|
+
return;
|
|
53
|
+
const text = readFileSync(file, 'utf8').replace(/\r\n?/g, '\n');
|
|
54
|
+
seen.set(file, text);
|
|
55
|
+
for (const m of text.matchAll(RELATIVE_IMPORT)) {
|
|
56
|
+
const dep = resolveImport(file, m[1]);
|
|
57
|
+
if (dep)
|
|
58
|
+
visit(dep);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const root = resolve(modulePath);
|
|
62
|
+
visit(root);
|
|
63
|
+
const keyed = [...seen].map(([file, text]) => [file === root ? '<module>' : relative(dirname(root), file).split('\\').join('/'), text]);
|
|
64
|
+
const hash = createHash('sha256');
|
|
65
|
+
for (const [key, text] of keyed.sort((a, b) => (a[0] < b[0] ? -1 : 1)))
|
|
66
|
+
hash.update(key).update('\0').update(text).update('\0');
|
|
67
|
+
return hash.digest('hex').slice(0, 12);
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type Platform } from './platform.js';
|
|
2
|
+
import { type Selector } from './selectors.js';
|
|
3
|
+
export type FetchLike = (url: string, init?: any) => Promise<Response>;
|
|
4
|
+
export interface CommandLogEntry {
|
|
5
|
+
cmd: string;
|
|
6
|
+
t: number;
|
|
7
|
+
ms: number;
|
|
8
|
+
status: number;
|
|
9
|
+
[k: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
export declare const realSleep: (ms: number) => Promise<void>;
|
|
12
|
+
export declare class WdError extends Error {
|
|
13
|
+
readonly wdError: string;
|
|
14
|
+
constructor(message: string, wdError: string);
|
|
15
|
+
}
|
|
16
|
+
export interface Rect {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
}
|
|
22
|
+
export interface DriverOptions {
|
|
23
|
+
hub: string;
|
|
24
|
+
fetch?: FetchLike;
|
|
25
|
+
log: CommandLogEntry[];
|
|
26
|
+
now?: () => number;
|
|
27
|
+
sleep?: (ms: number) => Promise<void>;
|
|
28
|
+
headers?: Record<string, string>;
|
|
29
|
+
bundleId: string;
|
|
30
|
+
/** Default iOS. */
|
|
31
|
+
platform?: Platform;
|
|
32
|
+
}
|
|
33
|
+
/** A minimal W3C WebDriver client that times every command, sent to returned. */
|
|
34
|
+
export declare class Driver {
|
|
35
|
+
readonly bundleId: string;
|
|
36
|
+
readonly platform: Platform;
|
|
37
|
+
readonly headers: Record<string, string>;
|
|
38
|
+
readonly hub: string;
|
|
39
|
+
readonly fetch: FetchLike;
|
|
40
|
+
readonly log: CommandLogEntry[];
|
|
41
|
+
readonly now: () => number;
|
|
42
|
+
readonly sleep: (ms: number) => Promise<void>;
|
|
43
|
+
sessionId: string | null;
|
|
44
|
+
context: Record<string, unknown>;
|
|
45
|
+
private viewport;
|
|
46
|
+
private readonly rects;
|
|
47
|
+
constructor(o: DriverOptions);
|
|
48
|
+
cmd(method: string, path: string, body?: unknown): Promise<any>;
|
|
49
|
+
start(capabilities: Record<string, unknown>): Promise<any>;
|
|
50
|
+
execute(script: string, arg?: unknown): Promise<any>;
|
|
51
|
+
/** The first match, inside `parent` when given, or null. */
|
|
52
|
+
find(using: string, value: string, parent?: string): Promise<string | null>;
|
|
53
|
+
findAll(using: string, value: string, parent?: string): Promise<string[]>;
|
|
54
|
+
screen(): Promise<Rect>;
|
|
55
|
+
/** On screen: in the tree with a frame that overlaps the window. `full` asks for the whole frame inside it. */
|
|
56
|
+
onScreen(el: string, full?: boolean): Promise<boolean>;
|
|
57
|
+
/** The first element the selector matches on screen, or null. */
|
|
58
|
+
findShown(sel: Selector, full?: boolean): Promise<string | null>;
|
|
59
|
+
/**
|
|
60
|
+
* For each selector, whether an element it matches is on screen: what `findShown` answers, one by one. With `group` the
|
|
61
|
+
* selectors that are only an id are asked together in one lookup (when the platform has one), then each match is told
|
|
62
|
+
* apart by its resource id; that costs one tree walk however many are missing, where one by one costs a walk for each.
|
|
63
|
+
*/
|
|
64
|
+
shownMany(sels: readonly Selector[], group: boolean): Promise<boolean[]>;
|
|
65
|
+
click(el: string): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* A touch at the centre of the element's frame (inside the window), not WDA's element click, which looks for a
|
|
68
|
+
* hittable point and misses a full-screen container such as a video tile.
|
|
69
|
+
*/
|
|
70
|
+
tapElement(el: string): Promise<void>;
|
|
71
|
+
/** One finger from to to over durationMs, in window points. */
|
|
72
|
+
drag(from: {
|
|
73
|
+
x: number;
|
|
74
|
+
y: number;
|
|
75
|
+
}, to: {
|
|
76
|
+
x: number;
|
|
77
|
+
y: number;
|
|
78
|
+
}, durationMs: number): Promise<void>;
|
|
79
|
+
/** Stop, then start, so the process is a fresh one. */
|
|
80
|
+
relaunch(bundleId?: string): Promise<void>;
|
|
81
|
+
activate(bundleId?: string): Promise<any>;
|
|
82
|
+
end(): Promise<void>;
|
|
83
|
+
}
|