Skip to content

Commit 3a1a630

Browse files
authored
Merge pull request #39 from AutomateThePlanet/releases/v1.2.0
Releases/v1.2.0
2 parents 990fa7b + 9c1d43b commit 3a1a630

File tree

16 files changed

+225
-92
lines changed

16 files changed

+225
-92
lines changed

.github/workflows/lint-build.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ name: Lint & Build
22

33
on:
44
pull_request:
5-
branches: [ "main" ]
5+
branches:
6+
- main
7+
- develop
68

79
jobs:
810
build:
@@ -11,12 +13,12 @@ jobs:
1113

1214
steps:
1315
- name: Checkout repository
14-
uses: actions/checkout@v4
16+
uses: actions/checkout@v5
1517

1618
- name: Set up Node.js
17-
uses: actions/setup-node@v4
19+
uses: actions/setup-node@v6
1820
with:
19-
node-version: 18.x
21+
node-version: 24.x
2022

2123
- name: Install dependencies
2224
run: npm install --no-package-lock

.github/workflows/release.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@ name: Release
33
on:
44
workflow_dispatch:
55
push:
6-
branches: [ main ]
6+
branches:
7+
- main
8+
- develop
79

810
jobs:
911
build:
1012
runs-on: ubuntu-latest
1113
environment: Release
1214
steps:
13-
- uses: actions/checkout@v2
15+
- uses: actions/checkout@v5
1416
- name: Use Node.js
15-
uses: actions/setup-node@v3
17+
uses: actions/setup-node@v6
1618
with:
17-
node-version: lts/*
19+
node-version: 24.x
1820
- run: npm install --no-package-lock
1921
name: Install dependencies
2022
- run: npm run build

.releaserc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{
2+
"branches": [
3+
"main",
4+
{ "name": "develop", "prerelease": "preview" }
5+
],
26
"plugins": [
37
["@semantic-release/commit-analyzer", {
48
"preset": "angular",

README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ It’s designed to handle real-world scenarios where traditional drivers fall sh
1111

1212
> **Note**
1313
>
14-
> This driver is built for Appium 2 and is not compatible with Appium 1. To get started,
15-
> clone the repository and run `npm install` to resolve dependencies. Then, use `npm run build`
16-
> to build the driver. Finally, add it to your Appium 2 distribution with the command:
14+
> This driver is built for Appium 2/3 and is not compatible with Appium 1. To install
15+
> the driver, simply run:
1716
> `appium driver install --source=npm appium-novawindows-driver`
1817
1918

@@ -44,6 +43,11 @@ delayBeforeClick | Time in milliseconds before a click is performed.
4443
delayAfterClick | Time in milliseconds after a click is performed.
4544
appTopLevelWindow | The handle of an existing application top-level window to attach to. It can be a number or string (not necessarily hexadecimal). Example: `12345`, `0x12345`.
4645
shouldCloseApp | Whether to close the window of the application in test after the session finishes. Default is `true`.
46+
appArguments | Optional string of arguments to pass to the app on launch.
47+
appWorkingDir | Optional working directory path for the application.
48+
prerun | An object containing either `script` or `command` key. The value of each key must be a valid PowerShell script or command to be executed prior to the WinAppDriver session startup. See [Power Shell commands execution](#power-shell-commands-execution) for more details. Example: `{script: 'Get-Process outlook -ErrorAction SilentlyContinue'}`
49+
postrun | An object containing either `script` or `command` key. The value of each key must be a valid PowerShell script or command to be executed after WinAppDriver session is stopped. See [Power Shell commands execution](#power-shell-commands-execution) for more details.
50+
isolatedScriptExecution | Whether PowerShell scripts are executed in an isolated session. Default is `false`.
4751

4852
Please note that more capabilities will be added as the development of this driver progresses. Since it is still in its early stages, some features may be missing or subject to change. If you need a specific capability or encounter any issues, please feel free to open an issue.
4953

@@ -103,8 +107,7 @@ key to the listof enabled insecure features. Refer to [Appium Security document]
103107
It is possible to ether execute a single Power Shell command or a whole script
104108
and get its stdout in response. If the script execution returns non-zero exit code then an exception
105109
is going to be thrown. The exception message will contain the actual stderr. Unlike, Appium Windows Driver,
106-
there is no difference if you paste the script with `command` or `script` argument. For ease of use, you
107-
can even pass the script as a string only, it will work the same way.
110+
there is no difference if you paste the script with `command` or `script` argument. For ease of use, you can pass the script as a string when executing a PowerShell command directly via the driver. Note: This shorthand does not work when using the prerun or postrun capabilities, which require full object syntax.
108111
Here's an example code of how to control the Notepad process:
109112

110113
```java

eslint.config.mjs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
// @ts-check
22

3+
import { defineConfig } from 'eslint/config';
34
import eslint from '@eslint/js';
4-
import { config, configs } from 'typescript-eslint';
55
import appiumConfig from '@appium/eslint-config-appium-ts';
66

77

8-
export default config(
9-
...appiumConfig,
8+
export default defineConfig(
109
eslint.configs.recommended,
11-
configs.recommended,
10+
...appiumConfig,
1211
);

lib/commands/app.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,24 @@ import {
2323
const GET_PAGE_SOURCE_COMMAND = pwsh$ /* ps1 */ `
2424
$el = ${0}
2525
26+
if ($el -eq $null) {
27+
$dummy = [xml]'<DummyRoot></DummyRoot>'
28+
return $dummy.OuterXml
29+
}
30+
2631
Get-PageSource $el |
2732
ForEach-Object { $_.OuterXml }
2833
`;
2934

3035
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+
3144
$rect = $rootElement.Current.BoundingRectangle
3245
$bitmap = New-Object Drawing.Bitmap([int32]$rect.Width, [int32]$rect.Height)
3346
@@ -104,12 +117,13 @@ export async function setWindow(this: NovaWindowsDriver, nameOrHandle: string):
104117
const elementId = await this.sendPowerShellCommand(AutomationElement.rootElement.findFirst(TreeScope.CHILDREN, condition).buildCommand());
105118

106119
if (elementId.trim() !== '') {
120+
this.log.info(`Found window with name '${name}'. Setting it as the root element.`);
107121
await this.sendPowerShellCommand(/* ps1 */ `$rootElement = ${new FoundAutomationElement(elementId).buildCommand()}`);
108122
trySetForegroundWindow(handle);
109123
return;
110124
}
111125

112-
this.log.info(`Failed to locate window. Sleeping for 500 milliseconds and retrying... (${i}/20)`); // TODO: make a setting for the number of retries or timeout
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
113127
await sleep(500); // TODO: make a setting for the sleep timeout
114128
}
115129

@@ -136,12 +150,14 @@ export async function changeRootElement(this: NovaWindowsDriver, pathOrNativeWin
136150

137151
const path = pathOrNativeWindowHandle;
138152
if (path.includes('!') && path.includes('_') && !(path.includes('/') || path.includes('\\'))) {
153+
this.log.debug('Detected app path to be in the UWP format.');
139154
await this.sendPowerShellCommand(/* ps1 */ `Start-Process 'explorer.exe' 'shell:AppsFolder\\${path}'${this.caps.appArguments ? ` -ArgumentList '${this.caps.appArguments}'` : ''}`);
140155
await sleep(500); // TODO: make a setting for the initial wait time
141156
for (let i = 1; i <= 20; i++) {
142157
const result = await this.sendPowerShellCommand(/* ps1 */ `(Get-Process -Name 'ApplicationFrameHost').Id`);
143158
const processIds = result.split('\n').map((pid) => pid.trim()).filter(Boolean).map(Number);
144159

160+
this.log.debug('Process IDs of ApplicationFrameHost processes: ' + processIds.join(', '));
145161
try {
146162
await this.attachToApplicationWindow(processIds);
147163
return;
@@ -153,6 +169,7 @@ export async function changeRootElement(this: NovaWindowsDriver, pathOrNativeWin
153169
await sleep(500); // TODO: make a setting for the sleep timeout
154170
}
155171
} else {
172+
this.log.debug('Detected app path to be in the classic format.');
156173
const normalizedPath = normalize(path);
157174
await this.sendPowerShellCommand(/* ps1 */ `Start-Process '${normalizedPath}'${this.caps.appArguments ? ` -ArgumentList '${this.caps.appArguments}'` : ''}`);
158175
await sleep(500); // TODO: make a setting for the initial wait time
@@ -163,6 +180,7 @@ export async function changeRootElement(this: NovaWindowsDriver, pathOrNativeWin
163180
const processName = executable.endsWith('.exe') ? executable.slice(0, executable.length - 4) : executable;
164181
const result = await this.sendPowerShellCommand(/* ps1 */ `(Get-Process -Name '${processName}' | Sort-Object StartTime -Descending).Id`);
165182
const processIds = result.split('\n').map((pid) => pid.trim()).filter(Boolean).map(Number);
183+
this.log.debug(`Process IDs of '${processName}' processes: ` + processIds.join(', '));
166184

167185
await this.attachToApplicationWindow(processIds);
168186
return;
@@ -182,6 +200,7 @@ export async function changeRootElement(this: NovaWindowsDriver, pathOrNativeWin
182200

183201
export async function attachToApplicationWindow(this: NovaWindowsDriver, processIds: number[]): Promise<void> {
184202
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(', ')}`);
185204

186205
if (nativeWindowHandles.length !== 0) {
187206
const elementId = await this.sendPowerShellCommand(AutomationElement.rootElement.findFirst(TreeScope.CHILDREN, new PropertyCondition(Property.NATIVE_WINDOW_HANDLE, new PSInt32(nativeWindowHandles[0]))).buildCommand());

0 commit comments

Comments
 (0)