appium-novawindows2-driver 0.1.1
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/.github/ISSUE_TEMPLATE/bug_report.yml +97 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +33 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +28 -0
- package/.github/workflows/lint-build.yml +30 -0
- package/.github/workflows/release.yml +39 -0
- package/.releaserc +41 -0
- package/CHANGELOG.md +33 -0
- package/LICENSE +202 -0
- package/README.md +557 -0
- package/build/eslint.config.d.mts +3 -0
- package/build/eslint.config.d.mts.map +1 -0
- package/build/eslint.config.mjs +6 -0
- package/build/eslint.config.mjs.map +1 -0
- package/build/lib/commands/actions.d.ts +11 -0
- package/build/lib/commands/actions.d.ts.map +1 -0
- package/build/lib/commands/actions.js +212 -0
- package/build/lib/commands/actions.js.map +1 -0
- package/build/lib/commands/app.d.ts +12 -0
- package/build/lib/commands/app.d.ts.map +1 -0
- package/build/lib/commands/app.js +195 -0
- package/build/lib/commands/app.js.map +1 -0
- package/build/lib/commands/device.d.ts +3 -0
- package/build/lib/commands/device.d.ts.map +1 -0
- package/build/lib/commands/device.js +31 -0
- package/build/lib/commands/device.js.map +1 -0
- package/build/lib/commands/element.d.ts +15 -0
- package/build/lib/commands/element.d.ts.map +1 -0
- package/build/lib/commands/element.js +213 -0
- package/build/lib/commands/element.js.map +1 -0
- package/build/lib/commands/extension.d.ts +87 -0
- package/build/lib/commands/extension.d.ts.map +1 -0
- package/build/lib/commands/extension.js +511 -0
- package/build/lib/commands/extension.js.map +1 -0
- package/build/lib/commands/functions.d.ts +3 -0
- package/build/lib/commands/functions.d.ts.map +1 -0
- package/build/lib/commands/functions.js +194 -0
- package/build/lib/commands/functions.js.map +1 -0
- package/build/lib/commands/index.d.ts +126 -0
- package/build/lib/commands/index.d.ts.map +1 -0
- package/build/lib/commands/index.js +54 -0
- package/build/lib/commands/index.js.map +1 -0
- package/build/lib/commands/powershell.d.ts +6 -0
- package/build/lib/commands/powershell.d.ts.map +1 -0
- package/build/lib/commands/powershell.js +204 -0
- package/build/lib/commands/powershell.js.map +1 -0
- package/build/lib/commands/system.d.ts +4 -0
- package/build/lib/commands/system.d.ts.map +1 -0
- package/build/lib/commands/system.js +8 -0
- package/build/lib/commands/system.js.map +1 -0
- package/build/lib/constants.d.ts +2 -0
- package/build/lib/constants.d.ts.map +1 -0
- package/build/lib/constants.js +5 -0
- package/build/lib/constants.js.map +1 -0
- package/build/lib/constraints.d.ts +40 -0
- package/build/lib/constraints.d.ts.map +1 -0
- package/build/lib/constraints.js +42 -0
- package/build/lib/constraints.js.map +1 -0
- package/build/lib/driver.d.ts +33 -0
- package/build/lib/driver.d.ts.map +1 -0
- package/build/lib/driver.js +181 -0
- package/build/lib/driver.js.map +1 -0
- package/build/lib/enums.d.ts +89 -0
- package/build/lib/enums.d.ts.map +1 -0
- package/build/lib/enums.js +83 -0
- package/build/lib/enums.js.map +1 -0
- package/build/lib/powershell/common.d.ts +39 -0
- package/build/lib/powershell/common.d.ts.map +1 -0
- package/build/lib/powershell/common.js +121 -0
- package/build/lib/powershell/common.js.map +1 -0
- package/build/lib/powershell/conditions.d.ts +24 -0
- package/build/lib/powershell/conditions.d.ts.map +1 -0
- package/build/lib/powershell/conditions.js +131 -0
- package/build/lib/powershell/conditions.js.map +1 -0
- package/build/lib/powershell/converter.d.ts +3 -0
- package/build/lib/powershell/converter.d.ts.map +1 -0
- package/build/lib/powershell/converter.js +273 -0
- package/build/lib/powershell/converter.js.map +1 -0
- package/build/lib/powershell/core.d.ts +8 -0
- package/build/lib/powershell/core.d.ts.map +1 -0
- package/build/lib/powershell/core.js +30 -0
- package/build/lib/powershell/core.js.map +1 -0
- package/build/lib/powershell/elements.d.ts +68 -0
- package/build/lib/powershell/elements.d.ts.map +1 -0
- package/build/lib/powershell/elements.js +515 -0
- package/build/lib/powershell/elements.js.map +1 -0
- package/build/lib/powershell/index.d.ts +8 -0
- package/build/lib/powershell/index.d.ts.map +1 -0
- package/build/lib/powershell/index.js +24 -0
- package/build/lib/powershell/index.js.map +1 -0
- package/build/lib/powershell/regex.d.ts +19 -0
- package/build/lib/powershell/regex.d.ts.map +1 -0
- package/build/lib/powershell/regex.js +68 -0
- package/build/lib/powershell/regex.js.map +1 -0
- package/build/lib/powershell/types.d.ts +155 -0
- package/build/lib/powershell/types.d.ts.map +1 -0
- package/build/lib/powershell/types.js +141 -0
- package/build/lib/powershell/types.js.map +1 -0
- package/build/lib/util.d.ts +10 -0
- package/build/lib/util.d.ts.map +1 -0
- package/build/lib/util.js +51 -0
- package/build/lib/util.js.map +1 -0
- package/build/lib/winapi/types/index.d.ts +8 -0
- package/build/lib/winapi/types/index.d.ts.map +1 -0
- package/build/lib/winapi/types/index.js +24 -0
- package/build/lib/winapi/types/index.js.map +1 -0
- package/build/lib/winapi/types/input.d.ts +11 -0
- package/build/lib/winapi/types/input.d.ts.map +1 -0
- package/build/lib/winapi/types/input.js +12 -0
- package/build/lib/winapi/types/input.js.map +1 -0
- package/build/lib/winapi/types/keyeventf.d.ts +13 -0
- package/build/lib/winapi/types/keyeventf.d.ts.map +1 -0
- package/build/lib/winapi/types/keyeventf.js +14 -0
- package/build/lib/winapi/types/keyeventf.js.map +1 -0
- package/build/lib/winapi/types/mouseeventf.d.ts +34 -0
- package/build/lib/winapi/types/mouseeventf.d.ts.map +1 -0
- package/build/lib/winapi/types/mouseeventf.js +37 -0
- package/build/lib/winapi/types/mouseeventf.js.map +1 -0
- package/build/lib/winapi/types/scancode.d.ts +95 -0
- package/build/lib/winapi/types/scancode.d.ts.map +1 -0
- package/build/lib/winapi/types/scancode.js +96 -0
- package/build/lib/winapi/types/scancode.js.map +1 -0
- package/build/lib/winapi/types/systemmetric.d.ts +214 -0
- package/build/lib/winapi/types/systemmetric.d.ts.map +1 -0
- package/build/lib/winapi/types/systemmetric.js +215 -0
- package/build/lib/winapi/types/systemmetric.js.map +1 -0
- package/build/lib/winapi/types/virtualkey.d.ts +353 -0
- package/build/lib/winapi/types/virtualkey.d.ts.map +1 -0
- package/build/lib/winapi/types/virtualkey.js +354 -0
- package/build/lib/winapi/types/virtualkey.js.map +1 -0
- package/build/lib/winapi/types/xmousebutton.d.ts +7 -0
- package/build/lib/winapi/types/xmousebutton.d.ts.map +1 -0
- package/build/lib/winapi/types/xmousebutton.js +8 -0
- package/build/lib/winapi/types/xmousebutton.js.map +1 -0
- package/build/lib/winapi/user32.d.ts +56 -0
- package/build/lib/winapi/user32.d.ts.map +1 -0
- package/build/lib/winapi/user32.js +591 -0
- package/build/lib/winapi/user32.js.map +1 -0
- package/build/lib/xpath/core.d.ts +8 -0
- package/build/lib/xpath/core.d.ts.map +1 -0
- package/build/lib/xpath/core.js +593 -0
- package/build/lib/xpath/core.js.map +1 -0
- package/build/lib/xpath/functions.d.ts +4 -0
- package/build/lib/xpath/functions.d.ts.map +1 -0
- package/build/lib/xpath/functions.js +271 -0
- package/build/lib/xpath/functions.js.map +1 -0
- package/build/lib/xpath/index.d.ts +3 -0
- package/build/lib/xpath/index.d.ts.map +1 -0
- package/build/lib/xpath/index.js +19 -0
- package/build/lib/xpath/index.js.map +1 -0
- package/eslint.config.mjs +11 -0
- package/examples/C#/CalculatorTest/CalculatorTest/CalculatorSession.cs +44 -0
- package/examples/C#/CalculatorTest/CalculatorTest/CalculatorTest.csproj +24 -0
- package/examples/C#/CalculatorTest/CalculatorTest/ScenarioStandard.cs +121 -0
- package/examples/C#/CalculatorTest/CalculatorTest/ScenarioStandardInvoke.cs +121 -0
- package/examples/C#/CalculatorTest/CalculatorTest.sln +16 -0
- package/lib/commands/actions.ts +229 -0
- package/lib/commands/app.ts +227 -0
- package/lib/commands/device.ts +41 -0
- package/lib/commands/element.ts +242 -0
- package/lib/commands/extension.ts +636 -0
- package/lib/commands/functions.ts +192 -0
- package/lib/commands/index.ts +28 -0
- package/lib/commands/powershell.ts +243 -0
- package/lib/commands/system.ts +7 -0
- package/lib/constants.ts +1 -0
- package/lib/constraints.ts +43 -0
- package/lib/driver.ts +247 -0
- package/lib/enums.ts +96 -0
- package/lib/powershell/common.ts +137 -0
- package/lib/powershell/conditions.ts +169 -0
- package/lib/powershell/converter.ts +373 -0
- package/lib/powershell/core.ts +29 -0
- package/lib/powershell/elements.ts +584 -0
- package/lib/powershell/index.ts +7 -0
- package/lib/powershell/regex.ts +77 -0
- package/lib/powershell/types.ts +208 -0
- package/lib/util.ts +52 -0
- package/lib/winapi/types/index.ts +7 -0
- package/lib/winapi/types/input.ts +12 -0
- package/lib/winapi/types/keyeventf.ts +14 -0
- package/lib/winapi/types/mouseeventf.ts +37 -0
- package/lib/winapi/types/scancode.ts +96 -0
- package/lib/winapi/types/systemmetric.ts +215 -0
- package/lib/winapi/types/virtualkey.ts +354 -0
- package/lib/winapi/types/xmousebutton.ts +8 -0
- package/lib/winapi/user32.ts +842 -0
- package/lib/xpath/core.ts +699 -0
- package/lib/xpath/functions.ts +366 -0
- package/lib/xpath/index.ts +2 -0
- package/package.json +61 -0
- package/tsconfig.json +13 -0
- package/verify_driver.js +96 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// Based on the original WinAppDriver Calculator test by Microsoft, licensed under the MIT License.
|
|
2
|
+
|
|
3
|
+
using OpenQA.Selenium;
|
|
4
|
+
using OpenQA.Selenium.Appium;
|
|
5
|
+
|
|
6
|
+
namespace CalculatorTest;
|
|
7
|
+
|
|
8
|
+
public class ScenarioStandardInvoke : CalculatorSession
|
|
9
|
+
{
|
|
10
|
+
private static AppiumElement _header;
|
|
11
|
+
private static AppiumElement _calculatorResult;
|
|
12
|
+
|
|
13
|
+
[Test]
|
|
14
|
+
public void Addition()
|
|
15
|
+
{
|
|
16
|
+
// Find the buttons by their names and click them in sequence to perform 1 + 7 = 8
|
|
17
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.Name("One")));
|
|
18
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.Name("Plus")));
|
|
19
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.Name("Seven")));
|
|
20
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.Name("Equals")));
|
|
21
|
+
Assert.That(GetCalculatorResultText(), Is.EqualTo("8"));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
[Test]
|
|
25
|
+
public void Division()
|
|
26
|
+
{
|
|
27
|
+
// Find the buttons by their accessibility ids and click them in sequence to perform 88 / 11 = 8
|
|
28
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.AccessibilityId("num8Button")));
|
|
29
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.AccessibilityId("num8Button")));
|
|
30
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.AccessibilityId("divideButton")));
|
|
31
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.AccessibilityId("num1Button")));
|
|
32
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.AccessibilityId("num1Button")));
|
|
33
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.AccessibilityId("equalButton")));
|
|
34
|
+
Assert.That(GetCalculatorResultText(), Is.EqualTo("8"));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[Test]
|
|
38
|
+
public void Multiplication()
|
|
39
|
+
{
|
|
40
|
+
// Find the buttons by their names using XPath and click them in sequence to perform 9 x 9 = 81
|
|
41
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(By.XPath("//Button[@Name='Nine']")));
|
|
42
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(By.XPath("//Button[@Name='Multiply by']")));
|
|
43
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(By.XPath("//Button[@Name='Nine']")));
|
|
44
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(By.XPath("//Button[@Name='Equals']")));
|
|
45
|
+
Assert.That(GetCalculatorResultText(), Is.EqualTo("81"));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
[Test]
|
|
49
|
+
public void Subtraction()
|
|
50
|
+
{
|
|
51
|
+
// Find the buttons by their accessibility ids using XPath and click them in sequence to perform 9 - 1 = 8
|
|
52
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(By.XPath("//Button[@AutomationId=\"num9Button\"]")));
|
|
53
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(By.XPath("//Button[@AutomationId=\"minusButton\"]")));
|
|
54
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(By.XPath("//Button[@AutomationId=\"num1Button\"]")));
|
|
55
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(By.XPath("//Button[@AutomationId=\"equalButton\"]")));
|
|
56
|
+
Assert.That(GetCalculatorResultText(), Is.EqualTo("8"));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[TestCase("One", "Plus", "Seven", "8")]
|
|
60
|
+
[TestCase("Nine", "Minus", "One", "8")]
|
|
61
|
+
[TestCase("Eight", "Divide by", "Eight", "1")]
|
|
62
|
+
public void Templatized(string input1, string operation, string input2, string expectedResult)
|
|
63
|
+
{
|
|
64
|
+
// Run sequence of button presses specified above and validate the results
|
|
65
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.Name(input1)));
|
|
66
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.Name(operation)));
|
|
67
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.Name(input2)));
|
|
68
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.Name("Equals")));
|
|
69
|
+
Assert.That(GetCalculatorResultText(), Is.EqualTo(expectedResult));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
[OneTimeSetUp]
|
|
73
|
+
public static void ClassInitialize()
|
|
74
|
+
{
|
|
75
|
+
// Create session to launch a Calculator window
|
|
76
|
+
Setup();
|
|
77
|
+
|
|
78
|
+
// Identify calculator mode by locating the header
|
|
79
|
+
try
|
|
80
|
+
{
|
|
81
|
+
_header = Session.FindElement(MobileBy.AccessibilityId("Header"));
|
|
82
|
+
}
|
|
83
|
+
catch
|
|
84
|
+
{
|
|
85
|
+
_header = Session.FindElement(MobileBy.AccessibilityId("ContentPresenter"));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Ensure that calculator is in standard mode
|
|
89
|
+
if (!_header.Text.Equals("Standard", StringComparison.OrdinalIgnoreCase))
|
|
90
|
+
{
|
|
91
|
+
Session.ExecuteScript("windows: invoke", Session.FindElement(MobileBy.AccessibilityId("TogglePaneButton")));
|
|
92
|
+
Thread.Sleep(TimeSpan.FromSeconds(1));
|
|
93
|
+
var splitViewPane = Session.FindElement(MobileBy.ClassName("SplitViewPane"));
|
|
94
|
+
Session.ExecuteScript("windows: invoke", splitViewPane.FindElement(MobileBy.Name("Standard Calculator")));
|
|
95
|
+
Thread.Sleep(TimeSpan.FromSeconds(1));
|
|
96
|
+
Assert.That(_header.Text.Equals("Standard", StringComparison.OrdinalIgnoreCase), Is.True);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Locate the calculatorResult element
|
|
100
|
+
_calculatorResult = Session.FindElement(MobileBy.AccessibilityId("CalculatorResults"));
|
|
101
|
+
Assert.That(_calculatorResult, Is.Not.Null);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
[OneTimeTearDown]
|
|
105
|
+
public static void ClassCleanup()
|
|
106
|
+
{
|
|
107
|
+
TearDown();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
[SetUp]
|
|
111
|
+
public void Clear()
|
|
112
|
+
{
|
|
113
|
+
Session.ExecuteScript("windows: invoke", Session?.FindElement(MobileBy.Name("Clear")));
|
|
114
|
+
Assert.That(GetCalculatorResultText(), Is.EqualTo("0"));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private static string GetCalculatorResultText()
|
|
118
|
+
{
|
|
119
|
+
return _calculatorResult.Text.Replace("Display is", string.Empty).Trim();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
3
|
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CalculatorTest", "CalculatorTest\CalculatorTest.csproj", "{AAB0751F-0906-4FE2-8725-BF90D7AC9F9D}"
|
|
4
|
+
EndProject
|
|
5
|
+
Global
|
|
6
|
+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
7
|
+
Debug|Any CPU = Debug|Any CPU
|
|
8
|
+
Release|Any CPU = Release|Any CPU
|
|
9
|
+
EndGlobalSection
|
|
10
|
+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
11
|
+
{AAB0751F-0906-4FE2-8725-BF90D7AC9F9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
12
|
+
{AAB0751F-0906-4FE2-8725-BF90D7AC9F9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
13
|
+
{AAB0751F-0906-4FE2-8725-BF90D7AC9F9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
14
|
+
{AAB0751F-0906-4FE2-8725-BF90D7AC9F9D}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
15
|
+
EndGlobalSection
|
|
16
|
+
EndGlobal
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActionSequence,
|
|
3
|
+
KeyAction,
|
|
4
|
+
KeyActionSequence,
|
|
5
|
+
NullActionSequence,
|
|
6
|
+
PointerActionSequence,
|
|
7
|
+
PointerMoveAction,
|
|
8
|
+
Rect,
|
|
9
|
+
ScrollAction,
|
|
10
|
+
WheelActionSequence,
|
|
11
|
+
} from '@appium/types';
|
|
12
|
+
|
|
13
|
+
import { W3C_ELEMENT_KEY, errors } from '@appium/base-driver';
|
|
14
|
+
import { NovaWindowsDriver } from '../driver';
|
|
15
|
+
import { keyDown, keyUp, mouseMoveRelative, mouseMoveAbsolute, mouseDown, mouseUp, mouseScroll } from '../winapi/user32';
|
|
16
|
+
import { sleep } from '../util';
|
|
17
|
+
import { AutomationElement, FoundAutomationElement } from '../powershell';
|
|
18
|
+
import { Key } from '../enums';
|
|
19
|
+
|
|
20
|
+
export async function performActions(this: NovaWindowsDriver, actionSequences: ActionSequence[]): Promise<void> {
|
|
21
|
+
for (const actionSequence of actionSequences) {
|
|
22
|
+
switch (actionSequence.type) {
|
|
23
|
+
case 'key':
|
|
24
|
+
await this.handleKeyActionSequence(actionSequence);
|
|
25
|
+
break;
|
|
26
|
+
case 'wheel':
|
|
27
|
+
await this.handleWheelActionSequence(actionSequence);
|
|
28
|
+
break;
|
|
29
|
+
case 'pointer':
|
|
30
|
+
await this.handlePointerActionSequence(actionSequence);
|
|
31
|
+
break;
|
|
32
|
+
case 'none':
|
|
33
|
+
await this.handleNullActionSequence(actionSequence);
|
|
34
|
+
break;
|
|
35
|
+
default:
|
|
36
|
+
throw new errors.InvalidArgumentError();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export async function handleKeyActionSequence(this: NovaWindowsDriver, actionSequence: KeyActionSequence): Promise<void> {
|
|
42
|
+
const actions = actionSequence.actions;
|
|
43
|
+
for (const action of actions) {
|
|
44
|
+
await this.handleKeyAction(action);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function handlePointerActionSequence(this: NovaWindowsDriver, actionSequence: PointerActionSequence): Promise<void> {
|
|
49
|
+
switch (actionSequence.parameters?.pointerType) {
|
|
50
|
+
case 'touch':
|
|
51
|
+
case 'pen':
|
|
52
|
+
throw new errors.NotImplementedError(`Pointer type ${actionSequence.parameters?.pointerType} not implemented yet.`);
|
|
53
|
+
case 'mouse':
|
|
54
|
+
default:
|
|
55
|
+
await this.handleMousePointerActionSequence(actionSequence);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function handleMousePointerActionSequence(this: NovaWindowsDriver, actionSequence: PointerActionSequence): Promise<void> {
|
|
60
|
+
const actions = actionSequence.actions;
|
|
61
|
+
for (const action of actions) {
|
|
62
|
+
switch (action.type) {
|
|
63
|
+
case 'pointerMove':
|
|
64
|
+
await this.handleMouseMoveAction(action);
|
|
65
|
+
break;
|
|
66
|
+
case 'pointerDown':
|
|
67
|
+
mouseDown(action.button);
|
|
68
|
+
break;
|
|
69
|
+
case 'pointerUp':
|
|
70
|
+
mouseUp(action.button);
|
|
71
|
+
break;
|
|
72
|
+
case 'pause':
|
|
73
|
+
if (action.duration) {
|
|
74
|
+
await sleep(action.duration);
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
default:
|
|
78
|
+
throw new errors.InvalidArgumentError();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function handleWheelActionSequence(this: NovaWindowsDriver, actionSequence: WheelActionSequence): Promise<void> {
|
|
84
|
+
const actions = actionSequence.actions;
|
|
85
|
+
for (const action of actions) {
|
|
86
|
+
switch (action.type) {
|
|
87
|
+
case 'scroll':
|
|
88
|
+
await this.handleMouseMoveAction(action);
|
|
89
|
+
mouseScroll(action.deltaX, action.deltaY);
|
|
90
|
+
break;
|
|
91
|
+
case 'pause':
|
|
92
|
+
if (action.duration) {
|
|
93
|
+
await sleep(action.duration);
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
default:
|
|
97
|
+
throw new errors.InvalidArgumentError();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function handleNullActionSequence(this: NovaWindowsDriver, actionSequence: NullActionSequence): Promise<void> {
|
|
103
|
+
const actions = actionSequence.actions;
|
|
104
|
+
for (const action of actions) {
|
|
105
|
+
if (action.duration) {
|
|
106
|
+
await sleep(action.duration);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function handleMouseMoveAction(this: NovaWindowsDriver, action: PointerMoveAction | ScrollAction): Promise<void> {
|
|
112
|
+
const easingFunction = this.caps.smoothPointerMove;
|
|
113
|
+
switch (action.origin) {
|
|
114
|
+
case 'pointer':
|
|
115
|
+
await mouseMoveRelative(action.x, action.y, action.duration, easingFunction);
|
|
116
|
+
break;
|
|
117
|
+
case 'viewport': {
|
|
118
|
+
const rootRectJson = await this.sendPowerShellCommand(AutomationElement.automationRoot.buildGetElementRectCommand());
|
|
119
|
+
const rootRect = JSON.parse(rootRectJson.replaceAll(/(?:infinity)/gi, 0x7FFFFFFF.toString())) as Rect;
|
|
120
|
+
await mouseMoveAbsolute(action.x + rootRect.x, action.y + rootRect.y, action.duration, easingFunction);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
default:
|
|
124
|
+
if (action.origin?.[W3C_ELEMENT_KEY]) {
|
|
125
|
+
const element = new FoundAutomationElement(action.origin[W3C_ELEMENT_KEY]);
|
|
126
|
+
const rectJson = await this.sendPowerShellCommand(element.buildGetElementRectCommand());
|
|
127
|
+
let rect = JSON.parse(rectJson.replaceAll(/(?:infinity)/gi, 0x7FFFFFFF.toString())) as Rect;
|
|
128
|
+
|
|
129
|
+
if (Object.values(rect).some((x) => x === 0x7FFFFFFF)) {
|
|
130
|
+
await this.sendPowerShellCommand(element.buildScrollIntoViewCommand());
|
|
131
|
+
rect = JSON.parse(rectJson.replaceAll(/(?:infinity)/gi, 0x7FFFFFFF.toString())) as Rect;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
await mouseMoveAbsolute(action.x === 0 ? rect.x + rect.width / 2 : action.x, action.y === 0 ? rect.y + rect.height / 2 : action.y, action.duration, easingFunction);
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
throw new errors.InvalidArgumentError();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function handleKeyAction(this: NovaWindowsDriver, action: KeyAction): Promise<void> {
|
|
143
|
+
if (action.type === 'pause') {
|
|
144
|
+
if (action.duration) {
|
|
145
|
+
await sleep(action.duration);
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
switch (action.value) {
|
|
151
|
+
case Key.SHIFT:
|
|
152
|
+
case Key.R_SHIFT:
|
|
153
|
+
if (action.type === 'keyDown') {
|
|
154
|
+
keyDown(action.value);
|
|
155
|
+
this.keyboardState.shift = true;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
keyUp(Key.SHIFT);
|
|
160
|
+
keyUp(Key.R_SHIFT);
|
|
161
|
+
this.keyboardState.shift = false;
|
|
162
|
+
return;
|
|
163
|
+
case Key.CONTROL:
|
|
164
|
+
case Key.R_CONTROL:
|
|
165
|
+
if (action.type === 'keyDown') {
|
|
166
|
+
keyDown(action.value);
|
|
167
|
+
this.keyboardState.ctrl = true;
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
keyUp(Key.CONTROL);
|
|
172
|
+
keyUp(Key.R_CONTROL);
|
|
173
|
+
this.keyboardState.ctrl = false;
|
|
174
|
+
return;
|
|
175
|
+
case Key.META:
|
|
176
|
+
case Key.R_META:
|
|
177
|
+
if (action.type === 'keyDown') {
|
|
178
|
+
keyDown(action.value);
|
|
179
|
+
this.keyboardState.meta = true;
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
keyUp(Key.META);
|
|
184
|
+
keyUp(Key.R_META);
|
|
185
|
+
this.keyboardState.meta = false;
|
|
186
|
+
return;
|
|
187
|
+
case Key.ALT:
|
|
188
|
+
case Key.R_ALT:
|
|
189
|
+
if (action.type === 'keyDown') {
|
|
190
|
+
keyDown(action.value);
|
|
191
|
+
this.keyboardState.alt = true;
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
keyUp(Key.ALT);
|
|
196
|
+
keyUp(Key.R_ALT);
|
|
197
|
+
this.keyboardState.alt = false;
|
|
198
|
+
return;
|
|
199
|
+
case Key.NULL:
|
|
200
|
+
if (action.type === 'keyDown') {
|
|
201
|
+
if (this.keyboardState.shift) {
|
|
202
|
+
await this.handleKeyAction({ type: 'keyUp', value: Key.SHIFT });
|
|
203
|
+
}
|
|
204
|
+
if (this.keyboardState.ctrl) {
|
|
205
|
+
await this.handleKeyAction({ type: 'keyUp', value: Key.CONTROL });
|
|
206
|
+
}
|
|
207
|
+
if (this.keyboardState.meta) {
|
|
208
|
+
await this.handleKeyAction({ type: 'keyUp', value: Key.META });
|
|
209
|
+
}
|
|
210
|
+
if (this.keyboardState.alt) {
|
|
211
|
+
await this.handleKeyAction({ type: 'keyUp', value: Key.ALT });
|
|
212
|
+
}
|
|
213
|
+
for (const key in Array.of(this.keyboardState.pressed)) {
|
|
214
|
+
keyUp(key);
|
|
215
|
+
this.keyboardState.pressed.delete(key);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return;
|
|
219
|
+
default:
|
|
220
|
+
if (action.type === 'keyDown') {
|
|
221
|
+
keyDown(action.value);
|
|
222
|
+
this.keyboardState.pressed.add(action.value);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
keyUp(action.value);
|
|
226
|
+
this.keyboardState.pressed.delete(action.value);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { normalize } from 'node:path';
|
|
2
|
+
import { Element, Rect } from '@appium/types';
|
|
3
|
+
import { NovaWindowsDriver } from '../driver';
|
|
4
|
+
import {
|
|
5
|
+
AutomationElement,
|
|
6
|
+
FoundAutomationElement,
|
|
7
|
+
PSInt32,
|
|
8
|
+
PSString,
|
|
9
|
+
Property,
|
|
10
|
+
PropertyCondition,
|
|
11
|
+
TreeScope,
|
|
12
|
+
TrueCondition,
|
|
13
|
+
pwsh$,
|
|
14
|
+
pwsh,
|
|
15
|
+
} from '../powershell';
|
|
16
|
+
import { sleep } from '../util';
|
|
17
|
+
import { errors, W3C_ELEMENT_KEY } from '@appium/base-driver';
|
|
18
|
+
import {
|
|
19
|
+
getWindowAllHandlesForProcessIds,
|
|
20
|
+
trySetForegroundWindow,
|
|
21
|
+
} from '../winapi/user32';
|
|
22
|
+
|
|
23
|
+
const GET_PAGE_SOURCE_COMMAND = pwsh$ /* ps1 */ `
|
|
24
|
+
$el = ${0}
|
|
25
|
+
|
|
26
|
+
if ($el -eq $null) {
|
|
27
|
+
$dummy = [xml]'<DummyRoot></DummyRoot>'
|
|
28
|
+
return $dummy.OuterXml
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
Get-PageSource $el |
|
|
32
|
+
ForEach-Object { $_.OuterXml }
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const GET_SCREENSHOT_COMMAND = pwsh /* ps1 */ `
|
|
36
|
+
if ($rootElement -eq $null) {
|
|
37
|
+
$bitmap = New-Object Drawing.Bitmap 1,1
|
|
38
|
+
$stream = New-Object IO.MemoryStream
|
|
39
|
+
$bitmap.Save($stream, [Drawing.Imaging.ImageFormat]::Png)
|
|
40
|
+
$bitmap.Dispose()
|
|
41
|
+
return [Convert]::ToBase64String($stream.ToArray())
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
$rect = $rootElement.Current.BoundingRectangle
|
|
45
|
+
$bitmap = New-Object Drawing.Bitmap([int32]$rect.Width, [int32]$rect.Height)
|
|
46
|
+
|
|
47
|
+
$graphics = [Drawing.Graphics]::FromImage($bitmap)
|
|
48
|
+
$graphics.CopyFromScreen([int32]$rect.Left, [int32]$rect.Top, 0, 0, $bitmap.Size)
|
|
49
|
+
$graphics.Dispose()
|
|
50
|
+
|
|
51
|
+
$stream = New-Object IO.MemoryStream
|
|
52
|
+
$bitmap.Save($stream, [Drawing.Imaging.ImageFormat]::Png)
|
|
53
|
+
$bitmap.Dispose()
|
|
54
|
+
|
|
55
|
+
[Convert]::ToBase64String($stream.ToArray())
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
export async function getPageSource(this: NovaWindowsDriver): Promise<string> {
|
|
59
|
+
return await this.sendPowerShellCommand(GET_PAGE_SOURCE_COMMAND.format(AutomationElement.automationRoot));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function getScreenshot(this: NovaWindowsDriver): Promise<string> {
|
|
63
|
+
const automationRootId = await this.sendPowerShellCommand(AutomationElement.automationRoot.buildCommand());
|
|
64
|
+
|
|
65
|
+
if (this.caps.app && this.caps.app.toLowerCase() !== 'root') {
|
|
66
|
+
try {
|
|
67
|
+
await this.focusElement({
|
|
68
|
+
[W3C_ELEMENT_KEY]: automationRootId.trim(),
|
|
69
|
+
} satisfies Element);
|
|
70
|
+
} catch {
|
|
71
|
+
// noop
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return await this.sendPowerShellCommand(GET_SCREENSHOT_COMMAND);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function getWindowRect(this: NovaWindowsDriver): Promise<Rect> {
|
|
79
|
+
const result = await this.sendPowerShellCommand(AutomationElement.automationRoot.buildGetElementRectCommand());
|
|
80
|
+
return JSON.parse(result.replaceAll(/(?:infinity)/gi, 0x7FFFFFFF.toString()));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function getWindowHandle(this: NovaWindowsDriver): Promise<string> {
|
|
84
|
+
const nativeWindowHandle = await this.sendPowerShellCommand(AutomationElement.automationRoot.buildGetPropertyCommand(Property.NATIVE_WINDOW_HANDLE));
|
|
85
|
+
return `0x${Number(nativeWindowHandle).toString(16).padStart(8, '0')}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function getWindowHandles(this: NovaWindowsDriver): Promise<string[]> {
|
|
89
|
+
const result = await this.sendPowerShellCommand(AutomationElement.rootElement.findAll(TreeScope.CHILDREN, new TrueCondition()).buildCommand());
|
|
90
|
+
const elIds = result.split('\n').map((x) => x.trim()).filter(Boolean);
|
|
91
|
+
const nativeWindowHandles: string[] = [];
|
|
92
|
+
|
|
93
|
+
for (const elId of elIds) {
|
|
94
|
+
const nativeWindowHandle = await this.sendPowerShellCommand(new FoundAutomationElement(elId).buildGetPropertyCommand(Property.NATIVE_WINDOW_HANDLE));
|
|
95
|
+
nativeWindowHandles.push(`0x${Number(nativeWindowHandle).toString(16).padStart(8, '0')}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return nativeWindowHandles;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function setWindow(this: NovaWindowsDriver, nameOrHandle: string): Promise<void> {
|
|
102
|
+
const handle = Number(nameOrHandle);
|
|
103
|
+
for (let i = 1; i <= 20; i++) { // TODO: make a setting for the number of retries or timeout
|
|
104
|
+
if (!isNaN(handle)) {
|
|
105
|
+
const condition = new PropertyCondition(Property.NATIVE_WINDOW_HANDLE, new PSInt32(handle));
|
|
106
|
+
const elementId = await this.sendPowerShellCommand(AutomationElement.rootElement.findFirst(TreeScope.CHILDREN_OR_SELF, condition).buildCommand());
|
|
107
|
+
|
|
108
|
+
if (elementId.trim() !== '') {
|
|
109
|
+
await this.sendPowerShellCommand(/* ps1 */ `$rootElement = ${new FoundAutomationElement(elementId).buildCommand()}`);
|
|
110
|
+
trySetForegroundWindow(handle);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const name = nameOrHandle;
|
|
116
|
+
const condition = new PropertyCondition(Property.NAME, new PSString(name));
|
|
117
|
+
const elementId = await this.sendPowerShellCommand(AutomationElement.rootElement.findFirst(TreeScope.CHILDREN, condition).buildCommand());
|
|
118
|
+
|
|
119
|
+
if (elementId.trim() !== '') {
|
|
120
|
+
this.log.info(`Found window with name '${name}'. Setting it as the root element.`);
|
|
121
|
+
await this.sendPowerShellCommand(/* ps1 */ `$rootElement = ${new FoundAutomationElement(elementId).buildCommand()}`);
|
|
122
|
+
trySetForegroundWindow(handle);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this.log.info(`Failed to locate window with name '${name}'. Sleeping for 500 milliseconds and retrying... (${i}/20)`); // TODO: make a setting for the number of retries or timeout
|
|
127
|
+
await sleep(500); // TODO: make a setting for the sleep timeout
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
throw new errors.NoSuchWindowError(`No window was found with name or handle '${nameOrHandle}'.`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export async function changeRootElement(this: NovaWindowsDriver, path: string): Promise<void>
|
|
134
|
+
export async function changeRootElement(this: NovaWindowsDriver, nativeWindowHandle: number): Promise<void>
|
|
135
|
+
export async function changeRootElement(this: NovaWindowsDriver, pathOrNativeWindowHandle: string | number): Promise<void> {
|
|
136
|
+
if (typeof pathOrNativeWindowHandle === 'number') {
|
|
137
|
+
const nativeWindowHandle = pathOrNativeWindowHandle;
|
|
138
|
+
const condition = new PropertyCondition(Property.NATIVE_WINDOW_HANDLE, new PSInt32(nativeWindowHandle));
|
|
139
|
+
const elementId = await this.sendPowerShellCommand(AutomationElement.rootElement.findFirst(TreeScope.CHILDREN_OR_SELF, condition).buildCommand());
|
|
140
|
+
|
|
141
|
+
if (elementId.trim() !== '') {
|
|
142
|
+
await this.sendPowerShellCommand(/* ps1 */ `$rootElement = ${new FoundAutomationElement(elementId).buildCommand()}`);
|
|
143
|
+
trySetForegroundWindow(nativeWindowHandle);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
throw new errors.UnknownError('Failed to locate top level window with that window handle.');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
const path = pathOrNativeWindowHandle;
|
|
152
|
+
if (path.includes('!') && path.includes('_') && !(path.includes('/') || path.includes('\\'))) {
|
|
153
|
+
this.log.debug('Detected app path to be in the UWP format.');
|
|
154
|
+
await this.sendPowerShellCommand(/* ps1 */ `Start-Process 'explorer.exe' 'shell:AppsFolder\\${path}'${this.caps.appArguments ? ` -ArgumentList '${this.caps.appArguments}'` : ''}`);
|
|
155
|
+
await sleep(500); // TODO: make a setting for the initial wait time
|
|
156
|
+
for (let i = 1; i <= 20; i++) {
|
|
157
|
+
const result = await this.sendPowerShellCommand(/* ps1 */ `(Get-Process -Name 'ApplicationFrameHost').Id`);
|
|
158
|
+
const processIds = result.split('\n').map((pid) => pid.trim()).filter(Boolean).map(Number);
|
|
159
|
+
|
|
160
|
+
this.log.debug('Process IDs of ApplicationFrameHost processes: ' + processIds.join(', '));
|
|
161
|
+
try {
|
|
162
|
+
await this.attachToApplicationWindow(processIds);
|
|
163
|
+
return;
|
|
164
|
+
} catch {
|
|
165
|
+
// noop
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.log.info(`Failed to locate window of the app. Sleeping for 500 milliseconds and retrying... (${i}/20)`); // TODO: make a setting for the number of retries or timeout
|
|
169
|
+
await sleep(500); // TODO: make a setting for the sleep timeout
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
this.log.debug('Detected app path to be in the classic format.');
|
|
173
|
+
const normalizedPath = normalize(path);
|
|
174
|
+
await this.sendPowerShellCommand(/* ps1 */ `Start-Process '${normalizedPath}'${this.caps.appArguments ? ` -ArgumentList '${this.caps.appArguments}'` : ''}`);
|
|
175
|
+
await sleep(500); // TODO: make a setting for the initial wait time
|
|
176
|
+
for (let i = 1; i <= 20; i++) {
|
|
177
|
+
try {
|
|
178
|
+
const breadcrumbs = normalizedPath.toLowerCase().split('\\').flatMap((x) => x.split('/'));
|
|
179
|
+
const executable = breadcrumbs[breadcrumbs.length - 1];
|
|
180
|
+
const processName = executable.endsWith('.exe') ? executable.slice(0, executable.length - 4) : executable;
|
|
181
|
+
const result = await this.sendPowerShellCommand(/* ps1 */ `(Get-Process -Name '${processName}' | Sort-Object StartTime -Descending).Id`);
|
|
182
|
+
const processIds = result.split('\n').map((pid) => pid.trim()).filter(Boolean).map(Number);
|
|
183
|
+
this.log.debug(`Process IDs of '${processName}' processes: ` + processIds.join(', '));
|
|
184
|
+
|
|
185
|
+
await this.attachToApplicationWindow(processIds);
|
|
186
|
+
return;
|
|
187
|
+
} catch (err) {
|
|
188
|
+
if (err instanceof Error) {
|
|
189
|
+
this.log.debug(`Received error:\n${err.message}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.log.info(`Failed to locate window of the app. Sleeping for 500 milliseconds and retrying... (${i}/20)`); // TODO: make a setting for the number of retries or timeout
|
|
194
|
+
await sleep(500); // TODO: make a setting for the sleep timeout
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
throw new errors.UnknownError('Failed to locate window of the app.');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export async function attachToApplicationWindow(this: NovaWindowsDriver, processIds: number[]): Promise<void> {
|
|
202
|
+
const nativeWindowHandles = getWindowAllHandlesForProcessIds(processIds);
|
|
203
|
+
this.log.debug(`Detected the following native window handles for the given process IDs: ${nativeWindowHandles.map((handle) => `0x${handle.toString(16).padStart(8, '0')}`).join(', ')}`);
|
|
204
|
+
|
|
205
|
+
if (nativeWindowHandles.length !== 0) {
|
|
206
|
+
let elementId = '';
|
|
207
|
+
for (let i = 1; i <= 20; i++) {
|
|
208
|
+
elementId = await this.sendPowerShellCommand(AutomationElement.rootElement.findFirst(TreeScope.CHILDREN, new PropertyCondition(Property.NATIVE_WINDOW_HANDLE, new PSInt32(nativeWindowHandles[0]))).buildCommand());
|
|
209
|
+
if (elementId) {
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
this.log.info(`The window with handle 0x${nativeWindowHandles[0].toString(16).padStart(8, '0')} is not yet available in the UI Automation tree. Sleeping for 500 milliseconds and retrying... (${i}/20)`); // TODO: make a setting for the number of retries or timeout
|
|
213
|
+
await sleep(500); // TODO: make a setting for the sleep timeout
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
await this.sendPowerShellCommand(/* ps1 */ `$rootElement = ${new FoundAutomationElement(elementId).buildCommand()}`);
|
|
217
|
+
if ((await this.sendPowerShellCommand(/* ps1 */ `$null -ne $rootElement`)).toLowerCase() === 'true') {
|
|
218
|
+
const nativeWindowHandle = Number(await this.sendPowerShellCommand(AutomationElement.automationRoot.buildGetPropertyCommand(Property.NATIVE_WINDOW_HANDLE)));
|
|
219
|
+
if (!trySetForegroundWindow(nativeWindowHandle)) {
|
|
220
|
+
await this.focusElement({
|
|
221
|
+
[W3C_ELEMENT_KEY]: elementId,
|
|
222
|
+
} satisfies Element);
|
|
223
|
+
};
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NovaWindowsDriver } from '../driver';
|
|
2
|
+
import { PSString, pwsh$ } from '../powershell';
|
|
3
|
+
|
|
4
|
+
const GET_SYSTEM_TIME_COMMAND = pwsh$ /* ps1 */ `(Get-Date).ToString(${0})`;
|
|
5
|
+
const ISO_8061_FORMAT = 'yyyy-MM-ddTHH:mm:sszzz';
|
|
6
|
+
|
|
7
|
+
export async function getDeviceTime(this: NovaWindowsDriver, format?: string): Promise<string> {
|
|
8
|
+
format = format ? new PSString(format).toString() : `'${ISO_8061_FORMAT}'`;
|
|
9
|
+
return await this.sendPowerShellCommand(GET_SYSTEM_TIME_COMMAND.format(format));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// command: 'hideKeyboard'
|
|
13
|
+
// payloadParams: { optional: ['strategy', 'key', 'keyCode', 'keyName'] }
|
|
14
|
+
|
|
15
|
+
// command: 'isKeyboardShown'
|
|
16
|
+
|
|
17
|
+
// command: 'pushFile'
|
|
18
|
+
// payloadParams: { required: ['path', 'data'] }
|
|
19
|
+
|
|
20
|
+
// command: 'pullFile'
|
|
21
|
+
// payloadParams: { required: ['path'] }
|
|
22
|
+
|
|
23
|
+
// command: 'pullFolder'
|
|
24
|
+
// payloadParams: { required: ['path'] }
|
|
25
|
+
|
|
26
|
+
// # APP MANAGEMENT
|
|
27
|
+
|
|
28
|
+
// command: 'activateApp'
|
|
29
|
+
// payloadParams: { required: [['appId'], ['bundleId']], optional: ['options'] }
|
|
30
|
+
|
|
31
|
+
// command: 'removeApp'
|
|
32
|
+
// payloadParams: { required: [['appId'], ['bundleId']], optional: ['options'] }
|
|
33
|
+
|
|
34
|
+
//command: 'terminateApp'
|
|
35
|
+
// payloadParams: { required: [['appId'], ['bundleId']], optional: ['options'] }
|
|
36
|
+
|
|
37
|
+
// command: 'isAppInstalled'
|
|
38
|
+
// payloadParams: { required: [['appId'], ['bundleId']] }
|
|
39
|
+
|
|
40
|
+
// command: 'installApp'
|
|
41
|
+
// payloadParams: { required: ['appPath'], optional: ['options'] }
|