diff --git a/package-lock.json b/package-lock.json index 8f77afa..06a4fd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "eslint-plugin-prettier": "^5.1.3", "jest": "^29.7.0", "mocha": "^10.4.0", + "prettier": "^3.3.2", "rimraf": "^5.0.7", "ts-node": "^10.9.2", "typescript": "^5.4.5", @@ -19655,7 +19656,6 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", "dev": true, - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, diff --git a/src/android.ts b/src/android.ts index 6f44546..d662a03 100644 --- a/src/android.ts +++ b/src/android.ts @@ -1,26 +1,18 @@ import AndroidUiautomator2Driver from 'appium-uiautomator2-driver'; -import { log } from './logger'; import type { InitialOpts } from '@appium/types'; import { AppiumFlutterDriver } from './driver'; import ADB from 'appium-adb'; -const setupNewAndroidDriver = async ( +export async function startAndroidSession( + this: AppiumFlutterDriver, ...args: any[] -): Promise => { +): Promise { + this.log.info(`Starting an Android proxy session`); const androiddriver = new AndroidUiautomator2Driver({} as InitialOpts); //@ts-ignore Args are ok await androiddriver.createSession(...args); return androiddriver; -}; - -export const startAndroidSession = async ( - flutterDriver: AppiumFlutterDriver, - caps: Record, - ...args: any[] -): Promise => { - log.info(`Starting an Android proxy session`); - return await setupNewAndroidDriver(...args); -}; +} export async function androidPortForward( adb: ADB, diff --git a/src/commands/element.ts b/src/commands/element.ts index f403f5c..c5491ac 100644 --- a/src/commands/element.ts +++ b/src/commands/element.ts @@ -1,18 +1,19 @@ -import { log } from '../logger'; import _ from 'lodash'; import { getProxyDriver } from '../utils'; import { JWProxy } from '@appium/base-driver'; import AndroidUiautomator2Driver from 'appium-uiautomator2-driver'; import { W3C_ELEMENT_KEY } from '@appium/base-driver/build/lib/constants'; +import type { AppiumFlutterDriver } from '../driver'; + export const ELEMENT_CACHE = new Map(); export async function findElOrEls( + this: AppiumFlutterDriver, strategy: string, selector: string, mult: boolean, context: string, ): Promise { - log.info('Finding element or elements', strategy, selector, mult, context); - const driver = await getProxyDriver(strategy, this.proxy, this.proxydriver); + const driver = await getProxyDriver.bind(this)(strategy); let elementBody; if ( !(driver instanceof JWProxy) && @@ -43,23 +44,30 @@ export async function findElOrEls( } } -export async function click(element: string) { +export async function click(this: AppiumFlutterDriver, element: string) { const driver = ELEMENT_CACHE.get(element); return await driver.command(`/element/${element}/click`, 'POST', { element, }); } -export async function getText(elementId: string) { +export async function getText(this: AppiumFlutterDriver, elementId: string) { const driver = ELEMENT_CACHE.get(elementId); return String(await driver.command(`/element/${elementId}/text`, 'GET', {})); } -export async function elementEnabled(elementId: string) { +export async function elementEnabled( + this: AppiumFlutterDriver, + elementId: string, +) { return toBool(await this.getAttribute('enabled', elementId)); } -export async function getAttribute(attribute: string, elementId: string) { +export async function getAttribute( + this: AppiumFlutterDriver, + attribute: string, + elementId: string, +) { const driver = ELEMENT_CACHE.get(elementId); return await driver.command( `/element/${elementId}/attribute/${attribute}`, @@ -68,21 +76,28 @@ export async function getAttribute(attribute: string, elementId: string) { ); } -export async function setValue(text: string, elementId: string) { +export async function setValue( + this: AppiumFlutterDriver, + text: string, + elementId: string, +) { const driver = ELEMENT_CACHE.get(elementId); return await driver.command(`/element/${elementId}/value`, 'POST', { text, }); } -export async function clear(elementId: string) { +export async function clear(this: AppiumFlutterDriver, elementId: string) { const driver = ELEMENT_CACHE.get(elementId); return await driver.command(`/element/${elementId}/clear`, 'POST', { elementId, }); } -export async function elementDisplayed(elementId: string) { +export async function elementDisplayed( + this: AppiumFlutterDriver, + elementId: string, +) { return await this.getAttribute('displayed', elementId); } diff --git a/src/driver.ts b/src/driver.ts index 544f207..04d7ee5 100644 --- a/src/driver.ts +++ b/src/driver.ts @@ -10,7 +10,7 @@ import type { type FlutterDriverConstraints = typeof desiredCapConstraints; import XCUITestDriver from 'appium-xcuitest-driver/build/lib/driver'; import AndroidUiautomator2Driver from 'appium-uiautomator2-driver'; -import { createSession } from './session'; +import { createSession as createSessionMixin } from './session'; import { findElOrEls, click, @@ -158,10 +158,9 @@ export class AppiumFlutterDriver extends BaseDriver { * flutterServerPort need to be passed as lauch argument using appium:processArguments * Refer: https://appium.github.io/appium-xcuitest-driver/latest/reference/capabilities/ */ - attachAppLaunchArguments(caps, ...args); + attachAppLaunchArguments.bind(this)(caps, ...args); - let sessionCreated = await createSession.call( - this, + let sessionCreated = await createSessionMixin.bind(this)( sessionId, caps, ...JSON.parse(JSON.stringify(args)), @@ -204,23 +203,16 @@ export class AppiumFlutterDriver extends BaseDriver { portcallbacks.portForwardCallback = iosPortForward; portcallbacks.portReleaseCallback = iosRemovePortForward; } - const flutterCaps: DriverCaps = { - flutterServerLaunchTimeout: - this.internalCaps.flutterServerLaunchTimeout || 5000, - flutterSystemPort: isIosSimulator - ? this.internalCaps.flutterSystemPort - : this.internalCaps.flutterSystemPort || (await getFreePort()), - } as DriverCaps; - const systemPort = flutterCaps.flutterSystemPort!; + const systemPort = + this.internalCaps.flutterSystemPort ?? (await getFreePort()); const udid = this.proxydriver.opts.udid!; - this.flutterPort = await fetchFlutterServerPort({ + this.flutterPort = await fetchFlutterServerPort.bind(this)({ udid, packageName, ...portcallbacks, systemPort, - flutterCaps, isIosSimulator, }); @@ -345,10 +337,6 @@ export class AppiumFlutterDriver extends BaseDriver { } async activateApp(appId: string, bundleId: string) { - const flutterCaps: DriverCaps = { - flutterServerLaunchTimeout: - this.internalCaps?.flutterServerLaunchTimeout || 5000, - } as DriverCaps; let activateAppResponse; //run only for ios if ( @@ -365,11 +353,10 @@ export class AppiumFlutterDriver extends BaseDriver { await this.proxydriver.activateApp(appId || bundleId); } - await waitForFlutterServerToBeActive( + await waitForFlutterServerToBeActive.bind(this)( this.proxy, appId, this.flutterPort, - flutterCaps, ); await this.proxy?.command('/session', 'POST', { capabilities: this.proxydriver.originalCaps, diff --git a/src/iOS.ts b/src/iOS.ts index 24c8365..7edc60a 100644 --- a/src/iOS.ts +++ b/src/iOS.ts @@ -2,32 +2,23 @@ import { AppiumFlutterDriver } from './driver'; // @ts-ignore import XCUITestDriver from 'appium-xcuitest-driver'; import type { InitialOpts } from '@appium/types'; -import { log } from './logger'; import { DEVICE_CONNECTIONS_FACTORY } from './iProxy'; -const setupNewIOSDriver = async (...args: any[]): Promise => { +export async function startIOSSession( + this: AppiumFlutterDriver, + ...args: any[] +): Promise { + this.log.info(`Starting an IOS proxy session`); const iosdriver = new XCUITestDriver({} as InitialOpts); await iosdriver.createSession(...args); return iosdriver; -}; - -export const startIOSSession = async ( - flutterDriver: AppiumFlutterDriver, - caps: Record, - ...args: any[] -): Promise => { - log.info(`Starting an IOS proxy session`); - return await setupNewIOSDriver(...args); -}; +} export async function iosPortForward( udid: string, systemPort: number, devicePort: number, ) { - log.info( - `Forwarding port ${systemPort} to device port ${devicePort} ${udid}`, - ); await DEVICE_CONNECTIONS_FACTORY.requestConnection(udid, systemPort, { usePortForwarding: true, devicePort: devicePort, diff --git a/src/iProxy.ts b/src/iProxy.ts index 6ebc4d5..318955f 100644 --- a/src/iProxy.ts +++ b/src/iProxy.ts @@ -5,7 +5,7 @@ import { logger, util, timing } from '@appium/support'; import { utilities } from 'appium-ios-device'; import { checkPortStatus } from 'portscanner'; import { waitForCondition } from 'asyncbox'; -import { AppiumLogger } from '@appium/types'; +import type { AppiumLogger } from '@appium/types'; const LOCALHOST = '127.0.0.1'; diff --git a/src/session.ts b/src/session.ts index 61b1e06..2e85cc4 100644 --- a/src/session.ts +++ b/src/session.ts @@ -3,16 +3,18 @@ import _ from 'lodash'; import { PLATFORM } from './platform'; import { startAndroidSession } from './android'; import { startIOSSession } from './iOS'; -export const createSession: any = async function ( +import type { DefaultCreateSessionResult } from '@appium/types'; + +export async function createSession( this: AppiumFlutterDriver, sessionId: string, caps: any, ...args: any[] -) { +): Promise> { try { switch (_.toLower(caps.platformName)) { case PLATFORM.IOS: - this.proxydriver = await startIOSSession(this, caps, ...args); + this.proxydriver = await startIOSSession.bind(this)(...args); this.proxydriver.relaxedSecurityEnabled = this.relaxedSecurityEnabled; this.proxydriver.denyInsecure = this.denyInsecure; @@ -20,7 +22,7 @@ export const createSession: any = async function ( break; case PLATFORM.ANDROID: - this.proxydriver = await startAndroidSession(this, caps, ...args); + this.proxydriver = await startAndroidSession.bind(this)(...args); this.proxydriver.relaxedSecurityEnabled = this.relaxedSecurityEnabled; this.proxydriver.denyInsecure = this.denyInsecure; @@ -38,4 +40,4 @@ export const createSession: any = async function ( await this.deleteSession(); throw e; } -}; +} diff --git a/src/utils.ts b/src/utils.ts index a53a6b4..10c5b46 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,32 +1,25 @@ import AndroidUiautomator2Driver from 'appium-uiautomator2-driver'; import XCUITestDriver from 'appium-xcuitest-driver/build/lib/driver'; -import { log } from './logger'; import { findAPortNotInUse } from 'portscanner'; import { waitForCondition } from 'asyncbox'; import { JWProxy } from '@appium/base-driver'; -import { desiredCapConstraints } from './desiredCaps'; -import { DriverCaps } from '@appium/types'; import type { PortForwardCallback, PortReleaseCallback } from './types'; -import path from 'path'; +import type { AppiumFlutterDriver } from './driver'; import _ from 'lodash'; const DEVICE_PORT_RANGE = [9000, 9020]; const SYSTEM_PORT_RANGE = [10000, 11000]; -type FlutterDriverConstraints = typeof desiredCapConstraints; export async function getProxyDriver( + this: AppiumFlutterDriver, strategy: string, - proxy: any, - proxyDriver: any, -) { +): Promise { if (['key', 'semantics label', 'text', 'type'].includes(strategy)) { - return proxy; - } else if (proxyDriver instanceof AndroidUiautomator2Driver) { - return proxyDriver.uiautomator2.jwproxy; - } else if (proxyDriver instanceof XCUITestDriver) { - return proxyDriver.wda.jwproxy; - } else { - return 'NA'; + return this.proxy; + } else if (this.proxydriver instanceof AndroidUiautomator2Driver) { + return this.proxydriver.uiautomator2.jwproxy; + } else if (this.proxydriver instanceof XCUITestDriver) { + return this.proxydriver.wda.jwproxy; } } @@ -57,86 +50,89 @@ export function isFlutterDriverCommand(command: string) { ); } -export async function getFreePort() { +export async function getFreePort(): Promise { const [start, end] = SYSTEM_PORT_RANGE; return await findAPortNotInUse(start, end); } export async function waitForFlutterServerToBeActive( - proxy: JWProxy | undefined, + this: AppiumFlutterDriver, + proxy: JWProxy, packageName: string, - port: any, - flutterCaps: DriverCaps, -) { + flutterPort: number, +): Promise { await waitForCondition( async () => { try { - const response: any = await proxy?.command('/status', 'GET'); + const response: any = await proxy.command('/status', 'GET'); if (!response) { return false; } if (response?.appInfo?.packageName === packageName) { - log.info( + this.log.info( `Flutter server version the application is build with ${response.serverVersion}`, ); return true; } else { - log.error( + this.log.error( `Looking for flutter server with package ${packageName}. But found ${response.appInfo?.packageName}`, ); } } catch (err: any) { - log.info(`FlutterServer not reachable on port ${port}, Retrying..`); + this.log.info( + `FlutterServer not reachable on port ${flutterPort}, Retrying..`, + ); return false; } }, { - waitMs: flutterCaps.flutterServerLaunchTimeout, + waitMs: this.opts.flutterServerLaunchTimeout ?? 5000, intervalMs: 150, }, ); } export async function waitForFlutterServer( + this: AppiumFlutterDriver, port: number, packageName: string, - flutterCaps: DriverCaps, ) { const proxy = new JWProxy({ server: '127.0.0.1', port: port, }); - await waitForFlutterServerToBeActive(proxy, packageName, port, flutterCaps); + await waitForFlutterServerToBeActive.bind(this)(proxy, packageName, port); } -export async function fetchFlutterServerPort({ - udid, - systemPort, - portForwardCallback, - portReleaseCallback, - packageName, - flutterCaps, - isIosSimulator, -}: { - udid: string; - systemPort?: number | null; - portForwardCallback?: PortForwardCallback; - portReleaseCallback?: PortReleaseCallback; - packageName: string; - flutterCaps: DriverCaps; - isIosSimulator: boolean; -}): Promise { +export async function fetchFlutterServerPort( + this: AppiumFlutterDriver, + { + udid, + systemPort, + portForwardCallback, + portReleaseCallback, + packageName, + isIosSimulator, + }: { + udid: string; + systemPort?: number | null; + portForwardCallback?: PortForwardCallback; + portReleaseCallback?: PortReleaseCallback; + packageName: string; + isIosSimulator: boolean; + }, +): Promise { const [startPort, endPort] = DEVICE_PORT_RANGE as [number, number]; let devicePort = startPort; let forwardedPort = systemPort; if (isIosSimulator && systemPort) { try { - log.info( + this.log.info( `Checking if flutter server is running on port ${systemPort} for simulator with id ${udid}`, ); - await waitForFlutterServer(systemPort!, packageName, flutterCaps); - log.info( + await waitForFlutterServer.bind(this)(systemPort!, packageName); + this.log.info( `Flutter server is successfully running on port ${systemPort}`, ); return systemPort!; @@ -158,11 +154,11 @@ export async function fetchFlutterServerPort({ await portForwardCallback(udid, forwardedPort!, devicePort); } try { - log.info( + this.log.info( `Checking if flutter server is running on port ${devicePort}`, ); - await waitForFlutterServer(forwardedPort!, packageName, flutterCaps); - log.info( + await waitForFlutterServer.bind(this)(forwardedPort!, packageName); + this.log.info( `Flutter server is successfully running on port ${devicePort}`, ); return forwardedPort!; @@ -174,15 +170,11 @@ export async function fetchFlutterServerPort({ await portForwardCallback(udid, systemPort!, devicePort); } try { - log.info( + this.log.info( `Checking if flutter server is running on port ${devicePort}`, ); - await waitForFlutterServer( - forwardedPort!, - packageName, - flutterCaps, - ); - log.info( + await waitForFlutterServer.bind(this)(forwardedPort!, packageName); + this.log.info( `Flutter server is successfully running on port ${devicePort}`, ); return forwardedPort!; @@ -219,21 +211,19 @@ export function isW3cCaps(caps: any) { return false; } -export function attachAppLaunchArguments(parsedCaps: any, ...caps: any) { +export function attachAppLaunchArguments( + this: AppiumFlutterDriver, + parsedCaps: any, + ...caps: any +) { const capsToUpdate = [...caps].find(isW3cCaps); const platformName: string | undefined = parsedCaps['platformName']; const systemPort: string | undefined = parsedCaps['flutterSystemPort']; - console.log( - 'Caps to update:', - systemPort, - capsToUpdate, - `${capsToUpdate.alwaysMatch['appium:flutterSystemPort']}`, - ); if (platformName && systemPort && platformName.toLowerCase() == 'ios') { const args = [ `--port=${capsToUpdate.alwaysMatch['appium:flutterSystemPort']}`, ]; - log.info( + this.log.info( `iOS platform detected and flutterSystemPort capability is present. So attaching processArguments: ${JSON.stringify(args)}`, ); capsToUpdate.alwaysMatch['appium:processArguments'] = {