appium-novawindows2-driver 0.1.2 → 0.1.4
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/build/lib/commands/actions.d.ts +9 -9
- package/build/lib/commands/actions.d.ts.map +1 -1
- package/build/lib/commands/actions.js.map +1 -1
- package/build/lib/commands/app.d.ts +10 -10
- package/build/lib/commands/app.d.ts.map +1 -1
- package/build/lib/commands/app.js.map +1 -1
- package/build/lib/commands/device.d.ts +2 -2
- package/build/lib/commands/device.d.ts.map +1 -1
- package/build/lib/commands/device.js.map +1 -1
- package/build/lib/commands/element.d.ts +13 -13
- package/build/lib/commands/element.d.ts.map +1 -1
- package/build/lib/commands/element.js.map +1 -1
- package/build/lib/commands/extension.d.ts +28 -28
- package/build/lib/commands/extension.d.ts.map +1 -1
- package/build/lib/commands/extension.js +4 -1
- package/build/lib/commands/extension.js.map +1 -1
- package/build/lib/commands/index.d.ts +63 -63
- package/build/lib/commands/index.d.ts.map +1 -1
- package/build/lib/commands/powershell.d.ts +5 -5
- package/build/lib/commands/powershell.d.ts.map +1 -1
- package/build/lib/commands/powershell.js +58 -48
- package/build/lib/commands/powershell.js.map +1 -1
- package/build/lib/commands/system.d.ts +2 -2
- package/build/lib/commands/system.d.ts.map +1 -1
- package/build/lib/driver.d.ts +5 -1
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +21 -4
- package/build/lib/driver.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -0
- package/package.json +10 -6
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -97
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -33
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
- package/.github/workflows/lint-build.yml +0 -30
- package/.github/workflows/release.yml +0 -39
- package/.releaserc +0 -41
- package/CHANGELOG.md +0 -33
- package/eslint.config.mjs +0 -11
- package/examples/C#/CalculatorTest/CalculatorTest/CalculatorSession.cs +0 -44
- package/examples/C#/CalculatorTest/CalculatorTest/CalculatorTest.csproj +0 -24
- package/examples/C#/CalculatorTest/CalculatorTest/ScenarioStandard.cs +0 -121
- package/examples/C#/CalculatorTest/CalculatorTest/ScenarioStandardInvoke.cs +0 -121
- package/examples/C#/CalculatorTest/CalculatorTest.sln +0 -16
- package/lib/commands/actions.ts +0 -229
- package/lib/commands/app.ts +0 -227
- package/lib/commands/device.ts +0 -41
- package/lib/commands/element.ts +0 -242
- package/lib/commands/extension.ts +0 -636
- package/lib/commands/functions.ts +0 -192
- package/lib/commands/index.ts +0 -28
- package/lib/commands/powershell.ts +0 -246
- package/lib/commands/system.ts +0 -7
- package/lib/constants.ts +0 -1
- package/lib/constraints.ts +0 -43
- package/lib/driver.ts +0 -247
- package/lib/enums.ts +0 -96
- package/lib/powershell/common.ts +0 -137
- package/lib/powershell/conditions.ts +0 -169
- package/lib/powershell/converter.ts +0 -373
- package/lib/powershell/core.ts +0 -29
- package/lib/powershell/elements.ts +0 -584
- package/lib/powershell/index.ts +0 -7
- package/lib/powershell/regex.ts +0 -77
- package/lib/powershell/types.ts +0 -208
- package/lib/util.ts +0 -52
- package/lib/winapi/types/index.ts +0 -7
- package/lib/winapi/types/input.ts +0 -12
- package/lib/winapi/types/keyeventf.ts +0 -14
- package/lib/winapi/types/mouseeventf.ts +0 -37
- package/lib/winapi/types/scancode.ts +0 -96
- package/lib/winapi/types/systemmetric.ts +0 -215
- package/lib/winapi/types/virtualkey.ts +0 -354
- package/lib/winapi/types/xmousebutton.ts +0 -8
- package/lib/winapi/user32.ts +0 -842
- package/lib/xpath/core.ts +0 -699
- package/lib/xpath/functions.ts +0 -366
- package/lib/xpath/index.ts +0 -2
- package/temp_log.txt +0 -215
- package/tsconfig.json +0 -13
- package/verify_driver.js +0 -142
package/lib/xpath/core.ts
DELETED
|
@@ -1,699 +0,0 @@
|
|
|
1
|
-
import { Element } from '@appium/types';
|
|
2
|
-
import { W3C_ELEMENT_KEY, errors } from '@appium/base-driver';
|
|
3
|
-
|
|
4
|
-
import XPathAnalyzer, {
|
|
5
|
-
ABSOLUTE_LOCATION_PATH,
|
|
6
|
-
ADDITIVE,
|
|
7
|
-
AND,
|
|
8
|
-
DIVISIONAL,
|
|
9
|
-
EQUALITY,
|
|
10
|
-
ExprNode,
|
|
11
|
-
FILTER,
|
|
12
|
-
FUNCTION_CALL,
|
|
13
|
-
GREATER_THAN,
|
|
14
|
-
GREATER_THAN_OR_EQUAL,
|
|
15
|
-
INEQUALITY,
|
|
16
|
-
LAST,
|
|
17
|
-
LESS_THAN,
|
|
18
|
-
LESS_THAN_OR_EQUAL,
|
|
19
|
-
LITERAL,
|
|
20
|
-
LocationNode,
|
|
21
|
-
MODULUS,
|
|
22
|
-
MULTIPLICATIVE,
|
|
23
|
-
NEGATION,
|
|
24
|
-
NUMBER,
|
|
25
|
-
OR,
|
|
26
|
-
PATH,
|
|
27
|
-
POSITION,
|
|
28
|
-
RELATIVE_LOCATION_PATH,
|
|
29
|
-
SUBTRACTIVE,
|
|
30
|
-
UNION,
|
|
31
|
-
StepNode,
|
|
32
|
-
PROCESSING_INSTRUCTION_TEST,
|
|
33
|
-
NodeTestNode,
|
|
34
|
-
NODE_NAME_TEST,
|
|
35
|
-
NODE_TYPE_TEST,
|
|
36
|
-
NODE,
|
|
37
|
-
ANCESTOR,
|
|
38
|
-
ANCESTOR_OR_SELF,
|
|
39
|
-
ATTRIBUTE,
|
|
40
|
-
CHILD,
|
|
41
|
-
DESCENDANT,
|
|
42
|
-
DESCENDANT_OR_SELF,
|
|
43
|
-
FOLLOWING,
|
|
44
|
-
FOLLOWING_SIBLING,
|
|
45
|
-
NAMESPACE,
|
|
46
|
-
PARENT,
|
|
47
|
-
PRECEDING,
|
|
48
|
-
PRECEDING_SIBLING,
|
|
49
|
-
SELF,
|
|
50
|
-
BOOLEAN,
|
|
51
|
-
STRING,
|
|
52
|
-
} from 'xpath-analyzer';
|
|
53
|
-
|
|
54
|
-
import {
|
|
55
|
-
Property,
|
|
56
|
-
PSControlType,
|
|
57
|
-
Condition,
|
|
58
|
-
FalseCondition,
|
|
59
|
-
PropertyCondition,
|
|
60
|
-
TrueCondition,
|
|
61
|
-
AutomationElement,
|
|
62
|
-
FoundAutomationElement,
|
|
63
|
-
AutomationElementGroup,
|
|
64
|
-
TreeScope,
|
|
65
|
-
AndCondition,
|
|
66
|
-
OrCondition,
|
|
67
|
-
Int32Property,
|
|
68
|
-
PSInt32,
|
|
69
|
-
PSString,
|
|
70
|
-
StringProperty,
|
|
71
|
-
BooleanProperty,
|
|
72
|
-
PSBoolean,
|
|
73
|
-
PSInt32Array,
|
|
74
|
-
PSOrientationType
|
|
75
|
-
} from '../powershell';
|
|
76
|
-
|
|
77
|
-
import { handleFunctionCall } from './functions';
|
|
78
|
-
|
|
79
|
-
const OptimizeLastStep = Symbol.for('LastStep');
|
|
80
|
-
|
|
81
|
-
const XPathAllowedProperties = Object.freeze([
|
|
82
|
-
Property.ACCELERATOR_KEY,
|
|
83
|
-
Property.ACCESS_KEY,
|
|
84
|
-
Property.AUTOMATION_ID,
|
|
85
|
-
Property.CLASS_NAME,
|
|
86
|
-
Property.FRAMEWORK_ID,
|
|
87
|
-
Property.HAS_KEYBOARD_FOCUS,
|
|
88
|
-
Property.HELP_TEXT,
|
|
89
|
-
Property.IS_CONTENT_ELEMENT,
|
|
90
|
-
Property.IS_CONTROL_ELEMENT,
|
|
91
|
-
Property.IS_ENABLED,
|
|
92
|
-
Property.IS_KEYBOARD_FOCUSABLE,
|
|
93
|
-
Property.IS_OFFSCREEN,
|
|
94
|
-
Property.IS_PASSWORD,
|
|
95
|
-
Property.IS_REQUIRED_FOR_FORM,
|
|
96
|
-
Property.ITEM_STATUS,
|
|
97
|
-
Property.ITEM_TYPE,
|
|
98
|
-
Property.LOCALIZED_CONTROL_TYPE,
|
|
99
|
-
Property.NAME,
|
|
100
|
-
Property.ORIENTATION,
|
|
101
|
-
Property.PROCESS_ID,
|
|
102
|
-
Property.RUNTIME_ID,
|
|
103
|
-
] as const);
|
|
104
|
-
|
|
105
|
-
type XPathAllowedProperties = typeof XPathAllowedProperties[number];
|
|
106
|
-
|
|
107
|
-
export async function xpathToElIdOrIds(selector: string, mult: boolean, context: string | undefined, sendPowerShellCommand: (command: string) => Promise<string>): Promise<Element | Element[]> {
|
|
108
|
-
let parsedXPath: ExprNode;
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
parsedXPath = new XPathAnalyzer(selector).parse();
|
|
112
|
-
} catch (error) {
|
|
113
|
-
if (error instanceof Error) {
|
|
114
|
-
throw new errors.InvalidSelectorError(`Malformed XPath: ${error.message}`);
|
|
115
|
-
} else {
|
|
116
|
-
throw new errors.InvalidSelectorError('Malformed XPath');
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (!mult) {
|
|
121
|
-
if (parsedXPath.type === UNION) {
|
|
122
|
-
const lhsLastStep = findLastStep(parsedXPath.lhs);
|
|
123
|
-
const rhsLastStep = findLastStep(parsedXPath.rhs);
|
|
124
|
-
if (lhsLastStep) {
|
|
125
|
-
lhsLastStep[lhsLastStep.length - 1][OptimizeLastStep] = true;
|
|
126
|
-
}
|
|
127
|
-
if (rhsLastStep) {
|
|
128
|
-
rhsLastStep[rhsLastStep.length - 1][OptimizeLastStep] = true;
|
|
129
|
-
}
|
|
130
|
-
} else {
|
|
131
|
-
const lastStep = findLastStep(parsedXPath);
|
|
132
|
-
if (lastStep && lastStep[lastStep.length - 1].predicates.every(predicateProcessableBeforeNode)) {
|
|
133
|
-
lastStep[lastStep.length - 1][OptimizeLastStep] = true;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (parsedXPath.type === 'absolute-location-path' && parsedXPath.steps[0].axis === CHILD) {
|
|
139
|
-
parsedXPath.steps[0].axis = SELF;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const foundElements = await processExprNode<FoundAutomationElement>(parsedXPath, context ? new FoundAutomationElement(context) : AutomationElement.automationRoot, sendPowerShellCommand);
|
|
143
|
-
const els = foundElements.filter((el) => el instanceof FoundAutomationElement).map((el) => ({ [W3C_ELEMENT_KEY]: el.runtimeId }));
|
|
144
|
-
|
|
145
|
-
if (mult) {
|
|
146
|
-
return els;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (els.length === 0) {
|
|
150
|
-
throw new errors.NoSuchElementError();
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return els[0];
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export async function processExprNode<T>(exprNode: ExprNode, context: AutomationElement, sendPowerShellCommand: (command: string) => Promise<string>): Promise<T[]> {
|
|
157
|
-
switch (exprNode.type) {
|
|
158
|
-
case NUMBER:
|
|
159
|
-
return [exprNode.number as T];
|
|
160
|
-
case LITERAL:
|
|
161
|
-
return [exprNode.string as T];
|
|
162
|
-
case UNION:
|
|
163
|
-
return [...await processExprNode<T>(exprNode.lhs, context, sendPowerShellCommand), ...await processExprNode<T>(exprNode.rhs, context, sendPowerShellCommand)];
|
|
164
|
-
case FUNCTION_CALL:
|
|
165
|
-
return await handleFunctionCall(exprNode.name, context, sendPowerShellCommand, ...exprNode.args);
|
|
166
|
-
case ABSOLUTE_LOCATION_PATH:
|
|
167
|
-
case RELATIVE_LOCATION_PATH: {
|
|
168
|
-
const result: T[][] = [];
|
|
169
|
-
for (const element of convertToElementArray(context)) {
|
|
170
|
-
result.push(await handleLocationNode(exprNode, element, sendPowerShellCommand) as T[]);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return result.flat();
|
|
174
|
-
}
|
|
175
|
-
case PATH: {
|
|
176
|
-
const filterResult = await processExprNode<T>(exprNode.filter, context, sendPowerShellCommand);
|
|
177
|
-
const result: T[][] = [];
|
|
178
|
-
for (const item of filterResult) {
|
|
179
|
-
if (item instanceof AutomationElement) {
|
|
180
|
-
const itemAfterSteps = await handleLocationNode({
|
|
181
|
-
type: RELATIVE_LOCATION_PATH,
|
|
182
|
-
steps: exprNode.steps,
|
|
183
|
-
}, item, sendPowerShellCommand);
|
|
184
|
-
result.push(itemAfterSteps as T[]);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return result.flat();
|
|
189
|
-
}
|
|
190
|
-
case FILTER: {
|
|
191
|
-
const result: T[] = [];
|
|
192
|
-
const exprResult = await processExprNode<T>(exprNode.primary, context, sendPowerShellCommand);
|
|
193
|
-
for (const item of exprResult) {
|
|
194
|
-
if (item instanceof AutomationElement) {
|
|
195
|
-
const filteredItem = await executeStep({
|
|
196
|
-
axis: SELF,
|
|
197
|
-
test: {
|
|
198
|
-
type: NODE_TYPE_TEST,
|
|
199
|
-
name: NODE,
|
|
200
|
-
},
|
|
201
|
-
predicates: exprNode.predicates,
|
|
202
|
-
}, item, sendPowerShellCommand) as T;
|
|
203
|
-
result.push(filteredItem);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return result;
|
|
208
|
-
}
|
|
209
|
-
case OR:
|
|
210
|
-
case AND: {
|
|
211
|
-
const [lhs] = await handleFunctionCall<T>(BOOLEAN, context, sendPowerShellCommand, exprNode.lhs);
|
|
212
|
-
const [rhs] = await handleFunctionCall<T>(BOOLEAN, context, sendPowerShellCommand, exprNode.rhs);
|
|
213
|
-
|
|
214
|
-
if (exprNode.type === AND) {
|
|
215
|
-
return [lhs && rhs];
|
|
216
|
-
} else {
|
|
217
|
-
return [lhs || rhs];
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
case NEGATION:
|
|
221
|
-
return [-await handleFunctionCall<T>(NUMBER, context, sendPowerShellCommand, exprNode.lhs) as T];
|
|
222
|
-
case EQUALITY:
|
|
223
|
-
case INEQUALITY: {
|
|
224
|
-
const [lhs] = await handleFunctionCall<string>(STRING, context, sendPowerShellCommand, exprNode.lhs);
|
|
225
|
-
const [rhs] = await handleFunctionCall<string>(STRING, context, sendPowerShellCommand, exprNode.rhs);
|
|
226
|
-
if (isNaN(Number(lhs)) || isNaN(Number(rhs))) {
|
|
227
|
-
return [exprNode.type === EQUALITY ? (lhs === rhs) as T : (lhs !== rhs) as T];
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return [exprNode.type === EQUALITY ? (Number(lhs) === Number(rhs)) as T : (Number(lhs) !== Number(rhs)) as T];
|
|
231
|
-
}
|
|
232
|
-
case ADDITIVE:
|
|
233
|
-
case DIVISIONAL:
|
|
234
|
-
case GREATER_THAN:
|
|
235
|
-
case GREATER_THAN_OR_EQUAL:
|
|
236
|
-
case LESS_THAN:
|
|
237
|
-
case LESS_THAN_OR_EQUAL:
|
|
238
|
-
case MODULUS:
|
|
239
|
-
case MULTIPLICATIVE:
|
|
240
|
-
case SUBTRACTIVE:
|
|
241
|
-
{
|
|
242
|
-
const [lhs] = await handleFunctionCall<number>(NUMBER, context, sendPowerShellCommand, exprNode.lhs);
|
|
243
|
-
const [rhs] = await handleFunctionCall<number>(NUMBER, context, sendPowerShellCommand, exprNode.rhs);
|
|
244
|
-
|
|
245
|
-
switch (exprNode.type) {
|
|
246
|
-
case ADDITIVE:
|
|
247
|
-
return [(lhs + rhs) as T];
|
|
248
|
-
case DIVISIONAL:
|
|
249
|
-
return [(lhs / rhs) as T];
|
|
250
|
-
case GREATER_THAN:
|
|
251
|
-
return [(lhs > rhs) as T];
|
|
252
|
-
case GREATER_THAN_OR_EQUAL:
|
|
253
|
-
return [(lhs >= rhs) as T];
|
|
254
|
-
case LESS_THAN:
|
|
255
|
-
return [(lhs < rhs) as T];
|
|
256
|
-
case LESS_THAN_OR_EQUAL:
|
|
257
|
-
return [(lhs <= rhs) as T];
|
|
258
|
-
case MODULUS:
|
|
259
|
-
return [(lhs % rhs) as T];
|
|
260
|
-
case MULTIPLICATIVE:
|
|
261
|
-
return [(lhs * rhs) as T];
|
|
262
|
-
case SUBTRACTIVE:
|
|
263
|
-
return [(lhs - rhs) as T];
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
async function handleLocationNode(location: LocationNode, context: AutomationElement, sendPowerShellCommand: (command: string) => Promise<string>): Promise<AutomationElement[] | string[]> {
|
|
270
|
-
if (location.steps.some((step) => step.test.name === null)) {
|
|
271
|
-
throw new errors.InvalidSelectorError('Expected path step expression.');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (location.type === ABSOLUTE_LOCATION_PATH) {
|
|
275
|
-
context = AutomationElement.automationRoot;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (context instanceof AutomationElementGroup && context.groups.length > 1) {
|
|
279
|
-
throw new errors.InvalidArgumentError(`handleLocationNode expects single context, but received ${context.groups.length} contexts.`);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
optimizeDoubleSlash(location.steps);
|
|
283
|
-
|
|
284
|
-
for (const [index, step] of location.steps.entries()) {
|
|
285
|
-
if (step.axis === ATTRIBUTE) {
|
|
286
|
-
if (index === location.steps.length - 1) {
|
|
287
|
-
return await convertAttributeNodeTestToStringArray(step.test, context, sendPowerShellCommand);
|
|
288
|
-
} else {
|
|
289
|
-
return [];
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
if (context instanceof AutomationElementGroup) {
|
|
294
|
-
const results: AutomationElementGroup[] = [];
|
|
295
|
-
for (const el of context.groups) {
|
|
296
|
-
results.push(await executeStep(step, el, sendPowerShellCommand));
|
|
297
|
-
}
|
|
298
|
-
context = new AutomationElementGroup(...flattenElementGroupsAndRemoveDuplicates(results));
|
|
299
|
-
} else {
|
|
300
|
-
context = await executeStep(step, context, sendPowerShellCommand);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return convertToElementArray(context);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
export async function processExprNodeAsPredicate(exprNode: ExprNode, context: AutomationElement, positions: Set<number>, sendPowerShellCommand: (command: string) => Promise<string>, relativeExprNodes?: ExprNode[]): Promise<[Condition, ExprNode[]?]> {
|
|
308
|
-
relativeExprNodes ??= [];
|
|
309
|
-
switch (exprNode.type) {
|
|
310
|
-
case NUMBER:
|
|
311
|
-
return await processExprNodeAsPredicate({
|
|
312
|
-
type: EQUALITY,
|
|
313
|
-
lhs: {
|
|
314
|
-
type: FUNCTION_CALL,
|
|
315
|
-
name: POSITION,
|
|
316
|
-
args: [],
|
|
317
|
-
},
|
|
318
|
-
rhs: {
|
|
319
|
-
type: NUMBER,
|
|
320
|
-
number: exprNode.number,
|
|
321
|
-
}
|
|
322
|
-
}, context, positions, sendPowerShellCommand, relativeExprNodes);
|
|
323
|
-
case OR:
|
|
324
|
-
return [new OrCondition(
|
|
325
|
-
(await processExprNodeAsPredicate(exprNode.lhs, context, positions, sendPowerShellCommand, relativeExprNodes))[0],
|
|
326
|
-
(await processExprNodeAsPredicate(exprNode.rhs, context, positions, sendPowerShellCommand, relativeExprNodes))[0]
|
|
327
|
-
), relativeExprNodes];
|
|
328
|
-
case AND:
|
|
329
|
-
return [new AndCondition(
|
|
330
|
-
(await processExprNodeAsPredicate(exprNode.lhs, context, positions, sendPowerShellCommand, relativeExprNodes))[0],
|
|
331
|
-
(await processExprNodeAsPredicate(exprNode.rhs, context, positions, sendPowerShellCommand, relativeExprNodes))[0]
|
|
332
|
-
), relativeExprNodes];
|
|
333
|
-
case EQUALITY:
|
|
334
|
-
case INEQUALITY: {
|
|
335
|
-
if ((exprNode.lhs.type === RELATIVE_LOCATION_PATH) !== (exprNode.rhs.type === RELATIVE_LOCATION_PATH)) {
|
|
336
|
-
if (exprNode.lhs.type === RELATIVE_LOCATION_PATH
|
|
337
|
-
&& exprNode.lhs.steps[0].axis === ATTRIBUTE
|
|
338
|
-
&& exprNode.lhs.steps[0].test.type === NODE_NAME_TEST
|
|
339
|
-
&& XPathAllowedProperties.includes(exprNode.lhs.steps[0].test.name?.toLowerCase() as XPathAllowedProperties)
|
|
340
|
-
) {
|
|
341
|
-
const propertyName = exprNode.lhs.steps[0].test.name?.toLowerCase() as Property;
|
|
342
|
-
const [value] = await processExprNode(exprNode.rhs, context, sendPowerShellCommand);
|
|
343
|
-
if (propertyName === Property.RUNTIME_ID) {
|
|
344
|
-
return [new PropertyCondition(propertyName, new PSInt32Array(String(value).split('.').map(Number))), relativeExprNodes];
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
if (propertyName === Property.ORIENTATION) {
|
|
348
|
-
return [new PropertyCondition(propertyName, new PSOrientationType(String(value))), relativeExprNodes];
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
if (Object.values(Int32Property).includes(propertyName as Int32Property)) {
|
|
352
|
-
return [new PropertyCondition(propertyName, new PSInt32(Number(value))), relativeExprNodes];
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if (Object.values(StringProperty).includes(propertyName as StringProperty)) {
|
|
356
|
-
return [new PropertyCondition(propertyName, new PSString(String(value))), relativeExprNodes];
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
if (Object.values(BooleanProperty).includes(propertyName as BooleanProperty)) {
|
|
360
|
-
return [new PropertyCondition(propertyName, new PSBoolean(Boolean(value))), relativeExprNodes];
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
if (exprNode.rhs.type === RELATIVE_LOCATION_PATH
|
|
365
|
-
&& exprNode.rhs.steps[0].axis === ATTRIBUTE
|
|
366
|
-
&& exprNode.rhs.steps[0].test.type === NODE_NAME_TEST
|
|
367
|
-
&& XPathAllowedProperties.includes(exprNode.rhs.steps[0].test.name?.toLowerCase() as XPathAllowedProperties)
|
|
368
|
-
) {
|
|
369
|
-
const propertyName = exprNode.rhs.steps[0].test.name?.toLowerCase() as Property;
|
|
370
|
-
const [value] = await processExprNode(exprNode.lhs, context, sendPowerShellCommand);
|
|
371
|
-
if (propertyName === Property.RUNTIME_ID) {
|
|
372
|
-
return [new PropertyCondition(propertyName, new PSInt32Array(String(value).split('.').map(Number))), relativeExprNodes];
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if (propertyName === Property.ORIENTATION) {
|
|
376
|
-
return [new PropertyCondition(propertyName, new PSOrientationType(String(value))), relativeExprNodes];
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
if (Object.values(Int32Property).includes(propertyName as Int32Property)) {
|
|
380
|
-
return [new PropertyCondition(propertyName, new PSInt32(Number(value))), relativeExprNodes];
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if (Object.values(StringProperty).includes(propertyName as StringProperty)) {
|
|
384
|
-
return [new PropertyCondition(propertyName, new PSString(String(value))), relativeExprNodes];
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (Object.values(BooleanProperty).includes(propertyName as BooleanProperty)) {
|
|
388
|
-
return [new PropertyCondition(propertyName, new PSBoolean(Boolean(value))), relativeExprNodes];
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
} else if ((exprNode.lhs.type === FUNCTION_CALL && exprNode.lhs.name === POSITION) !== (exprNode.rhs.type === FUNCTION_CALL && exprNode.rhs.name === POSITION)) {
|
|
392
|
-
if (exprNode.lhs.type === FUNCTION_CALL && exprNode.lhs.name === POSITION) {
|
|
393
|
-
if (exprNode.rhs.type === FUNCTION_CALL && exprNode.rhs.name === LAST) {
|
|
394
|
-
positions.add(0x7FFFFFFF);
|
|
395
|
-
} else {
|
|
396
|
-
const [value] = await processExprNode<number>(exprNode.rhs, context, sendPowerShellCommand);
|
|
397
|
-
|
|
398
|
-
if (typeof value !== 'number') {
|
|
399
|
-
return [new FalseCondition()];
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
positions.add(value);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
return [new TrueCondition];
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
if (exprNode.rhs.type === FUNCTION_CALL && exprNode.rhs.name === POSITION) {
|
|
409
|
-
if (exprNode.lhs.type === FUNCTION_CALL && exprNode.lhs.name === LAST) {
|
|
410
|
-
positions.add(0x7FFFFFFF);
|
|
411
|
-
} else {
|
|
412
|
-
const [value] = await processExprNode<number>(exprNode.lhs, context, sendPowerShellCommand);
|
|
413
|
-
|
|
414
|
-
if (typeof value !== 'number') {
|
|
415
|
-
return [new FalseCondition()];
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
positions.add(value);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
return [new TrueCondition];
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
// eslint-disable-next-line no-fallthrough
|
|
426
|
-
default: {
|
|
427
|
-
const result = await processExprNode(exprNode, context, sendPowerShellCommand);
|
|
428
|
-
|
|
429
|
-
if (result.length === 1 && typeof result[0] === 'number' && !isNaN(result[0])) {
|
|
430
|
-
return await processExprNodeAsPredicate({
|
|
431
|
-
type: EQUALITY,
|
|
432
|
-
lhs: {
|
|
433
|
-
type: FUNCTION_CALL,
|
|
434
|
-
name: POSITION,
|
|
435
|
-
args: [],
|
|
436
|
-
},
|
|
437
|
-
rhs: {
|
|
438
|
-
type: NUMBER,
|
|
439
|
-
number: result[0],
|
|
440
|
-
}
|
|
441
|
-
}, context, positions, sendPowerShellCommand, relativeExprNodes);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
relativeExprNodes.push(exprNode);
|
|
445
|
-
return [new TrueCondition(), relativeExprNodes];
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
async function executeStep(step: StepNode, context: AutomationElement, sendPowerShellCommand: (command: string) => Promise<string>): Promise<AutomationElementGroup> {
|
|
451
|
-
const predicateConditions: Condition[] = [];
|
|
452
|
-
const relativeExprNodes: ExprNode[] = [];
|
|
453
|
-
const positions: Set<number> = new Set();
|
|
454
|
-
for (const predicate of step.predicates) {
|
|
455
|
-
const [condition, exprNodes] = await processExprNodeAsPredicate(predicate, context, positions, sendPowerShellCommand);
|
|
456
|
-
predicateConditions.push(condition);
|
|
457
|
-
if (exprNodes) {
|
|
458
|
-
relativeExprNodes.push(...exprNodes);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
const condition = predicateConditions.length > 0 ? new AndCondition(convertNodeTestToCondition(step.test), ...predicateConditions) : convertNodeTestToCondition(step.test);
|
|
463
|
-
|
|
464
|
-
let find: AutomationElement;
|
|
465
|
-
switch (step.axis) {
|
|
466
|
-
case ANCESTOR:
|
|
467
|
-
find = step[OptimizeLastStep] ? context.findFirst(TreeScope.ANCESTORS, condition) : context.findAll(TreeScope.ANCESTORS, condition);
|
|
468
|
-
break;
|
|
469
|
-
case ANCESTOR_OR_SELF:
|
|
470
|
-
find = step[OptimizeLastStep] ? context.findFirst(TreeScope.ANCESTORS_OR_SELF, condition) : context.findAll(TreeScope.ANCESTORS_OR_SELF, condition);
|
|
471
|
-
break;
|
|
472
|
-
case CHILD:
|
|
473
|
-
find = step[OptimizeLastStep] ? context.findFirst(TreeScope.CHILDREN, condition) : context.findAll(TreeScope.CHILDREN, condition);
|
|
474
|
-
break;
|
|
475
|
-
case DESCENDANT:
|
|
476
|
-
find = step[OptimizeLastStep] ? context.findFirst(TreeScope.DESCENDANTS, condition) : context.findAll(TreeScope.DESCENDANTS, condition);
|
|
477
|
-
break;
|
|
478
|
-
case DESCENDANT_OR_SELF:
|
|
479
|
-
find = step[OptimizeLastStep] ? context.findFirst(TreeScope.SUBTREE, condition) : context.findAll(TreeScope.SUBTREE, condition);
|
|
480
|
-
break;
|
|
481
|
-
case FOLLOWING:
|
|
482
|
-
find = step[OptimizeLastStep] ? context.findFirst(TreeScope.FOLLOWING, condition) : context.findAll(TreeScope.FOLLOWING, condition);
|
|
483
|
-
break;
|
|
484
|
-
case FOLLOWING_SIBLING:
|
|
485
|
-
find = step[OptimizeLastStep] ? context.findFirst(TreeScope.FOLLOWING_SIBLING, condition) : context.findAll(TreeScope.FOLLOWING_SIBLING, condition);
|
|
486
|
-
break;
|
|
487
|
-
case NAMESPACE:
|
|
488
|
-
return new AutomationElementGroup(/* empty */);
|
|
489
|
-
case PARENT:
|
|
490
|
-
find = step[OptimizeLastStep] ? context.findFirst(TreeScope.PARENT, condition) : context.findAll(TreeScope.PARENT, condition);
|
|
491
|
-
break;
|
|
492
|
-
case PRECEDING:
|
|
493
|
-
find = step[OptimizeLastStep] ? context.findFirst(TreeScope.PRECEDING, condition) : context.findAll(TreeScope.PRECEDING, condition);
|
|
494
|
-
break;
|
|
495
|
-
case PRECEDING_SIBLING:
|
|
496
|
-
find = step[OptimizeLastStep] ? context.findFirst(TreeScope.PRECEDING_SIBLING, condition) : context.findAll(TreeScope.PRECEDING_SIBLING, condition);
|
|
497
|
-
break;
|
|
498
|
-
case SELF:
|
|
499
|
-
find = step[OptimizeLastStep] ? context.findFirst(TreeScope.ELEMENT, condition) : context.findAll(TreeScope.ELEMENT, condition);
|
|
500
|
-
break;
|
|
501
|
-
default:
|
|
502
|
-
throw new errors.InvalidArgumentError(); // should not be reached, attribute is handled before that
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
const result = await sendPowerShellCommand(find.buildCommand());
|
|
506
|
-
const els = result.split('\n').map((id) => id.trim()).filter(Boolean).map((id) => new FoundAutomationElement(id));
|
|
507
|
-
const validEls: FoundAutomationElement[] = [];
|
|
508
|
-
|
|
509
|
-
for (const el of els) {
|
|
510
|
-
let isValid = true;
|
|
511
|
-
for (const exprNode of relativeExprNodes) {
|
|
512
|
-
const [isTrue] = await handleFunctionCall(BOOLEAN, el, sendPowerShellCommand, exprNode);
|
|
513
|
-
if (!isTrue) {
|
|
514
|
-
isValid = false;
|
|
515
|
-
break;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
if (isValid) {
|
|
520
|
-
validEls.push(el);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
const positionsArray = Array.from(positions);
|
|
525
|
-
|
|
526
|
-
if (positionsArray.length === 0) {
|
|
527
|
-
return new AutomationElementGroup(...validEls);
|
|
528
|
-
} else {
|
|
529
|
-
return new AutomationElementGroup(...positionsArray.map((index) => index === 0x7FFFFFFF ? validEls[validEls.length - 1] : validEls[index - 1]).filter(Boolean));
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
function convertNodeTestToCondition(nodeTest: NodeTestNode): Condition {
|
|
534
|
-
switch (nodeTest.type) {
|
|
535
|
-
case NODE_NAME_TEST:
|
|
536
|
-
if (nodeTest.name === '*') {
|
|
537
|
-
return new TrueCondition();
|
|
538
|
-
}
|
|
539
|
-
// workaround for ControlType 50039 and 50040 not supported in UIAutomationClient
|
|
540
|
-
if (nodeTest.name.toLowerCase() === 'appbar' || nodeTest.name.toLowerCase() === 'semanticzoom') {
|
|
541
|
-
// PSControlType already has a logic to correct the value to localized control type
|
|
542
|
-
return new PropertyCondition(Property.LOCALIZED_CONTROL_TYPE, new PSString(new PSControlType(nodeTest.name).toString()));
|
|
543
|
-
}
|
|
544
|
-
return new PropertyCondition(Property.CONTROL_TYPE, new PSControlType(nodeTest.name));
|
|
545
|
-
case NODE_TYPE_TEST:
|
|
546
|
-
if (nodeTest.name === NODE) {
|
|
547
|
-
return new TrueCondition();
|
|
548
|
-
}
|
|
549
|
-
// eslint-disable-next-line no-fallthrough
|
|
550
|
-
case PROCESSING_INSTRUCTION_TEST:
|
|
551
|
-
return new FalseCondition();
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
async function convertAttributeNodeTestToStringArray(nodeTest: NodeTestNode, context: AutomationElement, sendPowerShellCommand: (command: string) => Promise<string>): Promise<string[]> {
|
|
556
|
-
const result = await sendPowerShellCommand(context.buildGetPropertyCommand(Property.RUNTIME_ID));
|
|
557
|
-
const els = result.split('\n').map((id) => id.trim()).filter(Boolean).map((id) => new FoundAutomationElement(id.trim()));
|
|
558
|
-
const extraProperties = ['x', 'y', 'width', 'height'];
|
|
559
|
-
switch (nodeTest.type) {
|
|
560
|
-
case NODE_TYPE_TEST:
|
|
561
|
-
if (nodeTest.name === NODE) {
|
|
562
|
-
const results: string[] = [];
|
|
563
|
-
for (const name of Object.values(Property)) {
|
|
564
|
-
for (const el of els) {
|
|
565
|
-
results.push(await sendPowerShellCommand(el.buildGetPropertyCommand(name)));
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
return results;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
return [];
|
|
573
|
-
case NODE_NAME_TEST:
|
|
574
|
-
if (extraProperties.includes(nodeTest.name.toLowerCase())) {
|
|
575
|
-
const results: string[] = [];
|
|
576
|
-
for (const el of els) {
|
|
577
|
-
const rectJson = await sendPowerShellCommand(el.buildGetElementRectCommand());
|
|
578
|
-
results.push(JSON.parse(rectJson.replaceAll(/(?:infinity)/gi, 0x7FFFFFFF.toString()))[nodeTest.name.toLowerCase()]);
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
return results;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
if (Object.values(Property).includes(nodeTest.name.toLowerCase() as Property)) {
|
|
585
|
-
const results: string[] = [];
|
|
586
|
-
for (const el of els) {
|
|
587
|
-
results.push(await sendPowerShellCommand(el.buildGetPropertyCommand(nodeTest.name)));
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
return results;
|
|
591
|
-
}
|
|
592
|
-
// eslint-disable-next-line no-fallthrough
|
|
593
|
-
case PROCESSING_INSTRUCTION_TEST:
|
|
594
|
-
default:
|
|
595
|
-
return [];
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
function convertToElementArray(element: AutomationElement): AutomationElement[] {
|
|
600
|
-
if (element instanceof AutomationElementGroup) {
|
|
601
|
-
return flattenElementGroupsAndRemoveDuplicates(element.groups);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
return [element];
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
function flattenElementGroupsAndRemoveDuplicates(elements: AutomationElement[]): AutomationElement[] {
|
|
608
|
-
const seen = new Set<string>();
|
|
609
|
-
return elements.reduce<AutomationElement[]>((acc, el) => {
|
|
610
|
-
if (el instanceof AutomationElementGroup) {
|
|
611
|
-
return acc.concat(flattenElementGroupsAndRemoveDuplicates(el.groups));
|
|
612
|
-
} else {
|
|
613
|
-
if (el instanceof FoundAutomationElement && seen.has(el.runtimeId)) {
|
|
614
|
-
return acc;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
if (el instanceof FoundAutomationElement) {
|
|
618
|
-
seen.add(el.runtimeId);
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
acc.push(el);
|
|
622
|
-
return acc;
|
|
623
|
-
}
|
|
624
|
-
}, []);
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
function optimizeDoubleSlash(steps: StepNode[]): void {
|
|
628
|
-
for (let i = 0; i < steps.length - 1; i++) {
|
|
629
|
-
// detect double slash: //element is the same as /descendant-or-self::node()/child::element
|
|
630
|
-
if (steps[i].axis === DESCENDANT_OR_SELF && steps[i].test.type === NODE_TYPE_TEST && steps[i].predicates.length === 0 && steps[i + 1].axis === CHILD) {
|
|
631
|
-
const optimizedStep: StepNode = { axis: DESCENDANT, test: steps[i + 1].test, predicates: steps[i + 1].predicates };
|
|
632
|
-
if (steps[i + 1][OptimizeLastStep]) {
|
|
633
|
-
optimizedStep[OptimizeLastStep] = true;
|
|
634
|
-
}
|
|
635
|
-
const stepsToAdd: StepNode[] = [optimizedStep];
|
|
636
|
-
if (steps[i].predicates.some((predicate) => predicate.type === FUNCTION_CALL && (predicate.name === LAST || predicate.name === POSITION))) {
|
|
637
|
-
stepsToAdd.push({ axis: PARENT, test: { type: NODE_TYPE_TEST, name: 'node' }, predicates: [] }, steps[i + 1]);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// mutates the original array by reference
|
|
641
|
-
steps.splice(i, 2, ...stepsToAdd);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
function findLastStep(obj: object): StepNode[] | undefined {
|
|
647
|
-
if (Array.isArray(obj)) {
|
|
648
|
-
return findLastStep(obj[obj.length - 1]);
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
let lastStepArray: StepNode[] | undefined;
|
|
652
|
-
for (const key in obj) {
|
|
653
|
-
if (key === 'steps' && Array.isArray(obj[key])) {
|
|
654
|
-
|
|
655
|
-
lastStepArray = obj[key];
|
|
656
|
-
}
|
|
657
|
-
if (typeof obj[key] === 'object') {
|
|
658
|
-
const result = findLastStep(obj[key]);
|
|
659
|
-
|
|
660
|
-
if (result !== undefined) {
|
|
661
|
-
lastStepArray = result;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
return lastStepArray;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
export function predicateProcessableBeforeNode(exprNode: ExprNode): boolean {
|
|
670
|
-
switch (exprNode.type) {
|
|
671
|
-
case OR:
|
|
672
|
-
case AND:
|
|
673
|
-
return predicateProcessableBeforeNode(exprNode.lhs) && predicateProcessableBeforeNode(exprNode.rhs);
|
|
674
|
-
case EQUALITY:
|
|
675
|
-
case INEQUALITY: {
|
|
676
|
-
if ((exprNode.lhs.type === RELATIVE_LOCATION_PATH) !== (exprNode.rhs.type === RELATIVE_LOCATION_PATH)) {
|
|
677
|
-
if (exprNode.lhs.type === RELATIVE_LOCATION_PATH
|
|
678
|
-
&& exprNode.lhs.steps[0].axis === ATTRIBUTE
|
|
679
|
-
&& exprNode.lhs.steps[0].test.type === NODE_NAME_TEST
|
|
680
|
-
&& XPathAllowedProperties.includes(exprNode.lhs.steps[0].test.name?.toLowerCase() as XPathAllowedProperties)
|
|
681
|
-
) {
|
|
682
|
-
return true;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
if (exprNode.rhs.type === RELATIVE_LOCATION_PATH
|
|
686
|
-
&& exprNode.rhs.steps[0].axis === ATTRIBUTE
|
|
687
|
-
&& exprNode.rhs.steps[0].test.type === NODE_NAME_TEST
|
|
688
|
-
&& XPathAllowedProperties.includes(exprNode.rhs.steps[0].test.name?.toLowerCase() as XPathAllowedProperties)
|
|
689
|
-
) {
|
|
690
|
-
return true;
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
// eslint-disable-next-line no-fallthrough
|
|
695
|
-
default: {
|
|
696
|
-
return false;
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
}
|