droidline 0.0.0-stage → 0.1.1
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 +38 -2
- package/dist/args.d.ts +7 -0
- package/dist/args.js +32 -0
- package/dist/client.d.ts +130 -0
- package/dist/client.js +462 -0
- package/dist/element.d.ts +54 -0
- package/dist/element.js +88 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.js +24 -0
- package/dist/generated.d.ts +3586 -0
- package/dist/generated.js +397 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/testing.d.ts +14 -0
- package/dist/testing.js +43 -0
- package/package.json +60 -4
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { By, ClearResult, DeviceCommands, InputResult, Node, Query, TapResult, TouchResult } from "./generated.js";
|
|
2
|
+
/**
|
|
3
|
+
* One screen element, as it was when find() or findAll() returned it.
|
|
4
|
+
*
|
|
5
|
+
* The fields are a snapshot. Actions look the element up again by its exact bounds and
|
|
6
|
+
* class, so after it moved or disappeared they reject with NOT_FOUND; call refresh() or
|
|
7
|
+
* find it again. Actions do not wait unless you pass a timeout.
|
|
8
|
+
*/
|
|
9
|
+
export declare class Element {
|
|
10
|
+
readonly device: DeviceCommands;
|
|
11
|
+
readonly node: Node;
|
|
12
|
+
constructor(device: DeviceCommands, node: Node);
|
|
13
|
+
get text(): string;
|
|
14
|
+
get id(): string;
|
|
15
|
+
get desc(): string;
|
|
16
|
+
get className(): string;
|
|
17
|
+
get package(): string;
|
|
18
|
+
get bounds(): [number, number, number, number];
|
|
19
|
+
get center(): [number, number];
|
|
20
|
+
/** The query that finds this element again: its exact bounds and class. */
|
|
21
|
+
query(): Query;
|
|
22
|
+
click(opts?: {
|
|
23
|
+
timeout?: number;
|
|
24
|
+
}): Promise<TouchResult>;
|
|
25
|
+
longClick(opts?: {
|
|
26
|
+
ms?: number;
|
|
27
|
+
timeout?: number;
|
|
28
|
+
}): Promise<TouchResult>;
|
|
29
|
+
input(text: string, opts?: {
|
|
30
|
+
append?: boolean;
|
|
31
|
+
timeout?: number;
|
|
32
|
+
}): Promise<InputResult>;
|
|
33
|
+
clear(opts?: {
|
|
34
|
+
timeout?: number;
|
|
35
|
+
}): Promise<ClearResult>;
|
|
36
|
+
/** Tap the center point, without looking the element up again. */
|
|
37
|
+
tap(): Promise<TapResult>;
|
|
38
|
+
exists(): Promise<boolean>;
|
|
39
|
+
/** The same element with its current fields, for example after its text changed. */
|
|
40
|
+
refresh(opts?: {
|
|
41
|
+
timeout?: number;
|
|
42
|
+
}): Promise<Element>;
|
|
43
|
+
/** An element inside this one. */
|
|
44
|
+
find(by: By | Query, value?: string, opts?: {
|
|
45
|
+
nth?: number;
|
|
46
|
+
timeout?: number;
|
|
47
|
+
}): Promise<Element>;
|
|
48
|
+
/** Every element inside this one that matches. */
|
|
49
|
+
findAll(by: By | Query, value?: string, opts?: {
|
|
50
|
+
timeout?: number;
|
|
51
|
+
}): Promise<Element[]>;
|
|
52
|
+
private within;
|
|
53
|
+
toString(): string;
|
|
54
|
+
}
|
package/dist/element.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One screen element, as it was when find() or findAll() returned it.
|
|
3
|
+
*
|
|
4
|
+
* The fields are a snapshot. Actions look the element up again by its exact bounds and
|
|
5
|
+
* class, so after it moved or disappeared they reject with NOT_FOUND; call refresh() or
|
|
6
|
+
* find it again. Actions do not wait unless you pass a timeout.
|
|
7
|
+
*/
|
|
8
|
+
export class Element {
|
|
9
|
+
device;
|
|
10
|
+
node;
|
|
11
|
+
constructor(device, node) {
|
|
12
|
+
this.device = device;
|
|
13
|
+
this.node = { ...node };
|
|
14
|
+
}
|
|
15
|
+
get text() {
|
|
16
|
+
return String(this.node.text ?? "");
|
|
17
|
+
}
|
|
18
|
+
get id() {
|
|
19
|
+
return String(this.node.id ?? "");
|
|
20
|
+
}
|
|
21
|
+
get desc() {
|
|
22
|
+
return String(this.node.desc ?? "");
|
|
23
|
+
}
|
|
24
|
+
get className() {
|
|
25
|
+
return String(this.node.class ?? "");
|
|
26
|
+
}
|
|
27
|
+
get package() {
|
|
28
|
+
return String(this.node.package ?? "");
|
|
29
|
+
}
|
|
30
|
+
get bounds() {
|
|
31
|
+
const b = this.node.bounds ?? [0, 0, 0, 0];
|
|
32
|
+
return [Number(b[0]), Number(b[1]), Number(b[2]), Number(b[3])];
|
|
33
|
+
}
|
|
34
|
+
get center() {
|
|
35
|
+
const [l, t, r, b] = this.bounds;
|
|
36
|
+
return [Math.floor((l + r) / 2), Math.floor((t + b) / 2)];
|
|
37
|
+
}
|
|
38
|
+
/** The query that finds this element again: its exact bounds and class. */
|
|
39
|
+
query() {
|
|
40
|
+
const q = { bounds: this.bounds };
|
|
41
|
+
if (this.className)
|
|
42
|
+
q.class = this.className;
|
|
43
|
+
return q;
|
|
44
|
+
}
|
|
45
|
+
click(opts = {}) {
|
|
46
|
+
return this.device.touch(this.query(), { timeout: opts.timeout ?? 0 });
|
|
47
|
+
}
|
|
48
|
+
longClick(opts = {}) {
|
|
49
|
+
return this.device.long_touch(this.query(), { ms: opts.ms, timeout: opts.timeout ?? 0 });
|
|
50
|
+
}
|
|
51
|
+
input(text, opts = {}) {
|
|
52
|
+
return this.device.input(this.query(), text, { append: opts.append, timeout: opts.timeout ?? 0 });
|
|
53
|
+
}
|
|
54
|
+
clear(opts = {}) {
|
|
55
|
+
return this.device.clear(this.query(), { timeout: opts.timeout ?? 0 });
|
|
56
|
+
}
|
|
57
|
+
/** Tap the center point, without looking the element up again. */
|
|
58
|
+
tap() {
|
|
59
|
+
const [x, y] = this.center;
|
|
60
|
+
return this.device.tap(x, y);
|
|
61
|
+
}
|
|
62
|
+
exists() {
|
|
63
|
+
return this.device.exists(this.query());
|
|
64
|
+
}
|
|
65
|
+
/** The same element with its current fields, for example after its text changed. */
|
|
66
|
+
refresh(opts = {}) {
|
|
67
|
+
return this.device.find(this.query(), { timeout: opts.timeout ?? 0 });
|
|
68
|
+
}
|
|
69
|
+
/** An element inside this one. */
|
|
70
|
+
find(by, value, opts = {}) {
|
|
71
|
+
return this.device.find(this.within(by, value), opts);
|
|
72
|
+
}
|
|
73
|
+
/** Every element inside this one that matches. */
|
|
74
|
+
findAll(by, value, opts = {}) {
|
|
75
|
+
return this.device.findAll(this.within(by, value), opts);
|
|
76
|
+
}
|
|
77
|
+
within(by, value) {
|
|
78
|
+
const q = typeof by === "string" ? { [by]: value } : { ...by };
|
|
79
|
+
if (q.inside !== undefined)
|
|
80
|
+
throw new TypeError("this query already has inside; call device.find() with your own query instead");
|
|
81
|
+
q.inside = this.query();
|
|
82
|
+
return q;
|
|
83
|
+
}
|
|
84
|
+
toString() {
|
|
85
|
+
const label = this.text || this.desc || this.id;
|
|
86
|
+
return `Element(${this.className || "?"} ${JSON.stringify(label)} [${this.bounds.join(", ")}])`;
|
|
87
|
+
}
|
|
88
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ErrorCode } from "./generated.js";
|
|
2
|
+
/** Codes raised by the SDK itself rather than the server. */
|
|
3
|
+
export type ClientErrorCode = "SERVER_NOT_RUNNING" | "CONNECTION_LOST";
|
|
4
|
+
/**
|
|
5
|
+
* Every failure the SDK reports. `code` is an error code from spec/commands.json or a
|
|
6
|
+
* ClientErrorCode; `data` holds the extra fields the server sent, like `screen` or `candidates`.
|
|
7
|
+
*/
|
|
8
|
+
export declare class DroidlineError extends Error {
|
|
9
|
+
readonly code: ErrorCode | ClientErrorCode | (string & {});
|
|
10
|
+
readonly retryable: boolean;
|
|
11
|
+
readonly data: Record<string, unknown>;
|
|
12
|
+
constructor(message: string, code: string, retryable?: boolean, data?: Record<string, unknown>);
|
|
13
|
+
}
|
|
14
|
+
export declare function errorFromResponse(resp: Record<string, unknown>): DroidlineError;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ERRORS } from "./generated.js";
|
|
2
|
+
/**
|
|
3
|
+
* Every failure the SDK reports. `code` is an error code from spec/commands.json or a
|
|
4
|
+
* ClientErrorCode; `data` holds the extra fields the server sent, like `screen` or `candidates`.
|
|
5
|
+
*/
|
|
6
|
+
export class DroidlineError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
retryable;
|
|
9
|
+
data;
|
|
10
|
+
constructor(message, code, retryable = false, data = {}) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "DroidlineError";
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.retryable = retryable;
|
|
15
|
+
this.data = data;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function errorFromResponse(resp) {
|
|
19
|
+
const { id: _id, ok: _ok, error, msg, retryable, ...data } = resp;
|
|
20
|
+
const code = typeof error === "string" && error ? error : "INTERNAL";
|
|
21
|
+
const known = ERRORS[code];
|
|
22
|
+
const flag = typeof retryable === "boolean" ? retryable : (known?.retryable ?? false);
|
|
23
|
+
return new DroidlineError(typeof msg === "string" && msg ? msg : code, code, flag, data);
|
|
24
|
+
}
|