appium-novawindows2-driver 0.1.3 → 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.
Files changed (50) hide show
  1. package/build/tsconfig.tsbuildinfo +1 -0
  2. package/package.json +4 -1
  3. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -97
  4. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -33
  5. package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
  6. package/.github/workflows/lint-build.yml +0 -30
  7. package/.github/workflows/release.yml +0 -39
  8. package/.releaserc +0 -41
  9. package/CHANGELOG.md +0 -33
  10. package/eslint.config.mjs +0 -11
  11. package/examples/api_test.js +0 -69
  12. package/examples/concurrency_test.js +0 -82
  13. package/examples/debug_test.js +0 -36
  14. package/examples/stress_test.js +0 -94
  15. package/examples/verify_driver.js +0 -142
  16. package/lib/commands/actions.ts +0 -229
  17. package/lib/commands/app.ts +0 -227
  18. package/lib/commands/device.ts +0 -41
  19. package/lib/commands/element.ts +0 -242
  20. package/lib/commands/extension.ts +0 -640
  21. package/lib/commands/functions.ts +0 -192
  22. package/lib/commands/index.ts +0 -28
  23. package/lib/commands/powershell.ts +0 -256
  24. package/lib/commands/system.ts +0 -7
  25. package/lib/constants.ts +0 -1
  26. package/lib/constraints.ts +0 -43
  27. package/lib/driver.ts +0 -266
  28. package/lib/enums.ts +0 -96
  29. package/lib/powershell/common.ts +0 -137
  30. package/lib/powershell/conditions.ts +0 -169
  31. package/lib/powershell/converter.ts +0 -373
  32. package/lib/powershell/core.ts +0 -29
  33. package/lib/powershell/elements.ts +0 -584
  34. package/lib/powershell/index.ts +0 -7
  35. package/lib/powershell/regex.ts +0 -77
  36. package/lib/powershell/types.ts +0 -208
  37. package/lib/util.ts +0 -52
  38. package/lib/winapi/types/index.ts +0 -7
  39. package/lib/winapi/types/input.ts +0 -12
  40. package/lib/winapi/types/keyeventf.ts +0 -14
  41. package/lib/winapi/types/mouseeventf.ts +0 -37
  42. package/lib/winapi/types/scancode.ts +0 -96
  43. package/lib/winapi/types/systemmetric.ts +0 -215
  44. package/lib/winapi/types/virtualkey.ts +0 -354
  45. package/lib/winapi/types/xmousebutton.ts +0 -8
  46. package/lib/winapi/user32.ts +0 -842
  47. package/lib/xpath/core.ts +0 -699
  48. package/lib/xpath/functions.ts +0 -366
  49. package/lib/xpath/index.ts +0 -2
  50. package/tsconfig.json +0 -13
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
- }