Skip to content

Commit

Permalink
fix: Varios typescript fixes (#620)
Browse files Browse the repository at this point in the history
  • Loading branch information
mykola-mokhnach authored Nov 10, 2023
1 parent 778d590 commit 333ef29
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 129 deletions.
2 changes: 1 addition & 1 deletion driver/lib/commands/clipboard.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FlutterDriver } from '../driver';
import type { FlutterDriver } from '../driver';

/**
* Set clipboard content via each native app driver
Expand Down
2 changes: 1 addition & 1 deletion driver/lib/commands/context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FlutterDriver } from '../driver';
import type { FlutterDriver } from '../driver';

export const FLUTTER_CONTEXT_NAME = `FLUTTER`;
export const NATIVE_CONTEXT_NAME = `NATIVE_APP`;
Expand Down
3 changes: 2 additions & 1 deletion driver/lib/commands/execute.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FlutterDriver } from '../driver';
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import type { FlutterDriver } from '../driver';
import { reConnectFlutterDriver } from '../sessions/session';
import { longTap, scroll, scrollIntoView, scrollUntilVisible, scrollUntilTapable } from './execute/scroll';
import { waitFor, waitForAbsent, waitForTappable } from './execute/wait';
Expand Down
2 changes: 1 addition & 1 deletion driver/lib/commands/gesture.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FlutterDriver } from '../driver';
import type { FlutterDriver } from '../driver';
import { longTap as longClick} from './execute/scroll';

export const click = async function(this: FlutterDriver, el: string) {
Expand Down
2 changes: 1 addition & 1 deletion driver/lib/commands/screen.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FlutterDriver } from '../driver';
import type { FlutterDriver } from '../driver';

export const getScreenshot = async function(this: FlutterDriver) {
const response = await this.socket!.call(`_flutter.screenshot`) as any;

Check warning on line 4 in driver/lib/commands/screen.ts

View workflow job for this annotation

GitHub Actions / build (20)

Forbidden non-null assertion

Check warning on line 4 in driver/lib/commands/screen.ts

View workflow job for this annotation

GitHub Actions / build (18)

Forbidden non-null assertion

Check warning on line 4 in driver/lib/commands/screen.ts

View workflow job for this annotation

GitHub Actions / build (16)

Forbidden non-null assertion
Expand Down
34 changes: 15 additions & 19 deletions driver/lib/driver.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
// @ts-ignore: no 'errors' export module
import { BaseDriver } from 'appium/driver';
import {
import type {
DefaultCreateSessionResult, DriverCaps, DriverData, W3CDriverCaps,
RouteMatcher
} from '@appium/types';
import { IsolateSocket } from './sessions/isolate_socket';

import type { IsolateSocket } from './sessions/isolate_socket';
import { log as logger } from './logger';

import { DRIVER_NAME as IOS_DEVICE_NAME } from './sessions/ios';
import { executeElementCommand, executeGetVMCommand,
executeGetIsolateCommand } from './sessions/observatory';
import {
executeElementCommand, executeGetVMCommand, executeGetIsolateCommand
} from './sessions/observatory';
import { createSession, deleteSession, reConnectFlutterDriver } from './sessions/session';

import { driverShouldDoProxyCmd, FLUTTER_CONTEXT_NAME,
getContexts, getCurrentContext, NATIVE_CONTEXT_NAME, setContext } from './commands/context';
import {
driverShouldDoProxyCmd, FLUTTER_CONTEXT_NAME,
getContexts, getCurrentContext, NATIVE_CONTEXT_NAME, setContext
} from './commands/context';
import { clear, getText, setValue } from './commands/element';
import { execute } from './commands/execute';
import { click, longTap, performTouch, tap, tapEl } from './commands/gesture';
Expand Down Expand Up @@ -45,7 +44,6 @@ class FlutterDriver extends BaseDriver<FluttertDriverConstraints> {
public socket: IsolateSocket | null;
public locatorStrategies = [`key`, `css selector`];
public proxydriver: XCUITestDriver | AndroidUiautomator2Driver;
public proxydriverName: string; // to store 'driver name' as proxy to.
public device: any;

// Used to keep the capabilities internally
Expand Down Expand Up @@ -91,7 +89,6 @@ class FlutterDriver extends BaseDriver<FluttertDriverConstraints> {
constructor(opts, shouldValidateCaps: boolean) {
super(opts, shouldValidateCaps);
this.socket = null;
this.proxydriverName = ``;
this.device = null;
this.desiredCapConstraints = desiredCapConstraints;
}
Expand All @@ -103,10 +100,9 @@ class FlutterDriver extends BaseDriver<FluttertDriverConstraints> {
}

public async deleteSession() {
await Promise.all([
deleteSession.bind(this)(),
super.deleteSession(),
]);
this.log.debug(`Deleting Flutter Driver session`);
await deleteSession.bind(this);
await super.deleteSession();
}

public async installApp(appPath: string, opts = {}) {
Expand Down Expand Up @@ -174,7 +170,7 @@ class FlutterDriver extends BaseDriver<FluttertDriverConstraints> {
// There are 2 CommandTimeout (FlutterDriver and proxy)
// Only FlutterDriver CommandTimeout is used; Proxy is disabled
// All proxy commands needs to reset the FlutterDriver CommandTimeout
// Here we manually reset the FlutterDriver CommandTimeout for commands that goe to proxy.
// Here we manually reset the FlutterDriver CommandTimeout for commands that goes to proxy.
this.clearNewCommandTimeout();
const result = await this.proxydriver.executeCommand(cmd, ...args);
this.startNewCommandTimeout();
Expand All @@ -197,10 +193,10 @@ class FlutterDriver extends BaseDriver<FluttertDriverConstraints> {
public proxyActive(): boolean {
// In WebView context, all request should got to each driver
// so that they can handle http request properly.
// On iOS, WebVie context is handled by XCUITest driver while Android is by chromedriver.
// On iOS, WebView context is handled by XCUITest driver while Android is by chromedriver.
// It means XCUITest driver should keep the XCUITest driver as a proxy,
// while UIAutomator2 driver should proxy to chromedriver instead of UIA2 proxy.
return this.proxyWebViewActive && this.proxydriverName !== IOS_DEVICE_NAME;
return this.proxyWebViewActive && this.proxydriver.constructor.name !== XCUITestDriver.name;
}

public canProxy(): boolean {
Expand Down
14 changes: 5 additions & 9 deletions driver/lib/sessions/android.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import AndroidUiautomator2Driver from 'appium-uiautomator2-driver';
import { log } from '../logger';
import { connectSocket, processLogToGetobservatory } from './observatory';
import { InitialOpts } from '@appium/types';

export const DRIVER_NAME = `UIAutomator2`;
type IsolateSocket = import('./isolate_socket').IsolateSocket;

import { connectSocket, fetchObservatoryUrl } from './observatory';
import type { InitialOpts } from '@appium/types';
import type { IsolateSocket } from './isolate_socket';

const setupNewAndroidDriver = async (...args: any[]): Promise<AndroidUiautomator2Driver> => {
const androiddriver = new AndroidUiautomator2Driver({} as InitialOpts);
// @ts-ignore
//@ts-ignore Args are ok
await androiddriver.createSession(...args);

return androiddriver;
};

Expand Down Expand Up @@ -48,7 +44,7 @@ export const getObservatoryWsUri = async (proxydriver: AndroidUiautomator2Driver
return urlObject.toJSON();
}
} else {
urlObject = processLogToGetobservatory(proxydriver.adb.logcat!.logs as [{message: string}]);
urlObject = fetchObservatoryUrl(proxydriver.adb.logcat!.logs as [{message: string}]);

Check warning on line 47 in driver/lib/sessions/android.ts

View workflow job for this annotation

GitHub Actions / build (20)

Forbidden non-null assertion

Check warning on line 47 in driver/lib/sessions/android.ts

View workflow job for this annotation

GitHub Actions / build (18)

Forbidden non-null assertion

Check warning on line 47 in driver/lib/sessions/android.ts

View workflow job for this annotation

GitHub Actions / build (16)

Forbidden non-null assertion
}
const remotePort = urlObject.port;
const localPort = caps.forwardingPort ?? remotePort;
Expand Down
15 changes: 9 additions & 6 deletions driver/lib/sessions/base64url.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
const W3C_ELEMENT = `element-6066-11e4-a52e-4f735466cecf`;
import _ from 'lodash';
import { util } from '@appium/support';

export const decode = (input: string | {ELEMENT: string}): string => {
let base64String = ``;
if (typeof input === `string`) {
if (_.isString(input)) {
base64String = input;
} else if (typeof input === `object` && input[W3C_ELEMENT]) {
base64String = input[W3C_ELEMENT];
} else if (typeof input === `object` && input.ELEMENT) {
} else if (_.has(input, util.W3C_WEB_ELEMENT_IDENTIFIER)) {
base64String = input[util.W3C_WEB_ELEMENT_IDENTIFIER];
} else if (_.has(input, 'ELEMENT')) {
base64String = input.ELEMENT;
} else {
throw new Error(`input is invalid ${JSON.stringify(input)}`);
throw new Error(
`Input is is expceted to be a base64-encoded string or a valid element object. ` +
`${JSON.stringify(input)} has been provided instead`);
}
return Buffer.from(base64String, `base64`).toString();
};
61 changes: 15 additions & 46 deletions driver/lib/sessions/ios.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import { utilities } from 'appium-ios-device';
import { timing } from '@appium/support';
import XCUITestDriver from 'appium-xcuitest-driver';
import { waitForCondition } from 'asyncbox';
import B from 'bluebird';
import net from 'net';
import { checkPortStatus } from 'portscanner';
import { log } from '../logger';
import { connectSocket, processLogToGetobservatory } from './observatory';
import { InitialOpts } from '@appium/types';
import { connectSocket, fetchObservatoryUrl } from './observatory';
import type { InitialOpts } from '@appium/types';
import type { IsolateSocket } from './isolate_socket';

const LOCALHOST = `127.0.0.1`;
const PORT_CLOSE_TIMEOUT = 15 * 1000; // 15 seconds
export const DRIVER_NAME = `XCUITest`;
type IsolateSocket = import('./isolate_socket').IsolateSocket;


const setupNewIOSDriver = async (...args: any[]): Promise<XCUITestDriver> => {
const iosdriver = new XCUITestDriver({} as InitialOpts);
await iosdriver.createSession(...args);

return iosdriver;
};

Expand All @@ -33,50 +27,25 @@ export const startIOSSession = async (
return [iosdriver, null];
}

return Promise.all([
return [
iosdriver,
connectSocket(getObservatoryWsUri, iosdriver, caps),
]);
await connectSocket(getObservatoryWsUri, iosdriver, caps),
];
};

export const connectIOSSession = async (
iosdriver: XCUITestDriver, caps: Record<string, any>
): Promise<IsolateSocket> =>
await connectSocket(getObservatoryWsUri, iosdriver, caps);

const waitForPortIsAvailable = async (port) => {
let isPortBusy = (await checkPortStatus(port, LOCALHOST)) === `open`;
if (isPortBusy) {
log.warn(`Port #${port} is busy. Did you quit the previous driver session(s) properly?`);
const timer = new timing.Timer().start();
try {
await waitForCondition(async () => {
try {
if ((await checkPortStatus(port, LOCALHOST)) !== `open`) {
log.info(`Port #${port} has been successfully released after ` +
`${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
isPortBusy = false;
return true;
}
} catch (ign) {
log.warn(``);
}
return false;
}, {
intervalMs: 300,
waitMs: PORT_CLOSE_TIMEOUT,
});
} catch (ign) {
log.warn(`Did not know how to release port #${port} in ` +
`${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
}
async function requireFreePort (port: number) {
if ((await checkPortStatus(port, LOCALHOST)) !== `open`) {
return;
}

if (isPortBusy) {
throw new Error(`The port :${port} is occupied by an other process. ` +
`You can either quit that process or select another free port.`);
}
};
log.warn(`Port #${port} is busy. Did you quit the previous driver session(s) properly?`);
throw new Error(`The port :${port} is occupied by an other process. ` +
`You can either quit that process or select another free port.`);
}

export const getObservatoryWsUri = async (
proxydriver: XCUITestDriver, caps: Record<string, any>
Expand All @@ -91,7 +60,7 @@ export const getObservatoryWsUri = async (
return urlObject.toJSON();
}
} else {
urlObject = processLogToGetobservatory(proxydriver.logs.syslog.logs);
urlObject = fetchObservatoryUrl(proxydriver.logs.syslog.logs);
}
if (!proxydriver.isRealDevice()) {
log.info(`Running on iOS simulator`);
Expand All @@ -104,7 +73,7 @@ export const getObservatoryWsUri = async (

log.info(`Running on iOS real device`);
const { udid } = proxydriver.opts;
await waitForPortIsAvailable(localPort);
await requireFreePort(localPort);
const localServer = net.createServer(async (localSocket) => {
let remoteSocket;
try {
Expand Down
8 changes: 5 additions & 3 deletions driver/lib/sessions/observatory.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { URL } from 'url';
import _ from 'lodash';
import { FlutterDriver } from '../driver';
import type { FlutterDriver } from '../driver';
import { log } from '../logger';
import { IsolateSocket } from './isolate_socket';
import { decode } from './base64url';
import B from 'bluebird';
import type XCUITestDriver from 'appium-xcuitest-driver';
import type AndroidUiautomator2Driver from 'appium-uiautomator2-driver';

const truncateLength = 500;
// https://github.com/flutter/flutter/blob/f90b019c68edf4541a4c8273865a2b40c2c01eb3/dev/devicelab/lib/framework/runner.dart#L183
Expand All @@ -17,7 +19,7 @@ const OBSERVATORY_URL_PATTERN = new RegExp(
`The Dart VM service is listening on )` +
`((http|//)[a-zA-Z0-9:/=_\\-.\\[\\]]+)`,
);
type AnyDriver = import('appium-xcuitest-driver').XCUITestDriver | import('appium-uiautomator2-driver').AndroidUiautomator2Driver;
type AnyDriver = XCUITestDriver | AndroidUiautomator2Driver;

// SOCKETS
export const connectSocket = async (
Expand Down Expand Up @@ -201,7 +203,7 @@ export const executeElementCommand = async function(
return data.response;
};

export const processLogToGetobservatory = (deviceLogs: [{ message: string }]): URL => {
export const fetchObservatoryUrl = (deviceLogs: [{ message: string }]): URL => {
let dartObservatoryURL: URL|undefined;
for (const line of deviceLogs.map((e) => e.message).reverse()) {
const match = line.match(OBSERVATORY_URL_PATTERN);
Expand Down
Loading

0 comments on commit 333ef29

Please sign in to comment.