import { ELEMENT_KEY } from 'webdriver' import type { ElementReference } from '@wdio/protocols' export type ActionType = 'key' | 'pointer' | 'wheel' export type KeyActionType = 'mouse' | 'pen' | 'touch' export interface ActionParameters { pointerType?: KeyActionType } export interface BaseActionParams { id?: string, parameters?: ActionParameters } interface Sequence { type: string duration?: number origin?: (ElementReference | WebdriverIO.Element | 'pointer' | 'viewport') & { then?: Function } value?: string } let actionIds = 0 export default class BaseAction { #id: string #type: ActionType #parameters: ActionParameters #instance: WebdriverIO.Browser protected sequence: Sequence[] = [] constructor ( protected instance: WebdriverIO.Browser, type: ActionType, params?: BaseActionParams ) { this.#instance = instance this.#id = params?.id || `action${++actionIds}` this.#type = type this.#parameters = params?.parameters || {} } toJSON () { return { id: this.#id, type: this.#type, parameters: this.#parameters, actions: this.sequence } } /** * Inserts a pause action for the specified device, ensuring it idles for a tick. * @param duration idle time of tick */ pause (duration: number) { this.sequence.push({ type: 'pause', duration }) return this } /** * Perform action sequence * @param skipRelease set to true if `releaseActions` command should not be invoked */ async perform (skipRelease = false) { /** * transform chainable / not resolved elements into WDIO elements */ for (const seq of this.sequence) { /** * continue if we don't deal with origin or elements within * origin at all */ if (!seq.origin || typeof seq.origin === 'string') { continue } /** * resolve promise element */ if (typeof seq.origin.then === 'function') { await (seq.origin as WebdriverIO.Element).waitForExist() seq.origin = await seq.origin } if (!seq.origin[ELEMENT_KEY]) { throw new Error(`Couldn't find element for "${seq.type}" action sequence`) } seq.origin = { [ELEMENT_KEY]: seq.origin[ELEMENT_KEY] } } await this.#instance.performActions([this.toJSON()]) if (!skipRelease) { await this.#instance.releaseActions() } } }