@allwright.dev/core 0.0.47 → 0.0.48
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/dist/bootstrap.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/mobile.js +23 -2
- package/dist/mobileSelectors.d.ts +2 -0
- package/dist/mobileSelectors.js +168 -0
- package/dist/types.d.ts +8 -0
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -12,7 +12,7 @@ const ALLWRIGHT_HOME_ENV_VAR = "ALLWRIGHT_HOME";
|
|
|
12
12
|
const ALLWRIGHT_REPOSITORY_ENV_VAR = "ALLWRIGHT_REPOSITORY";
|
|
13
13
|
const ALLWRIGHT_VERSION_ENV_VAR = "ALLWRIGHT_VERSION";
|
|
14
14
|
const DEFAULT_RELEASE_REPOSITORY = "allwright-dev/allwright";
|
|
15
|
-
const DEFAULT_RELEASE_VERSION = "0.0.
|
|
15
|
+
const DEFAULT_RELEASE_VERSION = "0.0.48";
|
|
16
16
|
const STARTUP_TIMEOUT_MS = 20_000;
|
|
17
17
|
const PING_TIMEOUT_MS = 1_000;
|
|
18
18
|
const PROTO_ROOT = fileURLToPath(new URL("../proto/", import.meta.url));
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { PageImpl } from "./page.js";
|
|
|
5
5
|
import { setServerAddr, shutdown } from "./runtime.js";
|
|
6
6
|
import type { Browser, BrowserKind, BrowserType, LaunchOptions, Page, ResolveConfigOptions, ResolvedAllwrightConfig } from "./types.js";
|
|
7
7
|
export { findConfigFile, loadConfigFile, resolveConfig, setServerAddr, shutdown };
|
|
8
|
-
export type { AllwrightConfig, Browser, BrowserInfo, BrowserKind, BrowserType, ClickResult, CommandOptions, CountResult, ElementResult, FillResult, HighlightOptions, HighlightResult, LaunchOptions, MobileAndroidConnectOptions, MobileAndroidDevice, MobileAndroidLaunchOptions, MobileAndroidPage, Locator, LocatorInfo, MobileSurfaceNamespace, NavigateResult, Page, PageInfo, PressOptions, PressResult, ResolveConfigOptions, ResolvedAllwrightConfig, RetryConfig, TextResult, WaitForSelectorOptions, WaitForSelectorResult, } from "./types.js";
|
|
8
|
+
export type { AllwrightConfig, Browser, BrowserInfo, BrowserKind, BrowserType, ClickResult, CommandOptions, CountResult, ElementResult, FillResult, HighlightOptions, HighlightResult, LaunchOptions, MobileAndroidConnectOptions, MobileAndroidDevice, MobileAndroidLaunchOptions, MobileAndroidLocator, MobileAndroidPage, Locator, LocatorInfo, MobileSurfaceNamespace, NavigateResult, Page, PageInfo, PressOptions, PressResult, ResolveConfigOptions, ResolvedAllwrightConfig, RetryConfig, TextResult, WaitForSelectorOptions, WaitForSelectorResult, } from "./types.js";
|
|
9
9
|
export declare const chromium: BrowserType;
|
|
10
10
|
export declare const firefox: BrowserType;
|
|
11
11
|
export { mobile };
|
package/dist/mobile.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ensurePluginsInstalled, invokePlugin } from "./bootstrap.js";
|
|
2
|
+
import { chainMobileSelectorForTransport, normalizeMobileSelectorForTransport } from "./mobileSelectors.js";
|
|
2
3
|
function timeoutMsOf(options) {
|
|
3
4
|
return options?.timeoutMs;
|
|
4
5
|
}
|
|
@@ -22,12 +23,15 @@ class MobileAndroidPageImpl {
|
|
|
22
23
|
get sessionId() {
|
|
23
24
|
return this.pageSession.page_id;
|
|
24
25
|
}
|
|
26
|
+
locator(selector) {
|
|
27
|
+
return new MobileAndroidLocatorImpl(this, normalizeMobileSelectorForTransport(selector));
|
|
28
|
+
}
|
|
25
29
|
async click(selector, options = {}) {
|
|
26
30
|
const result = await invokeAndroidExpected("click", {
|
|
27
31
|
command: "click_element",
|
|
28
32
|
browser_session: this.browserSession,
|
|
29
33
|
page_session: this.pageSession,
|
|
30
|
-
selector,
|
|
34
|
+
selector: normalizeMobileSelectorForTransport(selector),
|
|
31
35
|
timeout_ms: timeoutMsOf(options),
|
|
32
36
|
});
|
|
33
37
|
return {
|
|
@@ -41,7 +45,7 @@ class MobileAndroidPageImpl {
|
|
|
41
45
|
command: "fill_element",
|
|
42
46
|
browser_session: this.browserSession,
|
|
43
47
|
page_session: this.pageSession,
|
|
44
|
-
selector,
|
|
48
|
+
selector: normalizeMobileSelectorForTransport(selector),
|
|
45
49
|
value,
|
|
46
50
|
timeout_ms: timeoutMsOf(options),
|
|
47
51
|
});
|
|
@@ -52,6 +56,23 @@ class MobileAndroidPageImpl {
|
|
|
52
56
|
};
|
|
53
57
|
}
|
|
54
58
|
}
|
|
59
|
+
class MobileAndroidLocatorImpl {
|
|
60
|
+
page;
|
|
61
|
+
selector;
|
|
62
|
+
constructor(page, selector) {
|
|
63
|
+
this.page = page;
|
|
64
|
+
this.selector = selector;
|
|
65
|
+
}
|
|
66
|
+
async click(options = {}) {
|
|
67
|
+
return this.page.click(this.selector, options);
|
|
68
|
+
}
|
|
69
|
+
async fill(value, options = {}) {
|
|
70
|
+
return this.page.fill(this.selector, value, options);
|
|
71
|
+
}
|
|
72
|
+
locator(selector) {
|
|
73
|
+
return new MobileAndroidLocatorImpl(this.page, chainMobileSelectorForTransport(this.selector, selector));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
55
76
|
class MobileAndroidDeviceImpl {
|
|
56
77
|
connectInfoRaw;
|
|
57
78
|
#initialPage;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
const UIAUTOMATOR_SELECTOR_KEYS = new Set([
|
|
2
|
+
"text",
|
|
3
|
+
"textcontains",
|
|
4
|
+
"textmatches",
|
|
5
|
+
"textstartswith",
|
|
6
|
+
"classname",
|
|
7
|
+
"classnamematches",
|
|
8
|
+
"description",
|
|
9
|
+
"desc",
|
|
10
|
+
"descriptioncontains",
|
|
11
|
+
"desccontains",
|
|
12
|
+
"descriptionmatches",
|
|
13
|
+
"descmatches",
|
|
14
|
+
"descriptionstartswith",
|
|
15
|
+
"descstartswith",
|
|
16
|
+
"checkable",
|
|
17
|
+
"checked",
|
|
18
|
+
"clickable",
|
|
19
|
+
"longclickable",
|
|
20
|
+
"scrollable",
|
|
21
|
+
"enabled",
|
|
22
|
+
"focusable",
|
|
23
|
+
"focused",
|
|
24
|
+
"selected",
|
|
25
|
+
"packagename",
|
|
26
|
+
"package",
|
|
27
|
+
"packagenamematches",
|
|
28
|
+
"resourceid",
|
|
29
|
+
"resourceidmatches",
|
|
30
|
+
"index",
|
|
31
|
+
"instance",
|
|
32
|
+
]);
|
|
33
|
+
const SELECTOR_PREFIXES = ["xpath=", "xpath:", "css=", "css:", "uia=", "uia:"];
|
|
34
|
+
function decodeSelectorBody(body) {
|
|
35
|
+
const candidate = body.trim();
|
|
36
|
+
if (candidate.startsWith("\"") && candidate.endsWith("\"")) {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(candidate);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return candidate;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return candidate;
|
|
45
|
+
}
|
|
46
|
+
function parseExplicitSelectorPrefix(selector) {
|
|
47
|
+
const lowered = selector.toLowerCase();
|
|
48
|
+
if (lowered.startsWith("xpath=") || lowered.startsWith("xpath:")) {
|
|
49
|
+
return { flavor: "xpath", prefixLength: 6 };
|
|
50
|
+
}
|
|
51
|
+
if (lowered.startsWith("css=") || lowered.startsWith("css:")) {
|
|
52
|
+
return { flavor: "css", prefixLength: 4 };
|
|
53
|
+
}
|
|
54
|
+
if (lowered.startsWith("uia=") || lowered.startsWith("uia:")) {
|
|
55
|
+
return { flavor: "uia", prefixLength: 4 };
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function findJsonStringEnd(value) {
|
|
60
|
+
if (!value.startsWith("\"")) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
let escaped = false;
|
|
64
|
+
for (let index = 1; index < value.length; index += 1) {
|
|
65
|
+
const char = value[index];
|
|
66
|
+
if (escaped) {
|
|
67
|
+
escaped = false;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (char === "\\") {
|
|
71
|
+
escaped = true;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (char === "\"") {
|
|
75
|
+
return index + 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
function isNormalizedTransportSelector(selector) {
|
|
81
|
+
const trimmed = selector.trim();
|
|
82
|
+
if (!trimmed) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
let index = 0;
|
|
86
|
+
while (index < trimmed.length) {
|
|
87
|
+
const prefix = parseExplicitSelectorPrefix(trimmed.slice(index));
|
|
88
|
+
if (!prefix) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
index += prefix.prefixLength;
|
|
92
|
+
const remainder = trimmed.slice(index);
|
|
93
|
+
if (!remainder.startsWith("\"")) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const jsonEnd = findJsonStringEnd(remainder);
|
|
97
|
+
if (!jsonEnd) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
index += jsonEnd;
|
|
101
|
+
const tail = trimmed.slice(index);
|
|
102
|
+
if (!tail) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
if (!/^\s+/.test(tail)) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
const nextIndex = index + tail.match(/^\s+/)?.[0].length;
|
|
109
|
+
const nextSegment = trimmed.slice(nextIndex).toLowerCase();
|
|
110
|
+
if (!SELECTOR_PREFIXES.some((prefixValue) => nextSegment.startsWith(prefixValue))) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
index = nextIndex;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
function parseUiAutomatorSelectorPrefix(selector) {
|
|
118
|
+
const separatorIndex = selector.search(/[=:]/);
|
|
119
|
+
if (separatorIndex <= 0) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const key = selector.slice(0, separatorIndex).trim().toLowerCase();
|
|
123
|
+
if (!UIAUTOMATOR_SELECTOR_KEYS.has(key)) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return separatorIndex + 1;
|
|
127
|
+
}
|
|
128
|
+
function parseSelectorForTransport(selector) {
|
|
129
|
+
const trimmed = selector.trim();
|
|
130
|
+
const explicit = parseExplicitSelectorPrefix(trimmed);
|
|
131
|
+
if (explicit) {
|
|
132
|
+
return { flavor: explicit.flavor, body: decodeSelectorBody(trimmed.slice(explicit.prefixLength)) };
|
|
133
|
+
}
|
|
134
|
+
const uiAutomatorPrefix = parseUiAutomatorSelectorPrefix(trimmed);
|
|
135
|
+
if (uiAutomatorPrefix) {
|
|
136
|
+
return { flavor: "uia", body: `${trimmed.slice(0, uiAutomatorPrefix - 1)}=${trimmed.slice(uiAutomatorPrefix)}` };
|
|
137
|
+
}
|
|
138
|
+
if (trimmed.startsWith("//") ||
|
|
139
|
+
trimmed.startsWith(".//") ||
|
|
140
|
+
trimmed.startsWith("../") ||
|
|
141
|
+
trimmed.startsWith("/") ||
|
|
142
|
+
trimmed.startsWith("(")) {
|
|
143
|
+
return { flavor: "xpath", body: trimmed };
|
|
144
|
+
}
|
|
145
|
+
return { flavor: "css", body: trimmed };
|
|
146
|
+
}
|
|
147
|
+
export function normalizeMobileSelectorForTransport(selector) {
|
|
148
|
+
const trimmed = selector.trim();
|
|
149
|
+
if (!trimmed) {
|
|
150
|
+
return "";
|
|
151
|
+
}
|
|
152
|
+
if (isNormalizedTransportSelector(trimmed)) {
|
|
153
|
+
return trimmed;
|
|
154
|
+
}
|
|
155
|
+
const parsed = parseSelectorForTransport(selector);
|
|
156
|
+
return `${parsed.flavor}=${JSON.stringify(parsed.body)}`;
|
|
157
|
+
}
|
|
158
|
+
export function chainMobileSelectorForTransport(parent, child) {
|
|
159
|
+
const parentSelector = normalizeMobileSelectorForTransport(parent);
|
|
160
|
+
const childSelector = normalizeMobileSelectorForTransport(child);
|
|
161
|
+
if (!parentSelector) {
|
|
162
|
+
return childSelector;
|
|
163
|
+
}
|
|
164
|
+
if (!childSelector) {
|
|
165
|
+
return parentSelector;
|
|
166
|
+
}
|
|
167
|
+
return `${parentSelector} ${childSelector}`;
|
|
168
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -127,8 +127,16 @@ export interface MobileAndroidLaunchOptions {
|
|
|
127
127
|
stopBeforeLaunch?: boolean;
|
|
128
128
|
timeoutMs?: number;
|
|
129
129
|
}
|
|
130
|
+
export interface MobileAndroidLocator {
|
|
131
|
+
readonly page: MobileAndroidPage;
|
|
132
|
+
readonly selector: string;
|
|
133
|
+
click(options?: CommandOptions): Promise<ClickResult>;
|
|
134
|
+
fill(value: string, options?: CommandOptions): Promise<FillResult>;
|
|
135
|
+
locator(selector: string): MobileAndroidLocator;
|
|
136
|
+
}
|
|
130
137
|
export interface MobileAndroidPage {
|
|
131
138
|
readonly sessionId: string;
|
|
139
|
+
locator(selector: string): MobileAndroidLocator;
|
|
132
140
|
click(selector: string, options?: CommandOptions): Promise<ClickResult>;
|
|
133
141
|
fill(selector: string, value: string, options?: CommandOptions): Promise<FillResult>;
|
|
134
142
|
}
|