🏡 index : quine-global/webdriverio.git

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()
        }
    }
}