🏡 index : quine-global/webdriverio.git

import type { ElementReference } from '@wdio/protocols'
import type { BaseActionParams, KeyActionType } from './base.js'
import BaseAction from './base.js'
import type { ChainablePromiseElement } from '../../types.js'

const buttonNumbers = [0, 1, 2] as const
const buttonNames = ['left', 'middle', 'right'] as const
export const buttonValue = [...buttonNumbers, ...buttonNames] as const
export type ButtonNames = 'left' | 'middle' | 'right'
export type Button = 0 | 1 | 2
export type Origin = 'pointer' | 'viewport'

const ORIGIN_DEFAULT: Origin = 'viewport'
const BUTTON_DEFAULT: Button = 0
const POINTER_TYPE_DEFAULT: KeyActionType = 'mouse'

interface PointerActionUpParams {
    /**
     * The button to press (e.g. 0 for left, 1 for middle or 2 for right)
     * @default 0
     */
    button: Button | ButtonNames
}
const UP_PARAM_DEFAULTS = {
    button: BUTTON_DEFAULT
} as PointerActionUpParams

const PARAM_DEFAULTS = {
    ...UP_PARAM_DEFAULTS,
    width: 0,
    height: 0,
    pressure: 0,
    tangentialPressure: 0,
    tiltX: 0,
    tiltY: 0,
    twist: 0,
    altitudeAngle: 0,
    azimuthAngle: 0
}
const MOVE_PARAM_DEFAULTS = {
    x: 0,
    y: 0,
    duration: 100,
    origin: ORIGIN_DEFAULT as (Origin | ElementReference | ChainablePromiseElement | WebdriverIO.Element)
}

type PointerActionParams = Partial<typeof PARAM_DEFAULTS> & Partial<PointerActionUpParams>
type PointerActionMoveParams = Partial<typeof MOVE_PARAM_DEFAULTS> & PointerActionParams

function removeDefaultParams(seq: Record<string, unknown>) {
    for (const [key, value] of Object.entries(seq)) {
        if (value === 0 && !['x', 'y', 'button', 'duration'].includes(key)) {
            delete seq[key]
        }
    }
}

function mapButton(params: PointerActionParams | ButtonNames | Button) {
    const buttons = {
        left: 0,
        middle: 1,
        right: 2
    }
    if (typeof params === 'number') {
        return { button: params }
    }
    if (typeof params === 'string') {
        return { button: buttons[params] }
    }
    if (typeof params === 'object' && typeof params.button === 'string') {
        return { ...params, button: buttons[params.button] }
    }
    return params
}

export default class PointerAction extends BaseAction {
    constructor (instance: WebdriverIO.Browser, params: BaseActionParams = {}) {
        if (!params.parameters) {
            params.parameters = { pointerType: POINTER_TYPE_DEFAULT }
        }

        super(instance, 'pointer', params)
    }

    /**
     * Creates an action for moving the pointer `x` and `y` pixels from the specified
     * `origin`. The `origin` may be defined as the pointers current position (e.g. "pointer"),
     * the viewport (e.g. "viewport") or the center of a specific element.
     * @param params PointerActionMoveParams
     */
    move (params: PointerActionMoveParams): PointerAction
    move (x: number, y: number): PointerAction
    move (params: PointerActionMoveParams | number = {}, y?: number) {
        const seq = {
            type: 'pointerMove',
            // default params
            ...PARAM_DEFAULTS,
            ...UP_PARAM_DEFAULTS,
            ...MOVE_PARAM_DEFAULTS,
        }

        if (typeof params === 'number') {
            Object.assign(seq, { x: params, y })
        } else if (params) {
            Object.assign(seq, params)
        }
        removeDefaultParams(seq)

        this.sequence.push(seq)
        return this
    }

    /**
     * Creates an action to release a single key.
     * @param params PointerActionUpParams
     */
    up (button?: Button): PointerAction
    up (button?: ButtonNames): PointerAction
    up (params?: PointerActionUpParams): PointerAction
    up (params: PointerActionUpParams | ButtonNames | Button = UP_PARAM_DEFAULTS) {
        this.sequence.push({
            type: 'pointerUp',
            ...mapButton(params)
        })
        return this
    }

    /**
     * Creates an action to press a single key
     * @param params PointerActionParams
     */
    down (button?: Button): PointerAction
    down (button?: ButtonNames): PointerAction
    down (params?: PointerActionParams): PointerAction
    down (params: PointerActionParams | Button | ButtonNames = {}) {
        const seq = {
            type: 'pointerDown',
            ...PARAM_DEFAULTS,
            ...mapButton(params),
        }

        removeDefaultParams(seq)

        this.sequence.push(seq)
        return this
    }

    /**
     * An action that cancels this pointer's current input.
     */
    cancel () {
        this.sequence.push({ type: 'pointerCancel' })
        return this
    }
}