appium-flutter-driver
Version:
Appium Flutter driver
246 lines • 10.7 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FlutterDriver = void 0;
// @ts-ignore: no 'errors' export module
const lodash_1 = __importDefault(require("lodash"));
const driver_1 = require("appium/driver");
const logger_1 = require("./logger");
const observatory_1 = require("./sessions/observatory");
const platform_1 = require("./platform");
const session_1 = require("./sessions/session");
const context_1 = require("./commands/context");
const element_1 = require("./commands/element");
const execute_1 = require("./commands/execute");
const gesture_1 = require("./commands/gesture");
const screen_1 = require("./commands/screen");
const clipboard_1 = require("./commands/clipboard");
const desired_caps_1 = require("./desired-caps");
const appium_xcuitest_driver_1 = require("appium-xcuitest-driver");
// Need to not proxy in WebView context
const WEBVIEW_NO_PROXY = [
[`GET`, new RegExp(`^/session/[^/]+/appium`)],
[`GET`, new RegExp(`^/session/[^/]+/context`)],
[`GET`, new RegExp(`^/session/[^/]+/element/[^/]+/rect`)],
[`GET`, new RegExp(`^/session/[^/]+/log/types$`)],
[`GET`, new RegExp(`^/session/[^/]+/orientation`)],
[`POST`, new RegExp(`^/session/[^/]+/appium`)],
[`POST`, new RegExp(`^/session/[^/]+/context`)],
[`POST`, new RegExp(`^/session/[^/]+/log$`)],
[`POST`, new RegExp(`^/session/[^/]+/orientation`)],
[`POST`, new RegExp(`^/session/[^/]+/touch/multi/perform`)],
[`POST`, new RegExp(`^/session/[^/]+/touch/perform`)],
];
class FlutterDriver extends driver_1.BaseDriver {
socket;
locatorStrategies = [`key`, `css selector`];
proxydriver;
device;
portForwardLocalPort;
localServer;
_logmon;
// Used to keep the capabilities internally
internalCaps;
receiveAsyncResponse;
// to handle WebView context
proxyWebViewActive = false;
// session
executeElementCommand = observatory_1.executeElementCommand;
executeGetVMCommand = observatory_1.executeGetVMCommand;
executeGetIsolateCommand = observatory_1.executeGetIsolateCommand;
execute = execute_1.execute;
executeAsync = execute_1.execute;
// element
getText = element_1.getText;
setValue = element_1.setValue;
clear = element_1.clear;
getScreenshot = screen_1.getScreenshot;
// gesture
click = gesture_1.click;
longTap = gesture_1.longTap;
tapEl = gesture_1.tapEl;
tap = gesture_1.tap;
performTouch = gesture_1.performTouch;
// context
getContexts = context_1.getContexts;
getCurrentContext = context_1.getCurrentContext;
setContext = context_1.setContext;
currentContext = context_1.FLUTTER_CONTEXT_NAME;
driverShouldDoProxyCmd = context_1.driverShouldDoProxyCmd;
// content
getClipboard = clipboard_1.getClipboard;
setClipboard = clipboard_1.setClipboard;
constructor(opts, shouldValidateCaps) {
super(opts, shouldValidateCaps);
this.socket = null;
this.device = null;
this._logmon = null;
this.desiredCapConstraints = desired_caps_1.desiredCapConstraints;
// Used to keep the port for port forward to clear the pair.
this.portForwardLocalPort = null;
// Used for iOS to end the local server to proxy the request.
this.localServer = null;
}
async createSession(...args) {
const [sessionId, caps] = await super.createSession(...JSON.parse(JSON.stringify(args)));
this.internalCaps = caps;
return session_1.createSession.bind(this)(sessionId, caps, ...JSON.parse(JSON.stringify(args)));
}
async deleteSession() {
this.log.info(`Deleting Flutter Driver session`);
this._logmon?.stop();
this._logmon = null;
this.proxydriver?.eventEmitter?.removeAllListeners('syslogStarted');
this.log.info('Cleanup the port forward');
switch (lodash_1.default.toLower(this.internalCaps.platformName)) {
case platform_1.PLATFORM.IOS:
this.localServer?.close();
this.localServer = null;
break;
case platform_1.PLATFORM.ANDROID:
if (this.portForwardLocalPort) {
if (this.proxydriver) {
await this.proxydriver.adb?.removePortForward(this.portForwardLocalPort);
}
}
break;
}
if (this.proxydriver) {
this.log.info('Deleting the proxy driver session.');
try {
await this.proxydriver.deleteSession(this.sessionId || undefined);
}
catch (e) {
this.log.warn(e.message);
}
this.proxydriver = null;
}
await super.deleteSession();
}
async installApp(appPath, opts = {}) {
// @ts-expect-error this exist in xcuitestdriver or uia2 driver
this.proxydriver?.installApp(appPath, opts);
}
async activateApp(appId) {
// @ts-expect-error this exist in xcuitestdriver or uia2 driver
this.proxydriver?.activateApp(appId);
await session_1.reConnectFlutterDriver.bind(this)(this.internalCaps);
}
async terminateApp(appId) {
// @ts-expect-error this exist in xcuitestdriver or uia2 driver
return await this.proxydriver?.terminateApp(appId);
}
async back() {
// @ts-expect-error this exist in xcuitestdriver or uia2 driver
return await this.proxydriver?.back();
}
async getOrientation() {
if (!this.proxydriver) {
return null;
}
switch (lodash_1.default.toLower(this.internalCaps.platformName)) {
case platform_1.PLATFORM.IOS:
return await this.proxydriver.proxyCommand('/orientation', 'GET');
default:
return await this.proxydriver.getOrientation();
}
}
async setOrientation(orientation) {
switch (lodash_1.default.toLower(this.internalCaps.platformName)) {
case platform_1.PLATFORM.IOS:
return await this.proxydriver.proxyCommand('/orientation', 'POST', {
orientation,
});
default:
return await this.proxydriver.setOrientation(orientation);
}
}
validateLocatorStrategy(strategy) {
// @todo refactor DRY
if (this.currentContext === `NATIVE_APP`) {
return this.proxydriver?.validateLocatorStrategy(strategy);
}
super.validateLocatorStrategy(strategy, false);
}
validateDesiredCaps(caps) {
// check with the base class, and return if it fails
const res = super.validateDesiredCaps(caps);
if (!res) {
return res;
}
// finally, return true since the superclass check passed, as did this
return true;
}
async proxyCommand(url, method, body = null) {
// @ts-expect-error this exist in xcuitestdriver or uia2 driver
const result = await this.proxydriver?.proxyCommand(url, method, body);
return result;
}
async executeCommand(cmd, ...args) {
if (new RegExp(/^[\s]*mobile:[\s]*activateApp$/).test(args[0])) {
const { skipAttachObservatoryUrl = false } = args[1][0];
await this.proxydriver?.executeCommand(cmd, ...args);
if (skipAttachObservatoryUrl) {
return;
}
await session_1.reConnectFlutterDriver.bind(this)(this.internalCaps);
return;
}
else if (new RegExp(/^[\s]*mobile:[\s]*terminateApp$/).test(args[0])) {
// to make the behavior as same as this.terminateApp
return await this.proxydriver?.executeCommand(cmd, ...args);
}
else if (cmd === `receiveAsyncResponse`) {
logger_1.log.debug(`Executing FlutterDriver response '${cmd}'`);
return await this.receiveAsyncResponse(...args);
}
else if ([`setOrientation`, `getOrientation`, `back`].includes(cmd)) {
// The `setOrientation` and `getOrientation` commands are handled differently
// for iOS and Android platforms. These commands are deferred to the base driver's
// implementation (`super.executeCommand`) to ensure compatibility with both platforms
// and to leverage the platform-specific logic already implemented in the base driver.
logger_1.log.debug(`Executing FlutterDriver command '${cmd}'`);
return await super.executeCommand(cmd, ...args);
}
else {
if (this.driverShouldDoProxyCmd(cmd)) {
logger_1.log.debug(`Executing proxied driver command '${cmd}'`);
// 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 goes to proxy.
this.clearNewCommandTimeout();
const result = await this.proxydriver?.executeCommand(cmd, ...args);
this.startNewCommandTimeout();
return result;
}
else {
logger_1.log.debug(`Executing Flutter driver command '${cmd}'`);
return await super.executeCommand(cmd, ...args);
}
}
}
getProxyAvoidList() {
if ([context_1.FLUTTER_CONTEXT_NAME, context_1.NATIVE_CONTEXT_NAME].includes(this.currentContext)) {
return [];
}
return WEBVIEW_NO_PROXY;
}
proxyActive() {
// In WebView context, all request should got to each driver
// so that they can handle http request properly.
// 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.proxydriver?.constructor.name !== appium_xcuitest_driver_1.XCUITestDriver.name;
}
canProxy() {
// As same as proxyActive, all request should got to each driver
// so that they can handle http request properly
return this.proxyWebViewActive;
}
}
exports.FlutterDriver = FlutterDriver;
//# sourceMappingURL=driver.js.map