UNPKG

appium-flutter-driver

Version:
181 lines (169 loc) 7.21 kB
/* 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'; import {assertVisible, assertNotVisible, assertTappable, type FinderInput} from './assertions'; import {launchApp} from './../ios/app'; import B from 'bluebird'; const flutterCommandRegex = /^[\s]*flutter[\s]*:(.+)/; // Define types for better type safety type CommandHandler = (driver: FlutterDriver, ...args: any[]) => Promise<any>; type CommandMap = Record<string, CommandHandler>; interface DragAndDropParams { startX: string; startY: string; endX: string; endY: string; duration: string; } interface DiagnosticsOptions { subtreeDepth?: number; includeProperties?: boolean; } interface LongTapOptions { durationMilliseconds: number; frequency?: number; } interface OffsetOptions { offsetType: 'bottomLeft' | 'bottomRight' | 'center' | 'topLeft' | 'topRight'; } // Extract command handlers into a separate object for better organization const commandHandlers: CommandMap = { launchApp: async (driver, appId: string, opts = {}) => { const {arguments: args = [], environment: env = {}} = opts; await launchApp(driver.internalCaps.udid!, appId, args, env); await reConnectFlutterDriver.bind(driver)(driver.internalCaps); }, connectObservatoryWsUrl: async (driver) => { await reConnectFlutterDriver.bind(driver)(driver.internalCaps); }, checkHealth: async (driver) => (await driver.executeElementCommand('get_health')).status, getVMInfo: async (driver) => await driver.executeGetVMCommand(), getRenderTree: async (driver) => (await driver.executeElementCommand('get_render_tree')).tree, getOffset: async (driver, elementBase64: string, options: OffsetOptions) => await driver.executeElementCommand('get_offset', elementBase64, options), waitForCondition: async (driver, conditionName: string) => await driver.executeElementCommand('waitForCondition', '', { conditionName, }), forceGC: async (driver) => { const response = (await driver.socket!.call('_collectAllGarbage', { isolateId: driver.socket!.isolateId, })) as {type: string}; if (response.type !== 'Success') { throw new Error(`Could not forceGC, response was ${JSON.stringify(response)}`); } }, setIsolateId: async (driver, isolateId: string) => { driver.socket!.isolateId = isolateId; return await driver.socket!.call('getIsolate', {isolateId}); }, getIsolate: async (driver, isolateId?: string) => await driver.executeGetIsolateCommand(isolateId || driver.socket!.isolateId!), clearTimeline: async (driver) => { const call1 = driver.socket!.call('_clearVMTimeline'); const call2 = driver.socket!.call('clearVMTimeline'); const response = await B.any([call1, call2]); if (response.type !== 'Success') { throw new Error(`Could not clear timeline, response was ${JSON.stringify(response)}`); } }, getRenderObjectDiagnostics: async ( driver, elementBase64: string, opts: DiagnosticsOptions = {}, ) => { const {subtreeDepth = 0, includeProperties = true} = opts; return await driver.executeElementCommand('get_diagnostics_tree', elementBase64, { diagnosticsType: 'renderObject', includeProperties, subtreeDepth, }); }, getWidgetDiagnostics: async (driver, elementBase64: string, opts: DiagnosticsOptions = {}) => { const {subtreeDepth = 0, includeProperties = true} = opts; return await driver.executeElementCommand('get_diagnostics_tree', elementBase64, { diagnosticsType: 'widget', includeProperties, subtreeDepth, }); }, getSemanticsId: async (driver, elementBase64: string) => (await driver.executeElementCommand('get_semantics_id', elementBase64)).id, waitForAbsent: async (driver, finder: string, timeout?: number) => await waitForAbsent(driver, finder, timeout), waitFor: async (driver, finder: string, timeout?: number) => await waitFor(driver, finder, timeout), waitForTappable: async (driver, finder: string, timeout?: number) => await waitForTappable(driver, finder, timeout), scroll: async (driver, finder: string, opts: any) => await scroll(driver, finder, opts), scrollUntilVisible: async (driver, finder: string, opts: any) => await scrollUntilVisible(driver, finder, opts), scrollUntilTapable: async (driver, finder: string, opts: any) => await scrollUntilTapable(driver, finder, opts), scrollIntoView: async (driver, finder: string, opts: any) => await scrollIntoView(driver, finder, opts), setTextEntryEmulation: async (driver, enabled: boolean) => await driver.socket!.executeSocketCommand({ command: 'set_text_entry_emulation', enabled, }), enterText: async (driver, text: string) => await driver.socket!.executeSocketCommand({command: 'enter_text', text}), requestData: async (driver, message: string) => await driver.socket!.executeSocketCommand({ command: 'request_data', message, }), longTap: async (driver, finder: string, durationOrOptions: LongTapOptions) => await longTap(driver, finder, durationOrOptions), waitForFirstFrame: async (driver) => await driver.executeElementCommand('waitForCondition', '', { conditionName: 'FirstFrameRasterizedCondition', }), setFrameSync: async (driver, enabled: boolean, durationMilliseconds: number) => await driver.socket!.executeSocketCommand({ command: 'set_frame_sync', enabled, timeout: durationMilliseconds, }), clickElement: async (driver, finder: string, opts: {timeout?: number} = {}) => { const {timeout = 1000} = opts; return await driver.executeElementCommand('tap', finder, {timeout}); }, dragAndDropWithCommandExtension: async (driver, params: DragAndDropParams) => await driver.socket!.executeSocketCommand({ command: 'dragAndDropWithCommandExtension', ...params, }), assertVisible: async (driver, input: FinderInput, timeout = 5000) => await assertVisible(driver, input, timeout), assertNotVisible: async (driver, input: FinderInput, timeout = 5000) => await assertNotVisible(driver, input, timeout), assertTappable: async (driver, input: FinderInput, timeout = 5000) => await assertTappable(driver, input, timeout), getTextWithCommandExtension: async (driver, params: {findBy: string}) => await driver.socket!.executeSocketCommand({ command: 'getTextWithCommandExtension', findBy: params.findBy, }), }; export const execute = async function (this: FlutterDriver, rawCommand: string, args: any[]) { const matching = rawCommand.match(flutterCommandRegex); if (!matching) { throw new Error(`Command not supported: "${rawCommand}"`); } const command = matching[1].trim(); const handler = commandHandlers[command]; if (!handler) { throw new Error(`Command not supported: "${rawCommand}"`); } return await handler(this, ...args); };