appium-novawindows2-driver 0.1.2 → 0.1.3
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/examples/api_test.js +69 -0
- package/examples/concurrency_test.js +82 -0
- package/examples/debug_test.js +36 -0
- package/examples/stress_test.js +94 -0
- package/lib/commands/actions.ts +9 -9
- package/lib/commands/app.ts +11 -11
- package/lib/commands/device.ts +2 -2
- package/lib/commands/element.ts +13 -13
- package/lib/commands/extension.ts +35 -31
- package/lib/commands/index.ts +1 -1
- package/lib/commands/powershell.ts +68 -58
- package/lib/commands/system.ts +2 -2
- package/lib/driver.ts +21 -2
- package/package.json +7 -6
- 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/temp_log.txt +0 -215
- /package/{verify_driver.js → examples/verify_driver.js} +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { W3C_ELEMENT_KEY, errors } from '@appium/base-driver';
|
|
2
2
|
import { Element, Rect } from '@appium/types';
|
|
3
|
-
import {
|
|
3
|
+
import { NovaWindows2Driver } from '../driver';
|
|
4
4
|
import { $, sleep } from '../util';
|
|
5
5
|
import { POWER_SHELL_FEATURE } from '../constants';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
keyDown,
|
|
7
8
|
keyUp,
|
|
8
9
|
mouseDown,
|
|
9
10
|
mouseMoveAbsolute,
|
|
@@ -94,7 +95,7 @@ type KeyAction = {
|
|
|
94
95
|
down?: boolean,
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
export async function execute(this:
|
|
98
|
+
export async function execute(this: NovaWindows2Driver, script: string, args: any[]) {
|
|
98
99
|
if (script.startsWith(PLATFORM_COMMAND_PREFIX)) {
|
|
99
100
|
script = script.replace(PLATFORM_COMMAND_PREFIX, '').trim();
|
|
100
101
|
this.log.info(`Executing command '${PLATFORM_COMMAND_PREFIX} ${script}'...`);
|
|
@@ -108,11 +109,14 @@ export async function execute(this: NovaWindowsDriver, script: string, args: any
|
|
|
108
109
|
|
|
109
110
|
if (script === 'powerShell') {
|
|
110
111
|
this.assertFeatureEnabled(POWER_SHELL_FEATURE);
|
|
112
|
+
// this.log.info(`Executing command: \n${args[0]}`);
|
|
111
113
|
return await this.executePowerShellScript(args[0]);
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
if (script === 'return window.name') {
|
|
115
|
-
|
|
117
|
+
const command = AutomationElement.automationRoot.buildGetPropertyCommand(Property.NAME);
|
|
118
|
+
// this.log.info(`Executing command: \n${command}`);
|
|
119
|
+
return await this.sendPowerShellCommand(command);
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
throw new errors.NotImplementedError();
|
|
@@ -127,7 +131,7 @@ type CacheRequest = {
|
|
|
127
131
|
const TREE_SCOPE_REGEX = new PropertyRegexMatcher('System.Windows.Automation.TreeScope', ...Object.values(TreeScope)).toRegex('i');
|
|
128
132
|
const AUTOMATION_ELEMENT_MODE_REGEX = new PropertyRegexMatcher('System.Windows.Automation.AutomationElementMode', ...Object.values(AutomationElementMode)).toRegex('i');
|
|
129
133
|
|
|
130
|
-
export async function pushCacheRequest(this:
|
|
134
|
+
export async function pushCacheRequest(this: NovaWindows2Driver, cacheRequest: CacheRequest): Promise<void> {
|
|
131
135
|
if (Object.keys(cacheRequest).every((key) => cacheRequest[key] === undefined)) {
|
|
132
136
|
throw new errors.InvalidArgumentError('At least one property of the cache request must be set.');
|
|
133
137
|
}
|
|
@@ -163,28 +167,28 @@ export async function pushCacheRequest(this: NovaWindowsDriver, cacheRequest: Ca
|
|
|
163
167
|
}
|
|
164
168
|
}
|
|
165
169
|
|
|
166
|
-
export async function patternInvoke(this:
|
|
170
|
+
export async function patternInvoke(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
167
171
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildInvokeCommand());
|
|
168
172
|
}
|
|
169
173
|
|
|
170
|
-
export async function patternExpand(this:
|
|
174
|
+
export async function patternExpand(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
171
175
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildExpandCommand());
|
|
172
176
|
}
|
|
173
177
|
|
|
174
|
-
export async function patternCollapse(this:
|
|
178
|
+
export async function patternCollapse(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
175
179
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildCollapseCommand());
|
|
176
180
|
}
|
|
177
181
|
|
|
178
|
-
export async function patternScrollIntoView(this:
|
|
182
|
+
export async function patternScrollIntoView(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
179
183
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildScrollIntoViewCommand());
|
|
180
184
|
}
|
|
181
185
|
|
|
182
|
-
export async function patternIsMultiple(this:
|
|
186
|
+
export async function patternIsMultiple(this: NovaWindows2Driver, element: Element): Promise<boolean> {
|
|
183
187
|
const result = await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildIsMultipleSelectCommand());
|
|
184
188
|
return result.toLowerCase() === 'true' ? true : false;
|
|
185
189
|
}
|
|
186
190
|
|
|
187
|
-
export async function patternGetSelectedItem(this:
|
|
191
|
+
export async function patternGetSelectedItem(this: NovaWindows2Driver, element: Element): Promise<Element> {
|
|
188
192
|
const result = await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildGetSelectionCommand());
|
|
189
193
|
const elId = result.split('\n').filter(Boolean)[0];
|
|
190
194
|
|
|
@@ -195,28 +199,28 @@ export async function patternGetSelectedItem(this: NovaWindowsDriver, element: E
|
|
|
195
199
|
return { [W3C_ELEMENT_KEY]: elId };
|
|
196
200
|
}
|
|
197
201
|
|
|
198
|
-
export async function patternGetAllSelectedItems(this:
|
|
202
|
+
export async function patternGetAllSelectedItems(this: NovaWindows2Driver, element: Element): Promise<Element[]> {
|
|
199
203
|
const result = await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildGetSelectionCommand());
|
|
200
204
|
return result.split('\n').filter(Boolean).map((elId) => ({ [W3C_ELEMENT_KEY]: elId }));
|
|
201
205
|
}
|
|
202
206
|
|
|
203
|
-
export async function patternAddToSelection(this:
|
|
207
|
+
export async function patternAddToSelection(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
204
208
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildAddToSelectionCommand());
|
|
205
209
|
}
|
|
206
210
|
|
|
207
|
-
export async function patternRemoveFromSelection(this:
|
|
211
|
+
export async function patternRemoveFromSelection(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
208
212
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildRemoveFromSelectionCommand());
|
|
209
213
|
}
|
|
210
214
|
|
|
211
|
-
export async function patternSelect(this:
|
|
215
|
+
export async function patternSelect(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
212
216
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildSelectCommand());
|
|
213
217
|
}
|
|
214
218
|
|
|
215
|
-
export async function patternToggle(this:
|
|
219
|
+
export async function patternToggle(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
216
220
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildToggleCommand());
|
|
217
221
|
}
|
|
218
222
|
|
|
219
|
-
export async function patternSetValue(this:
|
|
223
|
+
export async function patternSetValue(this: NovaWindows2Driver, element: Element, value: string): Promise<void> {
|
|
220
224
|
try {
|
|
221
225
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildSetValueCommand(value));
|
|
222
226
|
} catch {
|
|
@@ -224,31 +228,31 @@ export async function patternSetValue(this: NovaWindowsDriver, element: Element,
|
|
|
224
228
|
}
|
|
225
229
|
}
|
|
226
230
|
|
|
227
|
-
export async function patternGetValue(this:
|
|
231
|
+
export async function patternGetValue(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
228
232
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildGetValueCommand());
|
|
229
233
|
}
|
|
230
234
|
|
|
231
|
-
export async function patternMaximize(this:
|
|
235
|
+
export async function patternMaximize(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
232
236
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildMaximizeCommand());
|
|
233
237
|
}
|
|
234
238
|
|
|
235
|
-
export async function patternMinimize(this:
|
|
239
|
+
export async function patternMinimize(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
236
240
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildMinimizeCommand());
|
|
237
241
|
}
|
|
238
242
|
|
|
239
|
-
export async function patternRestore(this:
|
|
243
|
+
export async function patternRestore(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
240
244
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildRestoreCommand());
|
|
241
245
|
}
|
|
242
246
|
|
|
243
|
-
export async function patternClose(this:
|
|
247
|
+
export async function patternClose(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
244
248
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildCloseCommand());
|
|
245
249
|
}
|
|
246
250
|
|
|
247
|
-
export async function focusElement(this:
|
|
251
|
+
export async function focusElement(this: NovaWindows2Driver, element: Element): Promise<void> {
|
|
248
252
|
await this.sendPowerShellCommand(new FoundAutomationElement(element[W3C_ELEMENT_KEY]).buildSetFocusCommand());
|
|
249
253
|
}
|
|
250
254
|
|
|
251
|
-
export async function getClipboardBase64(this:
|
|
255
|
+
export async function getClipboardBase64(this: NovaWindows2Driver, contentType?: ContentType | { contentType?: ContentType }): Promise<string> {
|
|
252
256
|
if (!contentType || (contentType && typeof contentType === 'object')) {
|
|
253
257
|
contentType = contentType?.contentType ?? ContentType.PLAINTEXT;
|
|
254
258
|
}
|
|
@@ -263,7 +267,7 @@ export async function getClipboardBase64(this: NovaWindowsDriver, contentType?:
|
|
|
263
267
|
}
|
|
264
268
|
}
|
|
265
269
|
|
|
266
|
-
export async function setClipboardFromBase64(this:
|
|
270
|
+
export async function setClipboardFromBase64(this: NovaWindows2Driver, args: { contentType?: ContentType, b64Content: string }): Promise<string> {
|
|
267
271
|
if (!args || typeof args !== 'object' || !args.b64Content) {
|
|
268
272
|
throw new errors.InvalidArgumentError(`'b64Content' must be provided.`);
|
|
269
273
|
}
|
|
@@ -280,7 +284,7 @@ export async function setClipboardFromBase64(this: NovaWindowsDriver, args: { co
|
|
|
280
284
|
}
|
|
281
285
|
}
|
|
282
286
|
|
|
283
|
-
export async function executePowerShellScript(this:
|
|
287
|
+
export async function executePowerShellScript(this: NovaWindows2Driver, script: string | { script: string, command: undefined } | { script: undefined, command: string }): Promise<string> {
|
|
284
288
|
if (script && typeof script === 'object') {
|
|
285
289
|
if (script.script) {
|
|
286
290
|
script = script.script;
|
|
@@ -299,7 +303,7 @@ export async function executePowerShellScript(this: NovaWindowsDriver, script: s
|
|
|
299
303
|
}
|
|
300
304
|
}
|
|
301
305
|
|
|
302
|
-
export async function executeKeys(this:
|
|
306
|
+
export async function executeKeys(this: NovaWindows2Driver, keyActions: { actions: KeyAction | KeyAction[], forceUnicode: boolean }) {
|
|
303
307
|
if (!Array.isArray(keyActions.actions)) {
|
|
304
308
|
keyActions.actions = [keyActions.actions];
|
|
305
309
|
}
|
|
@@ -358,7 +362,7 @@ export async function executeKeys(this: NovaWindowsDriver, keyActions: { actions
|
|
|
358
362
|
}
|
|
359
363
|
}
|
|
360
364
|
|
|
361
|
-
export async function executeClick(this:
|
|
365
|
+
export async function executeClick(this: NovaWindows2Driver, clickArgs: {
|
|
362
366
|
elementId?: string,
|
|
363
367
|
x?: number,
|
|
364
368
|
y?: number,
|
|
@@ -404,7 +408,7 @@ export async function executeClick(this: NovaWindowsDriver, clickArgs: {
|
|
|
404
408
|
pos = [x!, y!];
|
|
405
409
|
}
|
|
406
410
|
|
|
407
|
-
const clickTypeToButtonMapping: { [key in ClickType]: number} = {
|
|
411
|
+
const clickTypeToButtonMapping: { [key in ClickType]: number } = {
|
|
408
412
|
[ClickType.LEFT]: 0,
|
|
409
413
|
[ClickType.MIDDLE]: 1,
|
|
410
414
|
[ClickType.RIGHT]: 2,
|
|
@@ -458,7 +462,7 @@ export async function executeClick(this: NovaWindowsDriver, clickArgs: {
|
|
|
458
462
|
}
|
|
459
463
|
}
|
|
460
464
|
|
|
461
|
-
export async function executeHover(this:
|
|
465
|
+
export async function executeHover(this: NovaWindows2Driver, hoverArgs: {
|
|
462
466
|
startElementId?: string,
|
|
463
467
|
startX?: number,
|
|
464
468
|
startY?: number,
|
|
@@ -561,7 +565,7 @@ export async function executeHover(this: NovaWindowsDriver, hoverArgs: {
|
|
|
561
565
|
}
|
|
562
566
|
}
|
|
563
567
|
|
|
564
|
-
export async function executeScroll(this:
|
|
568
|
+
export async function executeScroll(this: NovaWindows2Driver, scrollArgs: {
|
|
565
569
|
elementId?: string,
|
|
566
570
|
x?: number,
|
|
567
571
|
y?: number,
|
package/lib/commands/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import {
|
|
2
|
+
import { NovaWindows2Driver } from '../driver';
|
|
3
3
|
import { errors } from '@appium/base-driver';
|
|
4
4
|
import { FIND_CHILDREN_RECURSIVELY, PAGE_SOURCE } from './functions';
|
|
5
5
|
|
|
@@ -11,11 +11,46 @@ const INIT_ROOT_ELEMENT = /* ps1 */ `$rootElement = [AutomationElement]::RootEle
|
|
|
11
11
|
const NULL_ROOT_ELEMENT = /* ps1 */ `$rootElement = $null`;
|
|
12
12
|
const INIT_ELEMENT_TABLE = /* ps1 */ `$elementTable = New-Object System.Collections.Generic.Dictionary[[string]\`,[AutomationElement]]`;
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
async function executeRawCommand(driver: NovaWindows2Driver, command: string): Promise<string> {
|
|
15
|
+
const magicNumber = 0xF2EE;
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17
|
+
const powerShell = driver.powerShell!;
|
|
18
|
+
|
|
19
|
+
driver.powerShellStdOut = '';
|
|
20
|
+
driver.powerShellStdErr = '';
|
|
21
|
+
|
|
22
|
+
powerShell.stdin.write(`${command}\n`);
|
|
23
|
+
powerShell.stdin.write(/* ps1 */ `Write-Output $([char]0x${magicNumber.toString(16)})\n`);
|
|
24
|
+
|
|
25
|
+
return await new Promise<string>((resolve, reject) => {
|
|
26
|
+
const onClose = (code: number) => {
|
|
27
|
+
reject(new errors.UnknownError(`PowerShell process exited unexpectedly with code ${code}`));
|
|
28
|
+
driver.powerShell = undefined; // Clear the reference as the process is dead
|
|
29
|
+
};
|
|
30
|
+
powerShell.on('close', onClose);
|
|
31
|
+
|
|
32
|
+
const onData: Parameters<typeof powerShell.stdout.on>[1] = ((chunk: any) => {
|
|
33
|
+
const magicChar = String.fromCharCode(magicNumber);
|
|
34
|
+
if (chunk.toString().includes(magicChar)) {
|
|
35
|
+
powerShell.stdout.off('data', onData);
|
|
36
|
+
powerShell.off('close', onClose);
|
|
37
|
+
if (driver.powerShellStdErr) {
|
|
38
|
+
reject(new errors.UnknownError(driver.powerShellStdErr));
|
|
39
|
+
} else {
|
|
40
|
+
resolve(driver.powerShellStdOut.replace(`${magicChar}`, '').trim());
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}).bind(driver);
|
|
44
|
+
|
|
45
|
+
powerShell.stdout.on('data', onData);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function startPowerShellSession(this: NovaWindows2Driver): Promise<void> {
|
|
15
50
|
this.log.debug('Starting new PowerShell session...');
|
|
16
51
|
const powerShell = spawn('powershell.exe', ['-NoExit', '-Command', '-']);
|
|
17
52
|
powerShell.stdout.setEncoding('utf8');
|
|
18
|
-
powerShell.
|
|
53
|
+
powerShell.stderr.setEncoding('utf8');
|
|
19
54
|
|
|
20
55
|
powerShell.stdout.on('data', (chunk: any) => {
|
|
21
56
|
this.powerShellStdOut += chunk.toString();
|
|
@@ -38,27 +73,29 @@ export async function startPowerShellSession(this: NovaWindowsDriver): Promise<v
|
|
|
38
73
|
for (const envVar of envVars) {
|
|
39
74
|
this.caps.appWorkingDir = this.caps.appWorkingDir.replaceAll(`%${envVar}%`, process.env[envVar.toUpperCase()] ?? '');
|
|
40
75
|
}
|
|
41
|
-
|
|
76
|
+
// Use raw execution to bypass queue
|
|
77
|
+
await executeRawCommand(this, `Set-Location -Path '${this.caps.appWorkingDir}'`);
|
|
42
78
|
}
|
|
43
79
|
|
|
44
|
-
|
|
45
|
-
await this
|
|
46
|
-
await this
|
|
47
|
-
await this
|
|
48
|
-
await this
|
|
80
|
+
// Use raw execution to bypass queue
|
|
81
|
+
await executeRawCommand(this, SET_UTF8_ENCODING);
|
|
82
|
+
await executeRawCommand(this, ADD_NECESSARY_ASSEMBLIES);
|
|
83
|
+
await executeRawCommand(this, USE_UI_AUTOMATION_CLIENT);
|
|
84
|
+
await executeRawCommand(this, INIT_CACHE_REQUEST);
|
|
85
|
+
await executeRawCommand(this, INIT_ELEMENT_TABLE);
|
|
49
86
|
|
|
50
87
|
// initialize functions
|
|
51
|
-
await this
|
|
52
|
-
await this
|
|
88
|
+
await executeRawCommand(this, PAGE_SOURCE);
|
|
89
|
+
await executeRawCommand(this, FIND_CHILDREN_RECURSIVELY);
|
|
53
90
|
|
|
54
91
|
if ((!this.caps.app && !this.caps.appTopLevelWindow) || (!this.caps.app || this.caps.app.toLowerCase() === 'none')) {
|
|
55
92
|
this.log.info(`No app or top-level window specified in capabilities. Setting root element to null.`);
|
|
56
|
-
await this
|
|
93
|
+
await executeRawCommand(this, NULL_ROOT_ELEMENT);
|
|
57
94
|
}
|
|
58
95
|
|
|
59
96
|
if (this.caps.app && this.caps.app.toLowerCase() === 'root') {
|
|
60
97
|
this.log.info(`'root' specified as app in capabilities. Setting root element to desktop root.`);
|
|
61
|
-
await this
|
|
98
|
+
await executeRawCommand(this, INIT_ROOT_ELEMENT);
|
|
62
99
|
}
|
|
63
100
|
|
|
64
101
|
if (this.caps.app && this.caps.app.toLowerCase() !== 'none' && this.caps.app.toLowerCase() !== 'root') {
|
|
@@ -91,7 +128,7 @@ export async function startPowerShellSession(this: NovaWindowsDriver): Promise<v
|
|
|
91
128
|
}
|
|
92
129
|
}
|
|
93
130
|
|
|
94
|
-
export async function sendIsolatedPowerShellCommand(this:
|
|
131
|
+
export async function sendIsolatedPowerShellCommand(this: NovaWindows2Driver, command: string): Promise<string> {
|
|
95
132
|
const magicNumber = 0xF2EE;
|
|
96
133
|
|
|
97
134
|
const powerShell = spawn('powershell.exe', ['-NoExit', '-Command', '-']);
|
|
@@ -166,55 +203,28 @@ export async function sendIsolatedPowerShellCommand(this: NovaWindowsDriver, com
|
|
|
166
203
|
}
|
|
167
204
|
}
|
|
168
205
|
|
|
169
|
-
export async function sendPowerShellCommand(this:
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
await this.startPowerShellSession();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const result = await new Promise<string>((resolve, reject) => {
|
|
179
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
180
|
-
const powerShell = this.powerShell!;
|
|
181
|
-
|
|
182
|
-
this.powerShellStdOut = '';
|
|
183
|
-
this.powerShellStdErr = '';
|
|
184
|
-
|
|
185
|
-
powerShell.stdin.write(`${command}\n`);
|
|
186
|
-
powerShell.stdin.write(/* ps1 */ `Write-Output $([char]0x${magicNumber.toString(16)})\n`);
|
|
187
|
-
|
|
188
|
-
const onClose = (code: number) => {
|
|
189
|
-
reject(new errors.UnknownError(`PowerShell process exited unexpectedly with code ${code}`));
|
|
190
|
-
this.powerShell = undefined; // Clear the reference as the process is dead
|
|
191
|
-
};
|
|
192
|
-
powerShell.on('close', onClose);
|
|
193
|
-
|
|
194
|
-
const onData: Parameters<typeof powerShell.stdout.on>[1] = ((chunk: any) => {
|
|
195
|
-
const magicChar = String.fromCharCode(magicNumber);
|
|
196
|
-
if (chunk.toString().includes(magicChar)) {
|
|
197
|
-
powerShell.stdout.off('data', onData);
|
|
198
|
-
powerShell.off('close', onClose);
|
|
199
|
-
if (this.powerShellStdErr) {
|
|
200
|
-
reject(new errors.UnknownError(this.powerShellStdErr));
|
|
201
|
-
} else {
|
|
202
|
-
// this.log.debug(`Received magic char, resolving command.`);
|
|
203
|
-
resolve(this.powerShellStdOut.replace(`${magicChar}`, '').trim());
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}).bind(this);
|
|
206
|
+
export async function sendPowerShellCommand(this: NovaWindows2Driver, command: string): Promise<string> {
|
|
207
|
+
const nextCommand = async () => {
|
|
208
|
+
if (!this.powerShell) {
|
|
209
|
+
this.log.warn('PowerShell session not running. It was either closed or has crashed. Attempting to start a new session...');
|
|
210
|
+
await this.startPowerShellSession();
|
|
211
|
+
}
|
|
207
212
|
|
|
208
|
-
|
|
209
|
-
|
|
213
|
+
// Use the extracted raw command function
|
|
214
|
+
return await executeRawCommand(this, command);
|
|
215
|
+
};
|
|
210
216
|
|
|
211
|
-
//
|
|
212
|
-
//
|
|
217
|
+
// Chain the command to the queue
|
|
218
|
+
// Use .catch to ignore previous failures, then .then to queue this command.
|
|
219
|
+
// This prevents re-running this command if it fails (no infinite loop).
|
|
220
|
+
this.commandQueue = this.commandQueue
|
|
221
|
+
.catch(() => { /* ignore previous error */ })
|
|
222
|
+
.then(nextCommand);
|
|
213
223
|
|
|
214
|
-
return
|
|
224
|
+
return this.commandQueue;
|
|
215
225
|
}
|
|
216
226
|
|
|
217
|
-
export async function terminatePowerShellSession(this:
|
|
227
|
+
export async function terminatePowerShellSession(this: NovaWindows2Driver): Promise<void> {
|
|
218
228
|
if (!this.powerShell) {
|
|
219
229
|
return;
|
|
220
230
|
}
|
package/lib/commands/system.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Orientation } from '@appium/types';
|
|
2
|
-
import {
|
|
2
|
+
import { NovaWindows2Driver } from '../driver';
|
|
3
3
|
import { getDisplayOrientation } from '../winapi/user32';
|
|
4
4
|
|
|
5
|
-
export function getOrientation(this:
|
|
5
|
+
export function getOrientation(this: NovaWindows2Driver): Orientation {
|
|
6
6
|
return getDisplayOrientation();
|
|
7
7
|
}
|
package/lib/driver.ts
CHANGED
|
@@ -54,11 +54,12 @@ const LOCATION_STRATEGIES = Object.freeze([
|
|
|
54
54
|
'-windows uiautomation',
|
|
55
55
|
] as const);
|
|
56
56
|
|
|
57
|
-
export class
|
|
57
|
+
export class NovaWindows2Driver extends BaseDriver<NovaWindowsDriverConstraints, StringRecord> {
|
|
58
58
|
isPowerShellSessionStarted: boolean = false;
|
|
59
59
|
powerShell?: ChildProcessWithoutNullStreams;
|
|
60
60
|
powerShellStdOut: string = '';
|
|
61
61
|
powerShellStdErr: string = '';
|
|
62
|
+
commandQueue: Promise<any> = Promise.resolve();
|
|
62
63
|
keyboardState: KeyboardState = {
|
|
63
64
|
pressed: new Set(),
|
|
64
65
|
alt: false,
|
|
@@ -67,6 +68,8 @@ export class NovaWindowsDriver extends BaseDriver<NovaWindowsDriverConstraints,
|
|
|
67
68
|
shift: false,
|
|
68
69
|
};
|
|
69
70
|
|
|
71
|
+
activeCommands: number = 0;
|
|
72
|
+
|
|
70
73
|
constructor(opts: InitialOpts = {} as InitialOpts, shouldValidateCaps = true) {
|
|
71
74
|
super(opts, shouldValidateCaps);
|
|
72
75
|
|
|
@@ -74,8 +77,24 @@ export class NovaWindowsDriver extends BaseDriver<NovaWindowsDriverConstraints,
|
|
|
74
77
|
this.desiredCapConstraints = UI_AUTOMATION_DRIVER_CONSTRAINTS;
|
|
75
78
|
|
|
76
79
|
for (const key in commands) { // TODO: create a decorator that will do that for the class
|
|
77
|
-
|
|
80
|
+
NovaWindows2Driver.prototype[key] = commands[key].bind(this);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
override async executeCommand(cmd: string, ...args: any[]): Promise<any> {
|
|
85
|
+
this.activeCommands++;
|
|
86
|
+
try {
|
|
87
|
+
return await super.executeCommand(cmd, ...args);
|
|
88
|
+
} finally {
|
|
89
|
+
this.activeCommands--;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
override async startNewCommandTimeout() {
|
|
94
|
+
if (this.activeCommands > 0) {
|
|
95
|
+
return;
|
|
78
96
|
}
|
|
97
|
+
await super.startNewCommandTimeout();
|
|
79
98
|
}
|
|
80
99
|
|
|
81
100
|
override async findElement(strategy: string, selector: string): Promise<Element> {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium-novawindows2-driver",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Appium driver for Windows",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"appium",
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"license": "Apache-2.0",
|
|
22
22
|
"repository": {
|
|
23
23
|
"type": "git",
|
|
24
|
-
"url": "https://github.com/nguyenvanhuy0612/appium-
|
|
24
|
+
"url": "https://github.com/nguyenvanhuy0612/appium-novawindows2-driver.git"
|
|
25
25
|
},
|
|
26
26
|
"bugs": {
|
|
27
|
-
"url": "https://github.com/nguyenvanhuy0612/appium-
|
|
27
|
+
"url": "https://github.com/nguyenvanhuy0612/appium-novawindows2-driver/issues"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"appium": "^3.1.0"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"platformNames": [
|
|
42
42
|
"Windows"
|
|
43
43
|
],
|
|
44
|
-
"mainClass": "
|
|
44
|
+
"mainClass": "NovaWindows2Driver"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@appium/eslint-config-appium-ts": "^2.0.3",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"semantic-release": "^25.0.1",
|
|
57
57
|
"typescript": "^5.9.3",
|
|
58
58
|
"typescript-eslint": "^8.46.1",
|
|
59
|
-
"webdriverio": "^9.21.0"
|
|
59
|
+
"webdriverio": "^9.21.0",
|
|
60
|
+
"axios": "^1.4.0"
|
|
60
61
|
}
|
|
61
|
-
}
|
|
62
|
+
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// Based on the original WinAppDriver Calculator test by Microsoft, licensed under the MIT License.
|
|
2
|
-
|
|
3
|
-
using OpenQA.Selenium.Appium;
|
|
4
|
-
using OpenQA.Selenium.Appium.Windows;
|
|
5
|
-
|
|
6
|
-
namespace CalculatorTest;
|
|
7
|
-
|
|
8
|
-
public class CalculatorSession
|
|
9
|
-
{
|
|
10
|
-
private const string AppiumServerUrl = "http://127.0.0.1:4723/";
|
|
11
|
-
private const string CalculatorAppId = "Microsoft.WindowsCalculator_8wekyb3d8bbwe!App";
|
|
12
|
-
|
|
13
|
-
protected static WindowsDriver Session { get; private set; }
|
|
14
|
-
|
|
15
|
-
protected static void Setup()
|
|
16
|
-
{
|
|
17
|
-
if (Session != null) return;
|
|
18
|
-
|
|
19
|
-
// Launch Calculator application if it is not yet launched
|
|
20
|
-
// Create a new session to bring up an instance of the Calculator application
|
|
21
|
-
// Note: Multiple calculator windows (instances) share the same process Id
|
|
22
|
-
var appiumOptions = new AppiumOptions
|
|
23
|
-
{
|
|
24
|
-
App = CalculatorAppId,
|
|
25
|
-
AutomationName = "NovaWindows",
|
|
26
|
-
PlatformName = "Windows",
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
Session = new WindowsDriver(new Uri(AppiumServerUrl), appiumOptions);
|
|
30
|
-
Assert.That(Session, Is.Not.Null);
|
|
31
|
-
|
|
32
|
-
// Set implicit timeout to 1.5 seconds to make element search to retry every 500 ms for at most three times
|
|
33
|
-
Session.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1.5);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
protected static void TearDown()
|
|
37
|
-
{
|
|
38
|
-
if (Session == null) return;
|
|
39
|
-
|
|
40
|
-
// Close the application and delete the session
|
|
41
|
-
Session.Quit();
|
|
42
|
-
Session = null;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
<Project Sdk="Microsoft.NET.Sdk">
|
|
2
|
-
|
|
3
|
-
<PropertyGroup>
|
|
4
|
-
<TargetFramework>net8.0</TargetFramework>
|
|
5
|
-
<ImplicitUsings>enable</ImplicitUsings>
|
|
6
|
-
<Nullable>disable</Nullable>
|
|
7
|
-
|
|
8
|
-
<IsPackable>false</IsPackable>
|
|
9
|
-
<IsTestProject>true</IsTestProject>
|
|
10
|
-
</PropertyGroup>
|
|
11
|
-
|
|
12
|
-
<ItemGroup>
|
|
13
|
-
<PackageReference Include="Appium.WebDriver" Version="7.2.0" />
|
|
14
|
-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
|
|
15
|
-
<PackageReference Include="NUnit" Version="3.14.0"/>
|
|
16
|
-
<PackageReference Include="NUnit.Analyzers" Version="3.9.0"/>
|
|
17
|
-
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
|
|
18
|
-
</ItemGroup>
|
|
19
|
-
|
|
20
|
-
<ItemGroup>
|
|
21
|
-
<Using Include="NUnit.Framework"/>
|
|
22
|
-
</ItemGroup>
|
|
23
|
-
|
|
24
|
-
</Project>
|