alumnium 0.14.0
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/.mocharc.json +10 -0
- package/.vscode/extensions.json +6 -0
- package/.vscode/settings.json +16 -0
- package/README.md +75 -0
- package/dist/Alumni.d.ts +33 -0
- package/dist/Alumni.js +116 -0
- package/dist/Area.d.ts +19 -0
- package/dist/Area.js +43 -0
- package/dist/Cache.d.ts +7 -0
- package/dist/Cache.js +11 -0
- package/dist/Model.d.ts +21 -0
- package/dist/Model.js +47 -0
- package/dist/accessibility/AccessibilityElement.d.ts +14 -0
- package/dist/accessibility/AccessibilityElement.js +15 -0
- package/dist/accessibility/BaseAccessibilityTree.d.ts +6 -0
- package/dist/accessibility/BaseAccessibilityTree.js +2 -0
- package/dist/accessibility/ChromiumAccessibilityTree.d.ts +20 -0
- package/dist/accessibility/ChromiumAccessibilityTree.js +216 -0
- package/dist/accessibility/UIAutomator2AccessibilityTree.d.ts +15 -0
- package/dist/accessibility/UIAutomator2AccessibilityTree.js +149 -0
- package/dist/accessibility/XCUITestAccessibilityTree.d.ts +15 -0
- package/dist/accessibility/XCUITestAccessibilityTree.js +144 -0
- package/dist/accessibility/index.d.ts +5 -0
- package/dist/accessibility/index.js +5 -0
- package/dist/clients/HttpClient.d.ts +27 -0
- package/dist/clients/HttpClient.js +204 -0
- package/dist/clients/typecasting.d.ts +5 -0
- package/dist/clients/typecasting.js +28 -0
- package/dist/drivers/AppiumDriver.d.ts +29 -0
- package/dist/drivers/AppiumDriver.js +187 -0
- package/dist/drivers/BaseDriver.d.ts +19 -0
- package/dist/drivers/BaseDriver.js +2 -0
- package/dist/drivers/PlaywrightDriver.d.ts +29 -0
- package/dist/drivers/PlaywrightDriver.js +146 -0
- package/dist/drivers/SeleniumDriver.d.ts +27 -0
- package/dist/drivers/SeleniumDriver.js +149 -0
- package/dist/drivers/index.d.ts +4 -0
- package/dist/drivers/index.js +1 -0
- package/dist/drivers/keys.d.ts +6 -0
- package/dist/drivers/keys.js +7 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +9 -0
- package/dist/tools/BaseTool.d.ts +10 -0
- package/dist/tools/BaseTool.js +14 -0
- package/dist/tools/ClickTool.d.ts +10 -0
- package/dist/tools/ClickTool.js +11 -0
- package/dist/tools/DragAndDropTool.d.ts +12 -0
- package/dist/tools/DragAndDropTool.js +12 -0
- package/dist/tools/HoverTool.d.ts +10 -0
- package/dist/tools/HoverTool.js +11 -0
- package/dist/tools/NavigateBackTool.d.ts +6 -0
- package/dist/tools/NavigateBackTool.js +16 -0
- package/dist/tools/PressKeyTool.d.ts +11 -0
- package/dist/tools/PressKeyTool.js +11 -0
- package/dist/tools/SelectTool.d.ts +12 -0
- package/dist/tools/SelectTool.js +12 -0
- package/dist/tools/TypeTool.d.ts +12 -0
- package/dist/tools/TypeTool.js +12 -0
- package/dist/tools/toolToSchemaConverter.d.ts +18 -0
- package/dist/tools/toolToSchemaConverter.js +62 -0
- package/dist/utils/logger.d.ts +1 -0
- package/dist/utils/logger.js +33 -0
- package/log/alumnium.log +731 -0
- package/package.json +58 -0
- package/playwright-report/data/9bb9e740488ba36abef0f2c1176f965fea9c0318.zip +0 -0
- package/playwright-report/index.html +85 -0
- package/playwright-report/trace/assets/codeMirrorModule-eyVcHN77.js +24 -0
- package/playwright-report/trace/assets/defaultSettingsView-w0zYjHsW.js +265 -0
- package/playwright-report/trace/codeMirrorModule.C3UTv-Ge.css +1 -0
- package/playwright-report/trace/codicon.DCmgc-ay.ttf +0 -0
- package/playwright-report/trace/defaultSettingsView.TQ8_7ybu.css +1 -0
- package/playwright-report/trace/index.Bx16ehp1.js +2 -0
- package/playwright-report/trace/index.I8N9v4jT.css +1 -0
- package/playwright-report/trace/index.html +43 -0
- package/playwright-report/trace/playwright-logo.svg +9 -0
- package/playwright-report/trace/snapshot.html +21 -0
- package/playwright-report/trace/sw.bundle.js +3 -0
- package/playwright-report/trace/uiMode.Btcz36p_.css +1 -0
- package/playwright-report/trace/uiMode.DRQ310U5.js +5 -0
- package/playwright-report/trace/uiMode.html +17 -0
- package/playwright-report/trace/xtermModule.DYP7pi_n.css +32 -0
- package/reports/test-report.html +1408 -0
- package/src/Alumni.ts +195 -0
- package/src/Area.ts +99 -0
- package/src/Cache.ts +17 -0
- package/src/Model.ts +56 -0
- package/src/accessibility/AccessibilityElement.ts +39 -0
- package/src/accessibility/BaseAccessibilityTree.ts +7 -0
- package/src/accessibility/ChromiumAccessibilityTree.ts +290 -0
- package/src/accessibility/UIAutomator2AccessibilityTree.ts +199 -0
- package/src/accessibility/XCUITestAccessibilityTree.ts +186 -0
- package/src/accessibility/index.ts +5 -0
- package/src/clients/HttpClient.ts +288 -0
- package/src/clients/typecasting.ts +34 -0
- package/src/drivers/AppiumDriver.ts +208 -0
- package/src/drivers/BaseDriver.ts +20 -0
- package/src/drivers/PlaywrightDriver.ts +186 -0
- package/src/drivers/SeleniumDriver.ts +208 -0
- package/src/drivers/index.ts +5 -0
- package/src/drivers/keys.ts +6 -0
- package/src/drivers/scripts/waitFor.js +8 -0
- package/src/drivers/scripts/waiter.js +194 -0
- package/src/index.ts +11 -0
- package/src/tools/BaseTool.ts +29 -0
- package/src/tools/ClickTool.ts +17 -0
- package/src/tools/DragAndDropTool.ts +20 -0
- package/src/tools/HoverTool.ts +17 -0
- package/src/tools/NavigateBackTool.ts +19 -0
- package/src/tools/PressKeyTool.ts +18 -0
- package/src/tools/SelectTool.ts +20 -0
- package/src/tools/TypeTool.ts +19 -0
- package/src/tools/toolToSchemaConverter.ts +83 -0
- package/src/utils/logger.ts +46 -0
- package/test-results/.last-run.json +6 -0
- package/test-results/calculator-Calculator-Tests-addition-default/trace.zip +0 -0
- package/tsconfig.json +27 -0
- package/wdio.conf.ts +45 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { AccessibilityElement } from "./AccessibilityElement.js";
|
|
2
|
+
import { BaseAccessibilityTree } from "./BaseAccessibilityTree.js";
|
|
3
|
+
export class ChromiumAccessibilityTree extends BaseAccessibilityTree {
|
|
4
|
+
constructor(cdpResponse) {
|
|
5
|
+
super();
|
|
6
|
+
this.nextRawId = 0;
|
|
7
|
+
this.raw = null;
|
|
8
|
+
this.cdpResponse = cdpResponse;
|
|
9
|
+
}
|
|
10
|
+
static fromXml(xmlString) {
|
|
11
|
+
const instance = new ChromiumAccessibilityTree({ nodes: [] });
|
|
12
|
+
instance.raw = xmlString;
|
|
13
|
+
return instance;
|
|
14
|
+
}
|
|
15
|
+
toStr() {
|
|
16
|
+
if (this.raw !== null) {
|
|
17
|
+
return this.raw;
|
|
18
|
+
}
|
|
19
|
+
const nodes = this.cdpResponse.nodes;
|
|
20
|
+
const nodeLookup = {};
|
|
21
|
+
for (const node of nodes) {
|
|
22
|
+
nodeLookup[node.nodeId] = node;
|
|
23
|
+
}
|
|
24
|
+
// Build tree structure and convert to XML
|
|
25
|
+
const rootNodes = [];
|
|
26
|
+
for (const node of nodes) {
|
|
27
|
+
if (!node.parentId) {
|
|
28
|
+
const xmlNode = this.nodeToXml(node, nodeLookup);
|
|
29
|
+
rootNodes.push(xmlNode);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Combine all root nodes into a single XML string
|
|
33
|
+
let xmlString = "";
|
|
34
|
+
for (const root of rootNodes) {
|
|
35
|
+
xmlString += this.elementToString(root, 0);
|
|
36
|
+
}
|
|
37
|
+
this.raw = xmlString;
|
|
38
|
+
return this.raw;
|
|
39
|
+
}
|
|
40
|
+
nodeToXml(node, nodeLookup) {
|
|
41
|
+
const role = node.role?.value || "unknown";
|
|
42
|
+
const elem = {
|
|
43
|
+
tag: role,
|
|
44
|
+
attributes: {},
|
|
45
|
+
children: [],
|
|
46
|
+
};
|
|
47
|
+
// Add our own sequential raw_id attribute
|
|
48
|
+
this.nextRawId++;
|
|
49
|
+
elem.attributes.raw_id = String(this.nextRawId);
|
|
50
|
+
// Add all node attributes as XML attributes
|
|
51
|
+
if (node.backendDOMNodeId !== undefined) {
|
|
52
|
+
elem.attributes.backendDOMNodeId = String(node.backendDOMNodeId);
|
|
53
|
+
}
|
|
54
|
+
if (node.nodeId !== undefined) {
|
|
55
|
+
elem.attributes.nodeId = String(node.nodeId);
|
|
56
|
+
}
|
|
57
|
+
if (node.ignored !== undefined) {
|
|
58
|
+
elem.attributes.ignored = String(node.ignored);
|
|
59
|
+
}
|
|
60
|
+
// Add name as attribute if present
|
|
61
|
+
if (node.name?.value) {
|
|
62
|
+
elem.attributes.name = node.name.value;
|
|
63
|
+
}
|
|
64
|
+
// Add properties as attributes
|
|
65
|
+
if (node.properties) {
|
|
66
|
+
for (const prop of node.properties) {
|
|
67
|
+
const propName = prop.name || "";
|
|
68
|
+
const propValue = prop.value;
|
|
69
|
+
if (propValue &&
|
|
70
|
+
typeof propValue === "object" &&
|
|
71
|
+
"value" in propValue) {
|
|
72
|
+
elem.attributes[propName] = String(propValue.value);
|
|
73
|
+
}
|
|
74
|
+
else if (typeof propValue === "object") {
|
|
75
|
+
// Complex property values (like nodeList) are converted to empty string
|
|
76
|
+
elem.attributes[propName] = "";
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
elem.attributes[propName] = String(propValue);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Process children recursively
|
|
84
|
+
if (node.childIds) {
|
|
85
|
+
for (const childId of node.childIds) {
|
|
86
|
+
if (childId in nodeLookup) {
|
|
87
|
+
const childElem = this.nodeToXml(nodeLookup[childId], nodeLookup);
|
|
88
|
+
elem.children.push(childElem);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return elem;
|
|
93
|
+
}
|
|
94
|
+
elementToString(elem, indent) {
|
|
95
|
+
const indentStr = " ".repeat(indent);
|
|
96
|
+
let result = `${indentStr}<${elem.tag}`;
|
|
97
|
+
// Add attributes
|
|
98
|
+
for (const [key, value] of Object.entries(elem.attributes)) {
|
|
99
|
+
// Escape XML special characters
|
|
100
|
+
const escapedValue = value
|
|
101
|
+
.replace(/&/g, "&")
|
|
102
|
+
.replace(/</g, "<")
|
|
103
|
+
.replace(/>/g, ">")
|
|
104
|
+
.replace(/"/g, """)
|
|
105
|
+
.replace(/'/g, "'");
|
|
106
|
+
result += ` ${key}="${escapedValue}"`;
|
|
107
|
+
}
|
|
108
|
+
if (elem.children.length === 0 && !elem.text) {
|
|
109
|
+
result += " />\n";
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
result += ">";
|
|
113
|
+
if (elem.text) {
|
|
114
|
+
result += elem.text;
|
|
115
|
+
}
|
|
116
|
+
if (elem.children.length > 0) {
|
|
117
|
+
result += "\n";
|
|
118
|
+
for (const child of elem.children) {
|
|
119
|
+
result += this.elementToString(child, indent + 1);
|
|
120
|
+
}
|
|
121
|
+
result += indentStr;
|
|
122
|
+
}
|
|
123
|
+
result += `</${elem.tag}>\n`;
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
elementById(rawId) {
|
|
128
|
+
const rawXml = this.toStr();
|
|
129
|
+
const element = this.findElementByRawId(rawXml, rawId);
|
|
130
|
+
if (!element) {
|
|
131
|
+
throw new Error(`No element with raw_id=${rawId} found`);
|
|
132
|
+
}
|
|
133
|
+
const backendNodeIdStr = element.attributes.backendDOMNodeId;
|
|
134
|
+
if (!backendNodeIdStr) {
|
|
135
|
+
throw new Error(`Element with raw_id=${rawId} has no backendDOMNodeId attribute`);
|
|
136
|
+
}
|
|
137
|
+
return new AccessibilityElement(undefined, undefined, undefined, element.tag, undefined, parseInt(backendNodeIdStr));
|
|
138
|
+
}
|
|
139
|
+
scopeToArea(rawId) {
|
|
140
|
+
const rawXml = this.toStr();
|
|
141
|
+
const element = this.findElementByRawId(rawXml, rawId);
|
|
142
|
+
if (!element) {
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
const scopedXml = this.elementToString(element, 0);
|
|
146
|
+
return ChromiumAccessibilityTree.fromXml(scopedXml);
|
|
147
|
+
}
|
|
148
|
+
findElementByRawId(xmlString, targetRawId) {
|
|
149
|
+
// Parse the XML string to find the element
|
|
150
|
+
// This is a simple XML parser for our specific use case
|
|
151
|
+
const elements = this.parseSimpleXml(xmlString);
|
|
152
|
+
const search = (elem) => {
|
|
153
|
+
if (elem.attributes.raw_id === String(targetRawId)) {
|
|
154
|
+
return elem;
|
|
155
|
+
}
|
|
156
|
+
for (const child of elem.children) {
|
|
157
|
+
const result = search(child);
|
|
158
|
+
if (result)
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
};
|
|
163
|
+
for (const elem of elements) {
|
|
164
|
+
const result = search(elem);
|
|
165
|
+
if (result)
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
parseSimpleXml(xmlString) {
|
|
171
|
+
const elements = [];
|
|
172
|
+
const stack = [];
|
|
173
|
+
// Combined regex for all XML tokens (opening tags, closing tags, self-closing tags)
|
|
174
|
+
const tokenRegex = /<\/?([a-zA-Z_][\w:.-]*)((?:\s+[\w:.-]+="[^"]*")*)\s*(\/?)>/g;
|
|
175
|
+
const attrRegex = /([\w:.-]+)="([^"]*)"/g;
|
|
176
|
+
let match;
|
|
177
|
+
while ((match = tokenRegex.exec(xmlString)) !== null) {
|
|
178
|
+
const fullMatch = match[0];
|
|
179
|
+
const tagName = match[1];
|
|
180
|
+
const attrsString = match[2];
|
|
181
|
+
const selfClosing = match[3] === "/";
|
|
182
|
+
const isClosingTag = fullMatch.startsWith("</");
|
|
183
|
+
if (isClosingTag) {
|
|
184
|
+
// Handle closing tag - pop from stack
|
|
185
|
+
if (stack.length > 0 && stack[stack.length - 1].tag === tagName) {
|
|
186
|
+
stack.pop();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
// Handle opening or self-closing tag
|
|
191
|
+
// Parse attributes
|
|
192
|
+
const attributes = {};
|
|
193
|
+
let attrMatch;
|
|
194
|
+
attrRegex.lastIndex = 0;
|
|
195
|
+
while ((attrMatch = attrRegex.exec(attrsString)) !== null) {
|
|
196
|
+
attributes[attrMatch[1]] = attrMatch[2];
|
|
197
|
+
}
|
|
198
|
+
const elem = {
|
|
199
|
+
tag: tagName,
|
|
200
|
+
attributes,
|
|
201
|
+
children: [],
|
|
202
|
+
};
|
|
203
|
+
if (stack.length > 0) {
|
|
204
|
+
stack[stack.length - 1].children.push(elem);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
elements.push(elem);
|
|
208
|
+
}
|
|
209
|
+
if (!selfClosing) {
|
|
210
|
+
stack.push(elem);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return elements;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AccessibilityElement } from "./AccessibilityElement.js";
|
|
2
|
+
import { BaseAccessibilityTree } from "./BaseAccessibilityTree.js";
|
|
3
|
+
export declare class UIAutomator2AccessibilityTree extends BaseAccessibilityTree {
|
|
4
|
+
private xmlString;
|
|
5
|
+
private nextRawId;
|
|
6
|
+
private raw;
|
|
7
|
+
constructor(xmlString: string);
|
|
8
|
+
toStr(): string;
|
|
9
|
+
private addRawIds;
|
|
10
|
+
elementById(rawId: number): AccessibilityElement;
|
|
11
|
+
scopeToArea(rawId: number): UIAutomator2AccessibilityTree;
|
|
12
|
+
private findElementByRawId;
|
|
13
|
+
private elementToString;
|
|
14
|
+
private parseSimpleXml;
|
|
15
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { AccessibilityElement } from "./AccessibilityElement.js";
|
|
2
|
+
import { BaseAccessibilityTree } from "./BaseAccessibilityTree.js";
|
|
3
|
+
export class UIAutomator2AccessibilityTree extends BaseAccessibilityTree {
|
|
4
|
+
constructor(xmlString) {
|
|
5
|
+
super();
|
|
6
|
+
this.nextRawId = 0;
|
|
7
|
+
this.raw = null;
|
|
8
|
+
// Clean multiple XML declaration lines from page source
|
|
9
|
+
const xmlDeclarationPattern = /^\s*<\?xml.*\?>\s*$/gm;
|
|
10
|
+
const lines = xmlString.split("\n");
|
|
11
|
+
const cleanedLines = lines.filter((line) => !xmlDeclarationPattern.test(line));
|
|
12
|
+
const cleanedXmlContent = cleanedLines.join("\n");
|
|
13
|
+
this.xmlString = `<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n<root>\n${cleanedXmlContent}\n</root>`;
|
|
14
|
+
}
|
|
15
|
+
toStr() {
|
|
16
|
+
if (this.raw !== null) {
|
|
17
|
+
return this.raw;
|
|
18
|
+
}
|
|
19
|
+
// Parse the XML
|
|
20
|
+
const elements = this.parseSimpleXml(this.xmlString);
|
|
21
|
+
// Add raw_id attributes recursively
|
|
22
|
+
for (const elem of elements) {
|
|
23
|
+
this.addRawIds(elem);
|
|
24
|
+
}
|
|
25
|
+
// Serialize back to string
|
|
26
|
+
let result = "";
|
|
27
|
+
for (const elem of elements) {
|
|
28
|
+
result += this.elementToString(elem, 0);
|
|
29
|
+
}
|
|
30
|
+
this.raw = result;
|
|
31
|
+
return this.raw;
|
|
32
|
+
}
|
|
33
|
+
addRawIds(elem) {
|
|
34
|
+
this.nextRawId++;
|
|
35
|
+
elem.attributes.raw_id = String(this.nextRawId);
|
|
36
|
+
for (const child of elem.children) {
|
|
37
|
+
this.addRawIds(child);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
elementById(rawId) {
|
|
41
|
+
const rawXml = this.toStr();
|
|
42
|
+
const element = this.findElementByRawId(rawXml, rawId);
|
|
43
|
+
if (!element) {
|
|
44
|
+
throw new Error(`No element with raw_id=${rawId} found`);
|
|
45
|
+
}
|
|
46
|
+
// Extract properties for UIAutomator2
|
|
47
|
+
return new AccessibilityElement(rawId, undefined, undefined, element.attributes.class || element.tag, undefined, undefined, element.attributes["resource-id"], undefined, element.attributes.text, element.attributes["content-desc"], element.attributes.bounds);
|
|
48
|
+
}
|
|
49
|
+
scopeToArea(rawId) {
|
|
50
|
+
const rawXml = this.toStr();
|
|
51
|
+
const element = this.findElementByRawId(rawXml, rawId);
|
|
52
|
+
if (!element) {
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
const scopedXml = this.elementToString(element, 0);
|
|
56
|
+
return new UIAutomator2AccessibilityTree(scopedXml);
|
|
57
|
+
}
|
|
58
|
+
findElementByRawId(xmlString, targetRawId) {
|
|
59
|
+
const elements = this.parseSimpleXml(xmlString);
|
|
60
|
+
const search = (elem) => {
|
|
61
|
+
if (elem.attributes.raw_id === String(targetRawId)) {
|
|
62
|
+
return elem;
|
|
63
|
+
}
|
|
64
|
+
for (const child of elem.children) {
|
|
65
|
+
const result = search(child);
|
|
66
|
+
if (result)
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
};
|
|
71
|
+
for (const elem of elements) {
|
|
72
|
+
const result = search(elem);
|
|
73
|
+
if (result)
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
elementToString(elem, indent) {
|
|
79
|
+
const indentStr = " ".repeat(indent);
|
|
80
|
+
let result = `${indentStr}<${elem.tag}`;
|
|
81
|
+
// Add attributes
|
|
82
|
+
for (const [key, value] of Object.entries(elem.attributes)) {
|
|
83
|
+
const escapedValue = value
|
|
84
|
+
.replace(/&/g, "&")
|
|
85
|
+
.replace(/</g, "<")
|
|
86
|
+
.replace(/>/g, ">")
|
|
87
|
+
.replace(/"/g, """)
|
|
88
|
+
.replace(/'/g, "'");
|
|
89
|
+
result += ` ${key}="${escapedValue}"`;
|
|
90
|
+
}
|
|
91
|
+
if (elem.children.length === 0) {
|
|
92
|
+
result += " />\n";
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
result += ">\n";
|
|
96
|
+
for (const child of elem.children) {
|
|
97
|
+
result += this.elementToString(child, indent + 1);
|
|
98
|
+
}
|
|
99
|
+
result += `${indentStr}</${elem.tag}>\n`;
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
parseSimpleXml(xmlString) {
|
|
104
|
+
const elements = [];
|
|
105
|
+
const stack = [];
|
|
106
|
+
// Combined regex for all XML tokens (opening tags, closing tags, self-closing tags)
|
|
107
|
+
const tokenRegex = /<\/?([a-zA-Z_][\w:.-]*)((?:\s+[\w:.-]+="[^"]*")*)\s*(\/?)>/g;
|
|
108
|
+
const attrRegex = /([\w:.-]+)="([^"]*)"/g;
|
|
109
|
+
let match;
|
|
110
|
+
while ((match = tokenRegex.exec(xmlString)) !== null) {
|
|
111
|
+
const fullMatch = match[0];
|
|
112
|
+
const tagName = match[1];
|
|
113
|
+
const attrsString = match[2];
|
|
114
|
+
const selfClosing = match[3] === "/";
|
|
115
|
+
const isClosingTag = fullMatch.startsWith("</");
|
|
116
|
+
if (isClosingTag) {
|
|
117
|
+
// Handle closing tag - pop from stack
|
|
118
|
+
if (stack.length > 0 && stack[stack.length - 1].tag === tagName) {
|
|
119
|
+
stack.pop();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Handle opening or self-closing tag
|
|
124
|
+
// Parse attributes
|
|
125
|
+
const attributes = {};
|
|
126
|
+
let attrMatch;
|
|
127
|
+
attrRegex.lastIndex = 0;
|
|
128
|
+
while ((attrMatch = attrRegex.exec(attrsString)) !== null) {
|
|
129
|
+
attributes[attrMatch[1]] = attrMatch[2];
|
|
130
|
+
}
|
|
131
|
+
const elem = {
|
|
132
|
+
tag: tagName,
|
|
133
|
+
attributes,
|
|
134
|
+
children: [],
|
|
135
|
+
};
|
|
136
|
+
if (stack.length > 0) {
|
|
137
|
+
stack[stack.length - 1].children.push(elem);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
elements.push(elem);
|
|
141
|
+
}
|
|
142
|
+
if (!selfClosing) {
|
|
143
|
+
stack.push(elem);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return elements;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AccessibilityElement } from "./AccessibilityElement.js";
|
|
2
|
+
import { BaseAccessibilityTree } from "./BaseAccessibilityTree.js";
|
|
3
|
+
export declare class XCUITestAccessibilityTree extends BaseAccessibilityTree {
|
|
4
|
+
private xmlString;
|
|
5
|
+
private nextRawId;
|
|
6
|
+
private raw;
|
|
7
|
+
constructor(xmlString: string);
|
|
8
|
+
toStr(): string;
|
|
9
|
+
private addRawIds;
|
|
10
|
+
elementById(rawId: number): AccessibilityElement;
|
|
11
|
+
scopeToArea(rawId: number): XCUITestAccessibilityTree;
|
|
12
|
+
private findElementByRawId;
|
|
13
|
+
private elementToString;
|
|
14
|
+
private parseSimpleXml;
|
|
15
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { AccessibilityElement } from "./AccessibilityElement.js";
|
|
2
|
+
import { BaseAccessibilityTree } from "./BaseAccessibilityTree.js";
|
|
3
|
+
export class XCUITestAccessibilityTree extends BaseAccessibilityTree {
|
|
4
|
+
constructor(xmlString) {
|
|
5
|
+
super();
|
|
6
|
+
this.nextRawId = 0;
|
|
7
|
+
this.raw = null;
|
|
8
|
+
this.xmlString = xmlString;
|
|
9
|
+
}
|
|
10
|
+
toStr() {
|
|
11
|
+
if (this.raw !== null) {
|
|
12
|
+
return this.raw;
|
|
13
|
+
}
|
|
14
|
+
// Parse the XML
|
|
15
|
+
const elements = this.parseSimpleXml(this.xmlString);
|
|
16
|
+
// Add raw_id attributes recursively
|
|
17
|
+
for (const elem of elements) {
|
|
18
|
+
this.addRawIds(elem);
|
|
19
|
+
}
|
|
20
|
+
// Serialize back to string
|
|
21
|
+
let result = "";
|
|
22
|
+
for (const elem of elements) {
|
|
23
|
+
result += this.elementToString(elem, 0);
|
|
24
|
+
}
|
|
25
|
+
this.raw = result;
|
|
26
|
+
return this.raw;
|
|
27
|
+
}
|
|
28
|
+
addRawIds(elem) {
|
|
29
|
+
this.nextRawId++;
|
|
30
|
+
elem.attributes.raw_id = String(this.nextRawId);
|
|
31
|
+
for (const child of elem.children) {
|
|
32
|
+
this.addRawIds(child);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
elementById(rawId) {
|
|
36
|
+
const rawXml = this.toStr();
|
|
37
|
+
const element = this.findElementByRawId(rawXml, rawId);
|
|
38
|
+
if (!element) {
|
|
39
|
+
throw new Error(`No element with raw_id=${rawId} found`);
|
|
40
|
+
}
|
|
41
|
+
// Extract properties for XCUITest
|
|
42
|
+
return new AccessibilityElement(rawId, element.attributes.name, element.attributes.label, element.tag, element.attributes.value);
|
|
43
|
+
}
|
|
44
|
+
scopeToArea(rawId) {
|
|
45
|
+
const rawXml = this.toStr();
|
|
46
|
+
const element = this.findElementByRawId(rawXml, rawId);
|
|
47
|
+
if (!element) {
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
const scopedXml = this.elementToString(element, 0);
|
|
51
|
+
return new XCUITestAccessibilityTree(scopedXml);
|
|
52
|
+
}
|
|
53
|
+
findElementByRawId(xmlString, targetRawId) {
|
|
54
|
+
const elements = this.parseSimpleXml(xmlString);
|
|
55
|
+
const search = (elem) => {
|
|
56
|
+
if (elem.attributes.raw_id === String(targetRawId)) {
|
|
57
|
+
return elem;
|
|
58
|
+
}
|
|
59
|
+
for (const child of elem.children) {
|
|
60
|
+
const result = search(child);
|
|
61
|
+
if (result)
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
};
|
|
66
|
+
for (const elem of elements) {
|
|
67
|
+
const result = search(elem);
|
|
68
|
+
if (result)
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
elementToString(elem, indent) {
|
|
74
|
+
const indentStr = " ".repeat(indent);
|
|
75
|
+
let result = `${indentStr}<${elem.tag}`;
|
|
76
|
+
// Add attributes
|
|
77
|
+
for (const [key, value] of Object.entries(elem.attributes)) {
|
|
78
|
+
const escapedValue = value
|
|
79
|
+
.replace(/&/g, "&")
|
|
80
|
+
.replace(/</g, "<")
|
|
81
|
+
.replace(/>/g, ">")
|
|
82
|
+
.replace(/"/g, """)
|
|
83
|
+
.replace(/'/g, "'");
|
|
84
|
+
result += ` ${key}="${escapedValue}"`;
|
|
85
|
+
}
|
|
86
|
+
if (elem.children.length === 0) {
|
|
87
|
+
result += " />\n";
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
result += ">\n";
|
|
91
|
+
for (const child of elem.children) {
|
|
92
|
+
result += this.elementToString(child, indent + 1);
|
|
93
|
+
}
|
|
94
|
+
result += `${indentStr}</${elem.tag}>\n`;
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
parseSimpleXml(xmlString) {
|
|
99
|
+
const elements = [];
|
|
100
|
+
const stack = [];
|
|
101
|
+
// Combined regex for all XML tokens (opening tags, closing tags, self-closing tags)
|
|
102
|
+
const tokenRegex = /<\/?([a-zA-Z_][\w:.-]*)((?:\s+[\w:.-]+="[^"]*")*)\s*(\/?)>/g;
|
|
103
|
+
const attrRegex = /([\w:.-]+)="([^"]*)"/g;
|
|
104
|
+
let match;
|
|
105
|
+
while ((match = tokenRegex.exec(xmlString)) !== null) {
|
|
106
|
+
const fullMatch = match[0];
|
|
107
|
+
const tagName = match[1];
|
|
108
|
+
const attrsString = match[2];
|
|
109
|
+
const selfClosing = match[3] === "/";
|
|
110
|
+
const isClosingTag = fullMatch.startsWith("</");
|
|
111
|
+
if (isClosingTag) {
|
|
112
|
+
// Handle closing tag - pop from stack
|
|
113
|
+
if (stack.length > 0 && stack[stack.length - 1].tag === tagName) {
|
|
114
|
+
stack.pop();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// Handle opening or self-closing tag
|
|
119
|
+
// Parse attributes
|
|
120
|
+
const attributes = {};
|
|
121
|
+
let attrMatch;
|
|
122
|
+
attrRegex.lastIndex = 0;
|
|
123
|
+
while ((attrMatch = attrRegex.exec(attrsString)) !== null) {
|
|
124
|
+
attributes[attrMatch[1]] = attrMatch[2];
|
|
125
|
+
}
|
|
126
|
+
const elem = {
|
|
127
|
+
tag: tagName,
|
|
128
|
+
attributes,
|
|
129
|
+
children: [],
|
|
130
|
+
};
|
|
131
|
+
if (stack.length > 0) {
|
|
132
|
+
stack[stack.length - 1].children.push(elem);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
elements.push(elem);
|
|
136
|
+
}
|
|
137
|
+
if (!selfClosing) {
|
|
138
|
+
stack.push(elem);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return elements;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AccessibilityElement } from "./AccessibilityElement.js";
|
|
2
|
+
export { BaseAccessibilityTree } from "./BaseAccessibilityTree.js";
|
|
3
|
+
export { ChromiumAccessibilityTree } from "./ChromiumAccessibilityTree.js";
|
|
4
|
+
export { UIAutomator2AccessibilityTree } from "./UIAutomator2AccessibilityTree.js";
|
|
5
|
+
export { XCUITestAccessibilityTree } from "./XCUITestAccessibilityTree.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AccessibilityElement } from "./AccessibilityElement.js";
|
|
2
|
+
export { BaseAccessibilityTree } from "./BaseAccessibilityTree.js";
|
|
3
|
+
export { ChromiumAccessibilityTree } from "./ChromiumAccessibilityTree.js";
|
|
4
|
+
export { UIAutomator2AccessibilityTree } from "./UIAutomator2AccessibilityTree.js";
|
|
5
|
+
export { XCUITestAccessibilityTree } from "./XCUITestAccessibilityTree.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BaseTool } from "../tools/BaseTool.js";
|
|
2
|
+
import { Data } from "./typecasting.js";
|
|
3
|
+
export declare class HttpClient {
|
|
4
|
+
private platform;
|
|
5
|
+
private tools;
|
|
6
|
+
private baseUrl;
|
|
7
|
+
private sessionId;
|
|
8
|
+
private sessionPromise;
|
|
9
|
+
private timeout;
|
|
10
|
+
constructor(baseUrl: string, platform: string, tools: Record<string, new (...args: any[]) => BaseTool>);
|
|
11
|
+
private fetchWithTimeout;
|
|
12
|
+
private ensureSession;
|
|
13
|
+
quit(): Promise<void>;
|
|
14
|
+
planActions(goal: string, accessibilityTree: string): Promise<string[]>;
|
|
15
|
+
addExample(goal: string, actions: string[]): Promise<void>;
|
|
16
|
+
clearExamples(): Promise<void>;
|
|
17
|
+
executeAction(goal: string, step: string, accessibilityTree: string): Promise<any[]>;
|
|
18
|
+
retrieve(statement: string, accessibilityTree: string, title: string, url: string, screenshot?: string): Promise<[string, Data]>;
|
|
19
|
+
findArea(description: string, accessibilityTree: string): Promise<{
|
|
20
|
+
id: number;
|
|
21
|
+
explanation: string;
|
|
22
|
+
}>;
|
|
23
|
+
findElement(description: string, accessibilityTree: string): Promise<any>;
|
|
24
|
+
saveCache(): Promise<void>;
|
|
25
|
+
discardCache(): Promise<void>;
|
|
26
|
+
getStats(): Promise<Record<string, Record<string, number>>>;
|
|
27
|
+
}
|