appium-novawindows2-driver 0.1.3 → 0.1.5

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 (103) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +557 -557
  3. package/build/lib/commands/actions.d.ts.map +1 -1
  4. package/build/lib/commands/actions.js +3 -4
  5. package/build/lib/commands/actions.js.map +1 -1
  6. package/build/lib/commands/app.d.ts.map +1 -1
  7. package/build/lib/commands/app.js +53 -45
  8. package/build/lib/commands/app.js.map +1 -1
  9. package/build/lib/commands/device.d.ts.map +1 -1
  10. package/build/lib/commands/device.js +2 -0
  11. package/build/lib/commands/device.js.map +1 -1
  12. package/build/lib/commands/element.d.ts.map +1 -1
  13. package/build/lib/commands/element.js +42 -12
  14. package/build/lib/commands/element.js.map +1 -1
  15. package/build/lib/commands/extension.d.ts.map +1 -1
  16. package/build/lib/commands/extension.js +29 -17
  17. package/build/lib/commands/extension.js.map +1 -1
  18. package/build/lib/commands/file.d.ts +5 -0
  19. package/build/lib/commands/file.d.ts.map +1 -0
  20. package/build/lib/commands/file.js +49 -0
  21. package/build/lib/commands/file.js.map +1 -0
  22. package/build/lib/commands/functions.d.ts.map +1 -1
  23. package/build/lib/commands/functions.js +189 -187
  24. package/build/lib/commands/functions.js.map +1 -1
  25. package/build/lib/commands/index.d.ts +3 -0
  26. package/build/lib/commands/index.d.ts.map +1 -1
  27. package/build/lib/commands/index.js +2 -0
  28. package/build/lib/commands/index.js.map +1 -1
  29. package/build/lib/commands/powershell.d.ts.map +1 -1
  30. package/build/lib/commands/powershell.js +114 -68
  31. package/build/lib/commands/powershell.js.map +1 -1
  32. package/build/lib/constraints.d.ts +18 -0
  33. package/build/lib/constraints.d.ts.map +1 -1
  34. package/build/lib/constraints.js +18 -0
  35. package/build/lib/constraints.js.map +1 -1
  36. package/build/lib/driver.d.ts.map +1 -1
  37. package/build/lib/driver.js +35 -4
  38. package/build/lib/driver.js.map +1 -1
  39. package/build/lib/powershell/converter.d.ts.map +1 -1
  40. package/build/lib/powershell/converter.js +12 -0
  41. package/build/lib/powershell/converter.js.map +1 -1
  42. package/build/lib/powershell/elements.d.ts.map +1 -1
  43. package/build/lib/powershell/elements.js +270 -265
  44. package/build/lib/powershell/elements.js.map +1 -1
  45. package/build/lib/winapi/user32.js.map +1 -1
  46. package/build/lib/xpath/core.d.ts +2 -2
  47. package/build/lib/xpath/core.d.ts.map +1 -1
  48. package/build/lib/xpath/core.js +32 -26
  49. package/build/lib/xpath/core.js.map +1 -1
  50. package/build/lib/xpath/functions.d.ts +1 -1
  51. package/build/lib/xpath/functions.d.ts.map +1 -1
  52. package/build/lib/xpath/functions.js +2 -2
  53. package/build/lib/xpath/functions.js.map +1 -1
  54. package/build/tsconfig.tsbuildinfo +1 -0
  55. package/package.json +67 -62
  56. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -97
  57. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -33
  58. package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
  59. package/.github/workflows/lint-build.yml +0 -30
  60. package/.github/workflows/release.yml +0 -39
  61. package/.releaserc +0 -41
  62. package/CHANGELOG.md +0 -33
  63. package/eslint.config.mjs +0 -11
  64. package/examples/api_test.js +0 -69
  65. package/examples/concurrency_test.js +0 -82
  66. package/examples/debug_test.js +0 -36
  67. package/examples/stress_test.js +0 -94
  68. package/examples/verify_driver.js +0 -142
  69. package/lib/commands/actions.ts +0 -229
  70. package/lib/commands/app.ts +0 -227
  71. package/lib/commands/device.ts +0 -41
  72. package/lib/commands/element.ts +0 -242
  73. package/lib/commands/extension.ts +0 -640
  74. package/lib/commands/functions.ts +0 -192
  75. package/lib/commands/index.ts +0 -28
  76. package/lib/commands/powershell.ts +0 -256
  77. package/lib/commands/system.ts +0 -7
  78. package/lib/constants.ts +0 -1
  79. package/lib/constraints.ts +0 -43
  80. package/lib/driver.ts +0 -266
  81. package/lib/enums.ts +0 -96
  82. package/lib/powershell/common.ts +0 -137
  83. package/lib/powershell/conditions.ts +0 -169
  84. package/lib/powershell/converter.ts +0 -373
  85. package/lib/powershell/core.ts +0 -29
  86. package/lib/powershell/elements.ts +0 -584
  87. package/lib/powershell/index.ts +0 -7
  88. package/lib/powershell/regex.ts +0 -77
  89. package/lib/powershell/types.ts +0 -208
  90. package/lib/util.ts +0 -52
  91. package/lib/winapi/types/index.ts +0 -7
  92. package/lib/winapi/types/input.ts +0 -12
  93. package/lib/winapi/types/keyeventf.ts +0 -14
  94. package/lib/winapi/types/mouseeventf.ts +0 -37
  95. package/lib/winapi/types/scancode.ts +0 -96
  96. package/lib/winapi/types/systemmetric.ts +0 -215
  97. package/lib/winapi/types/virtualkey.ts +0 -354
  98. package/lib/winapi/types/xmousebutton.ts +0 -8
  99. package/lib/winapi/user32.ts +0 -842
  100. package/lib/xpath/core.ts +0 -699
  101. package/lib/xpath/functions.ts +0 -366
  102. package/lib/xpath/index.ts +0 -2
  103. package/tsconfig.json +0 -13
@@ -1,192 +0,0 @@
1
- import { pwsh } from '../powershell';
2
-
3
- export const FIND_CHILDREN_RECURSIVELY = pwsh /* ps1 */ `
4
- function Find-ChildrenRecursively {
5
- param (
6
- [Parameter(Mandatory=$true)]
7
- [AutomationElement]$element,
8
- [Parameter(Mandatory=$true)]
9
- [Condition]$condition,
10
- [Parameter(Mandatory=$false)]
11
- [bool]$includeSelf = $false
12
- )
13
-
14
- $scope = if ($includeSelf) {
15
- [TreeScope]::Element -bor [TreeScope]::Children
16
- } else {
17
- [TreeScope]::Children
18
- }
19
-
20
- $validChild = $element.FindFirst($scope, $condition)
21
-
22
- if ($validChild -ne $null) {
23
- return $validChild
24
- }
25
-
26
- $children = $element.FindAll([TreeScope]::Children, [Condition]::TrueCondition)
27
- foreach ($child in $children) {
28
- $result = Find-AllChildrenRecursively -element $child -condition $condition -returnFirstResult $true
29
- if ($result -ne $null) {
30
- return $result[0]
31
- }
32
- }
33
-
34
- return $null
35
- }
36
-
37
- function Find-AllChildrenRecursively {
38
- param (
39
- [Parameter(Mandatory=$true)]
40
- [AutomationElement]$element,
41
- [Parameter(Mandatory=$true)]
42
- [Condition]$condition,
43
- [bool]$returnFirstResult = $false,
44
- [Parameter(Mandatory=$false)]
45
- [bool]$includeSelf = $false
46
- )
47
-
48
- $children = $element.FindAll([TreeScope]::Children, [Condition]::TrueCondition)
49
- $validChildren = @($children | Where-Object { $_.FindFirst([TreeScope]::Element, $condition) -ne $null })
50
-
51
- if ($includeSelf) {
52
- $self = $element.FindFirst([TreeScope]::Element, $condition)
53
- }
54
-
55
- if ($null -ne $self) {
56
- $validChildren += $self
57
- }
58
-
59
- foreach ($child in $children) {
60
- $Allresults = Find-AllChildrenRecursively -element $child -condition $condition
61
- if ($returnFirstResult -and $Allresults.Count -gt 0) {
62
- return $Allresults
63
- }
64
-
65
- foreach ($result in $Allresults) {
66
- $validChildren += ($result | Where-Object { $_.FindFirst([TreeScope]::Element, $condition) -ne $null })
67
- }
68
- }
69
-
70
- return $validChildren
71
- }
72
- `;
73
-
74
- export const PAGE_SOURCE = pwsh /* ps1 */ `
75
- function Get-PageSource {
76
- param (
77
- [Parameter(Mandatory = $true)]
78
- [AutomationElement]$element,
79
- [Xml.XmlDocument]$xmlDoc,
80
- [Xml.XmlElement]$xmlElement
81
- )
82
-
83
- try {
84
- $localizedControlType = $element.GetCurrentPropertyValue([AutomationElement]::LocalizedControlTypeProperty)
85
- $controlType = $element.GetCurrentPropertyValue([AutomationElement]::ControlTypeProperty)
86
-
87
- $tagName = ''
88
- try {
89
- $tagName = $controlType.ProgrammaticName.Split('.')[-1]
90
- } catch {
91
- # fallback to LocalizedControlType ControlType is empty
92
- $tagName = -join ($localizedControlType -split ' ' | ForEach-Object {
93
- $_.Substring(0, 1).ToUpper() + $_.Substring(1).ToLower()
94
- })
95
- }
96
-
97
- $acceleratorKey = $element.GetCurrentPropertyValue([AutomationElement]::AcceleratorKeyProperty)
98
- $accessKey = $element.GetCurrentPropertyValue([AutomationElement]::AccessKeyProperty)
99
- $automationId = $element.GetCurrentPropertyValue([AutomationElement]::AutomationIdProperty)
100
- $className = $element.GetCurrentPropertyValue([AutomationElement]::ClassNameProperty)
101
- $frameworkId = $element.GetCurrentPropertyValue([AutomationElement]::FrameworkIdProperty)
102
- $hasKeyboardfocus = $element.GetCurrentPropertyValue([AutomationElement]::HasKeyboardfocusProperty)
103
- $helpText = $element.GetCurrentPropertyValue([AutomationElement]::HelpTextProperty)
104
- $isContentelement = $element.GetCurrentPropertyValue([AutomationElement]::IsContentelementProperty)
105
- $isControlelement = $element.GetCurrentPropertyValue([AutomationElement]::IsControlelementProperty)
106
- $isEnabled = $element.GetCurrentPropertyValue([AutomationElement]::IsEnabledProperty)
107
- $isKeyboardfocusable = $element.GetCurrentPropertyValue([AutomationElement]::IsKeyboardfocusableProperty)
108
- $isOffscreen = $element.GetCurrentPropertyValue([AutomationElement]::IsOffscreenProperty)
109
- $isPassword = $element.GetCurrentPropertyValue([AutomationElement]::IsPasswordProperty)
110
- $isRequiredforform = $element.GetCurrentPropertyValue([AutomationElement]::IsRequiredforformProperty)
111
- $itemStatus = $element.GetCurrentPropertyValue([AutomationElement]::ItemStatusProperty)
112
- $itemType = $element.GetCurrentPropertyValue([AutomationElement]::ItemTypeProperty)
113
- $name = $element.GetCurrentPropertyValue([AutomationElement]::NameProperty)
114
- $orientation = $element.GetCurrentPropertyValue([AutomationElement]::OrientationProperty)
115
- $processId = $element.GetCurrentPropertyValue([AutomationElement]::ProcessIdProperty)
116
- $runtimeId = $element.GetCurrentPropertyValue([AutomationElement]::RuntimeIdProperty) -join '.'
117
- $boundingRectangle = $element.Current.BoundingRectangle
118
- $x = $boundingRectangle.X - $rootElement.Current.BoundingRectangle.X
119
- $y = $boundingRectangle.Y - $rootElement.Current.BoundingRectangle.Y
120
- $width = $boundingRectangle.Width
121
- $height = $boundingRectangle.Height
122
-
123
- if ($null -eq $xmlDoc) { $xmlDoc = [Xml.XmlDocument]::new() }
124
-
125
- $newXmlElement = $xmlDoc.CreateElement($tagName)
126
- $newXmlElement.SetAttribute("AcceleratorKey", $acceleratorKey)
127
- $newXmlElement.SetAttribute("AccessKey", $accessKey)
128
- $newXmlElement.SetAttribute("AutomationId", $automationId)
129
- $newXmlElement.SetAttribute("ClassName", $className)
130
- $newXmlElement.SetAttribute("FrameworkId", $frameworkId)
131
- $newXmlElement.SetAttribute("HasKeyboardfocus", $hasKeyboardfocus)
132
- $newXmlElement.SetAttribute("HelpText", $helpText)
133
- $newXmlElement.SetAttribute("IsContentelement", $isContentelement)
134
- $newXmlElement.SetAttribute("IsControlelement", $isControlelement)
135
- $newXmlElement.SetAttribute("IsEnabled", $isEnabled)
136
- $newXmlElement.SetAttribute("IsKeyboardfocusable", $isKeyboardfocusable)
137
- $newXmlElement.SetAttribute("IsOffscreen", $isOffscreen)
138
- $newXmlElement.SetAttribute("IsPassword", $isPassword)
139
- $newXmlElement.SetAttribute("IsRequiredforform", $isRequiredforform)
140
- $newXmlElement.SetAttribute("ItemStatus", $itemStatus)
141
- $newXmlElement.SetAttribute("ItemType", $itemType)
142
- $newXmlElement.SetAttribute("LocalizedControlType", $localizedControlType)
143
- $newXmlElement.SetAttribute("Name", $name)
144
- $newXmlElement.SetAttribute("Orientation", $orientation)
145
- $newXmlElement.SetAttribute("ProcessId", $processId)
146
- $newXmlElement.SetAttribute("RuntimeId", $runtimeId)
147
- $newXmlElement.SetAttribute("x", $x)
148
- $newXmlElement.SetAttribute("y", $y)
149
- $newXmlElement.SetAttribute("width", $width)
150
- $newXmlElement.SetAttribute("height", $height)
151
-
152
- $pattern = $null
153
-
154
- if ($element.TryGetCurrentPattern([WindowPattern]::Pattern, [ref]$pattern)) {
155
- $newXmlElement.SetAttribute("CanMaximize", $windowPattern.Current.CanMaximize)
156
- $newXmlElement.SetAttribute("CanMinimize", $windowPattern.Current.CanMinimize)
157
- $newXmlElement.SetAttribute("IsModal", $windowPattern.Current.IsModal)
158
- $newXmlElement.SetAttribute("WindowVisualState", $windowPattern.Current.WindowVisualState)
159
- $newXmlElement.SetAttribute("WindowInteractionState", $windowPattern.Current.WindowInteractionState)
160
- $newXmlElement.SetAttribute("IsTopmost", $windowPattern.Current.IsTopmost)
161
- }
162
-
163
- if ($element.TryGetCurrentPattern([TransformPattern]::Pattern, [ref]$pattern)) {
164
- $newXmlElement.SetAttribute("CanRotate", $windowPattern.Current.CanRotate)
165
- $newXmlElement.SetAttribute("CanResize", $windowPattern.Current.CanResize)
166
- $newXmlElement.SetAttribute("CanMove", $windowPattern.Current.CanMove)
167
- }
168
-
169
- # TODO: more to be added depending on the available patterns
170
-
171
- if ($null -eq $xmlElement) {
172
- $xmlElement = $xmlDoc.AppendChild($newXmlElement)
173
- } else {
174
- $xmlElement = $xmlElement.AppendChild($newXmlElement)
175
- }
176
-
177
- $elementsToProcess = New-Object System.Collections.Queue
178
- $element.FindAll([TreeScope]::Children, $cacheRequest.TreeFilter) | ForEach-Object {
179
- $elementsToProcess.Enqueue($_)
180
- }
181
-
182
- while ($elementsToProcess.Count -gt 0) {
183
- $currentElement = $elementsToProcess.Dequeue()
184
- Get-PageSource $currentElement $xmlDoc $xmlElement | Out-Null
185
- }
186
- } catch {
187
- # noop
188
- }
189
-
190
- return $xmlElement
191
- }
192
- `;
@@ -1,28 +0,0 @@
1
- import * as actions from './actions';
2
- import * as powershell from './powershell';
3
- import * as element from './element';
4
- import * as extension from './extension';
5
- import * as device from './device';
6
- import * as system from './system';
7
- import * as app from './app';
8
-
9
- const commands = {
10
- ...actions,
11
- ...powershell,
12
- ...element,
13
- ...extension,
14
- ...system,
15
- ...device,
16
- ...app,
17
- // add the rest of the commands here
18
- };
19
-
20
- type Commands = {
21
- [key in keyof typeof commands]: typeof commands[key];
22
- };
23
-
24
- declare module '../driver' {
25
- interface NovaWindows2Driver extends Commands { }
26
- }
27
-
28
- export default commands;
@@ -1,256 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- import { NovaWindows2Driver } from '../driver';
3
- import { errors } from '@appium/base-driver';
4
- import { FIND_CHILDREN_RECURSIVELY, PAGE_SOURCE } from './functions';
5
-
6
- const SET_UTF8_ENCODING = /* ps1 */ `$OutputEncoding = [Console]::OutputEncoding = [Text.Encoding]::UTF8`;
7
- const ADD_NECESSARY_ASSEMBLIES = /* ps1 */ `Add-Type -AssemblyName UIAutomationClient; Add-Type -AssemblyName System.Drawing; Add-Type -AssemblyName PresentationCore; Add-Type -AssemblyName System.Windows.Forms`;
8
- const USE_UI_AUTOMATION_CLIENT = /* ps1 */ `using namespace System.Windows.Automation`;
9
- const INIT_CACHE_REQUEST = /* ps1 */ `($cacheRequest = New-Object System.Windows.Automation.CacheRequest).TreeFilter = [AndCondition]::new([Automation]::ControlViewCondition, [NotCondition]::new([PropertyCondition]::new([AutomationElement]::FrameworkIdProperty, 'Chrome'))); $cacheRequest.Push()`;
10
- const INIT_ROOT_ELEMENT = /* ps1 */ `$rootElement = [AutomationElement]::RootElement`;
11
- const NULL_ROOT_ELEMENT = /* ps1 */ `$rootElement = $null`;
12
- const INIT_ELEMENT_TABLE = /* ps1 */ `$elementTable = New-Object System.Collections.Generic.Dictionary[[string]\`,[AutomationElement]]`;
13
-
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> {
50
- this.log.debug('Starting new PowerShell session...');
51
- const powerShell = spawn('powershell.exe', ['-NoExit', '-Command', '-']);
52
- powerShell.stdout.setEncoding('utf8');
53
- powerShell.stderr.setEncoding('utf8');
54
-
55
- powerShell.stdout.on('data', (chunk: any) => {
56
- this.powerShellStdOut += chunk.toString();
57
- });
58
-
59
- powerShell.stderr.on('data', (chunk: any) => {
60
- this.powerShellStdErr += chunk.toString();
61
- });
62
-
63
- this.powerShell = powerShell;
64
-
65
- if (this.caps.appWorkingDir) {
66
- const envVarsSet: Set<string> = new Set();
67
- const matches = this.caps.appWorkingDir.matchAll(/%([^%]+)%/g);
68
-
69
- for (const match of matches) {
70
- envVarsSet.add(match[1]);
71
- }
72
- const envVars = Array.from(envVarsSet);
73
- for (const envVar of envVars) {
74
- this.caps.appWorkingDir = this.caps.appWorkingDir.replaceAll(`%${envVar}%`, process.env[envVar.toUpperCase()] ?? '');
75
- }
76
- // Use raw execution to bypass queue
77
- await executeRawCommand(this, `Set-Location -Path '${this.caps.appWorkingDir}'`);
78
- }
79
-
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);
86
-
87
- // initialize functions
88
- await executeRawCommand(this, PAGE_SOURCE);
89
- await executeRawCommand(this, FIND_CHILDREN_RECURSIVELY);
90
-
91
- if ((!this.caps.app && !this.caps.appTopLevelWindow) || (!this.caps.app || this.caps.app.toLowerCase() === 'none')) {
92
- this.log.info(`No app or top-level window specified in capabilities. Setting root element to null.`);
93
- await executeRawCommand(this, NULL_ROOT_ELEMENT);
94
- }
95
-
96
- if (this.caps.app && this.caps.app.toLowerCase() === 'root') {
97
- this.log.info(`'root' specified as app in capabilities. Setting root element to desktop root.`);
98
- await executeRawCommand(this, INIT_ROOT_ELEMENT);
99
- }
100
-
101
- if (this.caps.app && this.caps.app.toLowerCase() !== 'none' && this.caps.app.toLowerCase() !== 'root') {
102
- this.log.info(`Application path specified in capabilities: ${this.caps.app}`);
103
- const envVarsSet: Set<string> = new Set();
104
- const matches = this.caps.app.matchAll(/%([^%]+)%/g);
105
-
106
- for (const match of matches) {
107
- envVarsSet.add(match[1]);
108
- }
109
-
110
- const envVars = Array.from(envVarsSet);
111
- this.log.info(`Detected the following environment variables in app path: ${envVars.map((envVar) => `%${envVar}%`).join(', ')}`);
112
-
113
- for (const envVar of envVars) {
114
- this.caps.app = this.caps.app.replaceAll(`%${envVar}%`, process.env[envVar.toUpperCase()] ?? '');
115
- }
116
-
117
- await this.changeRootElement(this.caps.app);
118
- }
119
-
120
- if (this.caps.appTopLevelWindow) {
121
- const nativeWindowHandle = Number(this.caps.appTopLevelWindow);
122
-
123
- if (isNaN(nativeWindowHandle)) {
124
- throw new errors.InvalidArgumentError(`Invalid capabilities. Capability 'appTopLevelWindow' is not a valid native window handle.`);
125
- }
126
-
127
- await this.changeRootElement(nativeWindowHandle);
128
- }
129
- }
130
-
131
- export async function sendIsolatedPowerShellCommand(this: NovaWindows2Driver, command: string): Promise<string> {
132
- const magicNumber = 0xF2EE;
133
-
134
- const powerShell = spawn('powershell.exe', ['-NoExit', '-Command', '-']);
135
- try {
136
- powerShell.stdout.setEncoding('utf8');
137
- powerShell.stdout.setEncoding('utf8');
138
-
139
- powerShell.stdout.on('data', (chunk: any) => {
140
- this.powerShellStdOut += chunk.toString();
141
- });
142
-
143
- powerShell.stderr.on('data', (chunk: any) => {
144
- this.powerShellStdErr += chunk.toString();
145
- });
146
-
147
- const result = await new Promise<string>((resolve, reject) => {
148
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
149
- const powerShell = this.powerShell!;
150
-
151
- this.powerShellStdOut = '';
152
- this.powerShellStdErr = '';
153
-
154
- powerShell.stdin.write(`${SET_UTF8_ENCODING}\n`);
155
- if (this.caps.appWorkingDir) {
156
- const envVarsSet: Set<string> = new Set();
157
- const matches = this.caps.appWorkingDir.matchAll(/%([^%]+)%/g);
158
-
159
- for (const match of matches) {
160
- envVarsSet.add(match[1]);
161
- }
162
- const envVars = Array.from(envVarsSet);
163
- for (const envVar of envVars) {
164
- this.caps.appWorkingDir = this.caps.appWorkingDir.replaceAll(`%${envVar}%`, process.env[envVar.toUpperCase()] ?? '');
165
- }
166
- powerShell.stdin.write(`Set-Location -Path '${this.caps.appWorkingDir}'\n`);
167
- }
168
- powerShell.stdin.write(`${command}\n`);
169
- powerShell.stdin.write(/* ps1 */ `Write-Output $([char]0x${magicNumber.toString(16)})\n`);
170
-
171
- const onClose = (code: number) => {
172
- reject(new errors.UnknownError(`PowerShell process exited unexpectedly with code ${code}`));
173
- };
174
- powerShell.on('close', onClose);
175
-
176
- const onData: Parameters<typeof powerShell.stdout.on>[1] = ((chunk: any) => {
177
- const magicChar = String.fromCharCode(magicNumber);
178
- if (chunk.toString().includes(magicChar)) {
179
- powerShell.stdout.off('data', onData);
180
- powerShell.off('close', onClose);
181
- if (this.powerShellStdErr) {
182
- reject(new errors.UnknownError(this.powerShellStdErr));
183
- } else {
184
- resolve(this.powerShellStdOut.replace(`${magicChar}`, '').trim());
185
- }
186
- }
187
- }).bind(this);
188
-
189
- powerShell.stdout.on('data', onData);
190
- });
191
-
192
- // commented out for now to avoid cluttering the logs with long command outputs
193
- // this.log.debug(`PowerShell command executed:\n${command}\n\nCommand output below:\n${result}\n --------`);
194
-
195
- return result;
196
- } finally {
197
- // Ensure the isolated PowerShell process is terminated
198
- try {
199
- powerShell.kill();
200
- } catch (e) {
201
- this.log.warn(`Failed to terminate isolated PowerShell process: ${e}`);
202
- }
203
- }
204
- }
205
-
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
- }
212
-
213
- // Use the extracted raw command function
214
- return await executeRawCommand(this, command);
215
- };
216
-
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);
223
-
224
- return this.commandQueue;
225
- }
226
-
227
- export async function terminatePowerShellSession(this: NovaWindows2Driver): Promise<void> {
228
- if (!this.powerShell) {
229
- return;
230
- }
231
-
232
- if (this.powerShell.exitCode !== null) {
233
- this.log.debug(`PowerShell session already terminated.`);
234
- return;
235
- }
236
-
237
- this.log.debug(`Terminating PowerShell session...`);
238
- const waitForClose = new Promise<void>((resolve, reject) => {
239
- if (!this.powerShell) {
240
- resolve();
241
- }
242
-
243
- this.powerShell?.once('close', () => {
244
- resolve();
245
- });
246
-
247
- this.powerShell?.once('error', (err: Error) => {
248
- reject(err);
249
- });
250
- });
251
-
252
-
253
- this.powerShell.kill();
254
- await waitForClose;
255
- this.log.debug(`PowerShell session terminated successfully.`);
256
- }
@@ -1,7 +0,0 @@
1
- import { Orientation } from '@appium/types';
2
- import { NovaWindows2Driver } from '../driver';
3
- import { getDisplayOrientation } from '../winapi/user32';
4
-
5
- export function getOrientation(this: NovaWindows2Driver): Orientation {
6
- return getDisplayOrientation();
7
- }
package/lib/constants.ts DELETED
@@ -1 +0,0 @@
1
- export const POWER_SHELL_FEATURE = 'power_shell';
@@ -1,43 +0,0 @@
1
- import type { Constraints } from '@appium/types';
2
-
3
- export const UI_AUTOMATION_DRIVER_CONSTRAINTS = {
4
- platformName: {
5
- isString: true,
6
- inclusionCaseInsensitive: ['Windows'],
7
- presence: true,
8
- },
9
- smoothPointerMove: {
10
- isString: true,
11
- },
12
- delayBeforeClick: {
13
- isNumber: true,
14
- },
15
- delayAfterClick: {
16
- isNumber: true,
17
- },
18
- appTopLevelWindow: {
19
- isString: true,
20
- },
21
- shouldCloseApp: {
22
- isBoolean: true,
23
- },
24
- appArguments: {
25
- isString: true,
26
- },
27
- appWorkingDir: {
28
- isString: true,
29
- },
30
- prerun: {
31
- isObject: true,
32
- },
33
- postrun: {
34
- isObject: true,
35
- },
36
- isolatedScriptExecution: {
37
- isBoolean: true,
38
- }
39
- } as const satisfies Constraints;
40
-
41
- export default UI_AUTOMATION_DRIVER_CONSTRAINTS;
42
-
43
- export type NovaWindowsDriverConstraints = typeof UI_AUTOMATION_DRIVER_CONSTRAINTS;