Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@ updates:
commit-message:
prefix: "chore"
include: "scope"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: daily
time: "11:00"
open-pull-requests-limit: 10
commit-message:
prefix: "chore"
include: "scope"
12 changes: 3 additions & 9 deletions .github/workflows/pr-title.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,8 @@ on:
pull_request:
types: [opened, edited, synchronize, reopened]


jobs:
lint:
name: https://www.conventionalcommits.org
runs-on: ubuntu-latest
steps:
- uses: beemojs/conventional-pr-action@v3
with:
config-preset: angular
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: appium/appium-workflows/.github/workflows/pr-title.yml@main
with:
config-preset: angular
6 changes: 4 additions & 2 deletions .github/workflows/publish.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: lts/*
- run: npm install --no-package-lock
- uses: SocketDev/action@v1
with:
mode: firewall-free
- run: sfw npm install --no-package-lock
name: Install dependencies
- run: npm run test
name: Run NPM Test
Expand All @@ -37,4 +40,3 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
name: Release

21 changes: 9 additions & 12 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,25 @@ on: [pull_request, push]


jobs:
prepare_matrix:
runs-on: ubuntu-latest
outputs:
versions: ${{ steps.generate-matrix.outputs.versions }}
steps:
- name: Select 3 most recent LTS versions of Node.js
id: generate-matrix
run: echo "versions=$(curl -s https://endoflife.date/api/nodejs.json | jq -c '[[.[] | select(.lts != false)][:3] | .[].cycle | tonumber]')" >> "$GITHUB_OUTPUT"
node_matrix:
uses: appium/appium-workflows/.github/workflows/node-lts-matrix.yml@main

test:
needs:
- prepare_matrix
- node_matrix
strategy:
matrix:
node-version: ${{ fromJSON(needs.prepare_matrix.outputs.versions) }}
runs-on: windows-latest
node-version: ${{ fromJSON(needs.node_matrix.outputs.versions) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm install --no-package-lock
- uses: SocketDev/action@v1
with:
mode: firewall-free
- run: sfw npm install --no-package-lock
name: Install dev dependencies
- run: npm run lint
name: Run linter
Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions lib/desired-caps.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type {Constraints} from '@appium/types';

export const desiredCapConstraints = {
// https://github.com/microsoft/WinAppDriver/blob/master/Docs/AuthoringTestScripts.md#supported-capabilities
platformName: {
Expand Down Expand Up @@ -47,6 +49,4 @@ export const desiredCapConstraints = {
wadUrl: {
isString: true
},
} as const;

export default desiredCapConstraints;
} as const satisfies Constraints;
153 changes: 86 additions & 67 deletions lib/driver.js → lib/driver.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
import _ from 'lodash';
import type {
RouteMatcher,
HTTPMethod,
HTTPBody,
DefaultCreateSessionResult,
DriverData,
InitialOpts,
StringRecord,
ExternalDriver,
DriverOpts,
W3CDriverCaps,
} from '@appium/types';
import { BaseDriver } from 'appium/driver';
import { system } from 'appium/support';
import { WinAppDriver } from './winappdriver';
import type { WindowsDriverCaps } from './winappdriver';
import { desiredCapConstraints } from './desired-caps';
import * as appManagementCommands from './commands/app-management';
import * as clipboardCommands from './commands/clipboard';
Expand All @@ -19,8 +32,7 @@ import { POWER_SHELL_FEATURE } from './constants';
import { newMethodMap } from './method-map';
import { executeMethodMap } from './execute-method-map';

/** @type {import('@appium/types').RouteMatcher[]} */
const NO_PROXY = [
const NO_PROXY: RouteMatcher[] = [
['GET', new RegExp('^/session/[^/]+/appium/(?!app/)[^/]+')],
['POST', new RegExp('^/session/[^/]+/appium/(?!app/)[^/]+')],
['POST', new RegExp('^/session/[^/]+/element/[^/]+/elements?$')],
Expand Down Expand Up @@ -48,28 +60,20 @@ const NO_PROXY = [
];

// Appium instantiates this class
/**
* @implements {ExternalDriver<WindowsDriverConstraints, string>}
* @extends {BaseDriver<WindowsDriverConstraints>}
*/
export class WindowsDriver extends BaseDriver {
/** @type {boolean} */
isProxyActive;

/** @type {import('@appium/types').RouteMatcher[]} */
jwpProxyAvoid;

/** @type {WinAppDriver} */
winAppDriver;

/** @type {import('./commands/record-screen').ScreenRecorder | null} */
_screenRecorder;
export class WindowsDriver
extends BaseDriver<WindowsDriverConstraints, StringRecord>
implements ExternalDriver<WindowsDriverConstraints, string, StringRecord>
{
private isProxyActive: boolean;
private jwpProxyAvoid: RouteMatcher[];
private _winAppDriver: WinAppDriver | null;
_screenRecorder: recordScreenCommands.ScreenRecorder | null;
public proxyReqRes: (...args: any) => any;

static newMethodMap = newMethodMap;
static executeMethodMap = executeMethodMap;

constructor (opts = {}, shouldValidateCaps = true) {
// @ts-ignore TODO: Make opts typed
constructor(opts: InitialOpts, shouldValidateCaps = true) {
super(opts, shouldValidateCaps);
this.desiredCapConstraints = desiredCapConstraints;
this.locatorStrategies = [
Expand All @@ -83,74 +87,67 @@ export class WindowsDriver extends BaseDriver {
this.resetState();
}

resetState () {
this.jwpProxyAvoid = NO_PROXY;
this.isProxyActive = false;
// @ts-ignore It's ok
this.winAppDriver = null;
this._screenRecorder = null;
get winAppDriver(): WinAppDriver {
if (!this._winAppDriver) {
throw new Error('WinAppDriver is not started');
}
return this._winAppDriver;
}

// @ts-ignore TODO: Make args typed
async createSession (...args) {
override async createSession(
w3cCaps1: W3CWindowsDriverCaps,
w3cCaps2?: W3CWindowsDriverCaps,
w3cCaps3?: W3CWindowsDriverCaps,
driverData?: DriverData[]
): Promise<DefaultCreateSessionResult<WindowsDriverConstraints>> {
if (!system.isWindows()) {
throw new Error('WinAppDriver tests only run on Windows');
}

try {
// @ts-ignore TODO: Make args typed
const [sessionId, caps] = await super.createSession(...args);
const [sessionId, caps] = await super.createSession(w3cCaps1, w3cCaps2, w3cCaps3, driverData);
this.caps = caps;
this.opts = this.opts as WindowsDriverOpts;
if (caps.prerun) {
this.log.info('Executing prerun PowerShell script');
if (!_.isString(caps.prerun.command) && !_.isString(caps.prerun.script)) {
const prerun = caps.prerun as PrerunCapability;
if (!_.isString(prerun.command) && !_.isString(prerun.script)) {
throw new Error(`'prerun' capability value must either contain ` +
`'script' or 'command' entry of string type`);
}
this.assertFeatureEnabled(POWER_SHELL_FEATURE);
const output = await this.execPowerShell(caps.prerun);
const output = await this.execPowerShell(prerun);
if (output) {
this.log.info(`Prerun script output: ${output}`);
}
}
await this.startWinAppDriverSession();
return [sessionId, caps];
} catch (e) {
} catch (e: any) {
await this.deleteSession();
throw e;
}
}

async startWinAppDriverSession () {
this.winAppDriver = new WinAppDriver(this.log, {
url: this.opts.wadUrl,
port: this.opts.systemPort,
reqBasePath: this.basePath,
});
await this.winAppDriver.start(this.caps);
this.proxyReqRes = this.winAppDriver.proxy?.proxyReqRes.bind(this.winAppDriver.proxy);
// now that everything has started successfully, turn on proxying so all
// subsequent session requests go straight to/from WinAppDriver
this.isProxyActive = true;
}

async deleteSession () {
override async deleteSession(): Promise<void> {
this.log.debug('Deleting WinAppDriver session');
await this._screenRecorder?.stop(true);
await this.winAppDriver?.stop();
await this._winAppDriver?.stop();

if (this.opts.postrun) {
if (!_.isString(this.opts.postrun.command) && !_.isString(this.opts.postrun.script)) {
const postrun = this.opts.postrun as PostrunCapability | undefined;
if (postrun) {
if (!_.isString(postrun.command) && !_.isString(postrun.script)) {
this.log.error(`'postrun' capability value must either contain ` +
`'script' or 'command' entry of string type`);
} else {
this.log.info('Executing postrun PowerShell script');
try {
this.assertFeatureEnabled(POWER_SHELL_FEATURE);
const output = await this.execPowerShell(this.opts.postrun);
const output = await this.execPowerShell(postrun);
if (output) {
this.log.info(`Postrun script output: ${output}`);
}
} catch (e) {
} catch (e: any) {
this.log.error(e.message);
}
}
Expand All @@ -162,25 +159,45 @@ export class WindowsDriver extends BaseDriver {
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
proxyActive (sessionId) {
override proxyActive(sessionId: string): boolean {
return this.isProxyActive;
}

canProxy () {
override canProxy(): boolean {
// we can always proxy to the WinAppDriver server
return true;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
getProxyAvoidList (sessionId) {
override getProxyAvoidList(sessionId: string): RouteMatcher[] {
return this.jwpProxyAvoid;
}

async proxyCommand (url, method, body) {
async proxyCommand(url: string, method: HTTPMethod, body: HTTPBody = null): Promise<any> {
if (!this.winAppDriver?.proxy) {
throw new Error('The proxy must be defined in order to send commands');
}
return /** @type {any} */ (await this.winAppDriver.proxy.command(url, method, body));
return await this.winAppDriver.proxy.command(url, method, body);
}

async startWinAppDriverSession(): Promise<void> {
this._winAppDriver = new WinAppDriver(this.log, {
url: this.opts.wadUrl,
port: this.opts.systemPort,
reqBasePath: this.basePath,
});
await this.winAppDriver.start(this.caps as any as WindowsDriverCaps);
this.proxyReqRes = this.winAppDriver.proxy?.proxyReqRes.bind(this.winAppDriver.proxy);
// now that everything has started successfully, turn on proxying so all
// subsequent session requests go straight to/from WinAppDriver
this.isProxyActive = true;
}

private resetState(): void {
this.jwpProxyAvoid = NO_PROXY;
this.isProxyActive = false;
this._winAppDriver = null;
this._screenRecorder = null;
}

windowsLaunchApp = appManagementCommands.windowsLaunchApp;
Expand All @@ -197,7 +214,6 @@ export class WindowsDriver extends BaseDriver {
windowsDeleteFile = fileCommands.windowsDeleteFile;
windowsDeleteFolder = fileCommands.windowsDeleteFolder;

// @ts-ignore This is expected
findElOrEls = findCommands.findElOrEls;

getWindowSize = generalCommands.getWindowSize;
Expand Down Expand Up @@ -230,13 +246,16 @@ export class WindowsDriver extends BaseDriver {

export default WindowsDriver;

/**
* @typedef {typeof desiredCapConstraints} WindowsDriverConstraints
* @typedef {import('@appium/types').DriverOpts<WindowsDriverConstraints>} WindowsDriverOpts
*/
interface PrerunCapability {
command?: string;
script?: string;
}

interface PostrunCapability {
command?: string;
script?: string;
}

/**
* @template {import('@appium/types').Constraints} C
* @template [Ctx=string]
* @typedef {import('@appium/types').ExternalDriver<C, Ctx>} ExternalDriver
*/
type WindowsDriverConstraints = typeof desiredCapConstraints;
type WindowsDriverOpts = DriverOpts<WindowsDriverConstraints>;
type W3CWindowsDriverCaps = W3CDriverCaps<WindowsDriverConstraints>;
6 changes: 4 additions & 2 deletions lib/execute-method-map.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { ExecuteMethodMap } from '@appium/types';
import type { ExecuteMethodMap } from '@appium/types';
import type { WindowsDriver } from './driver';

export const executeMethodMap = {
'windows: startRecordingScreen': {
// @ts-ignore Type checked is confused
command: 'windowsStartRecordingScreen',
params: {
optional: [
Expand Down Expand Up @@ -142,4 +144,4 @@ export const executeMethodMap = {
],
},
},
} as const satisfies ExecuteMethodMap<any>;
} as const satisfies ExecuteMethodMap<WindowsDriver>;
Loading