packages feed

marionette-1.1.0: src/Test/Marionette/Actions.hs

{-# LANGUAGE DuplicateRecordFields #-}
{-# OPTIONS_GHC -Wno-partial-fields #-}

-- | The <W3C https://w3c.github.io/webdriver/#actions Actions> API.
module Test.Marionette.Actions where

import Data.Aeson (ToJSON (toJSON), Value (String), object, (.=))
import Data.Text (Text)
import Data.Text qualified as Text
import Test.Marionette.Element (Element)
import Test.Marionette.Key (Key)
import Prelude

-- | Where a pointer or wheel action is anchored.
data Origin
    = -- | The top-left of the viewport.
      Viewport
    | -- | The input source's current position.
      Pointer
    | -- | The centre of the given element.
      FromElement Element
    deriving stock (Show, Eq)

instance ToJSON Origin where
    toJSON Viewport = String "viewport"
    toJSON Pointer = String "pointer"
    toJSON (FromElement element) = toJSON element

-- | Which mouse button an action addresses.
data Button
    = LeftButton
    | MiddleButton
    | RightButton
    | BackButton
    | ForwardButton
    deriving stock (Show, Eq, Enum, Bounded)

-- | The kind of physical device a pointer input source represents.
data PointerType = Mouse | Pen | Touch
    deriving stock (Show, Eq)

instance ToJSON PointerType where
    toJSON = String . Text.toLower . Text.show

-- | One tick of a pointer input source's action list.
data PointerAction
    = -- | Do nothing for this many milliseconds, or until every other
      -- source's action in this tick has finished, whichever is longer.
      PointerPause {durationMs :: Int}
    | -- | Move to (or by, relative to the 'Origin') the given position over
      -- the given number of milliseconds.
      PointerMove {durationMs :: Int, origin :: Origin, x :: Int, y :: Int}
    | PointerDown Button
    | PointerUp Button
    deriving stock (Show, Eq)

instance ToJSON PointerAction where
    toJSON PointerPause{..} =
        object
            [ "type" .= String "pause"
            , "duration" .= durationMs
            ]
    toJSON PointerMove{..} =
        object
            [ "type" .= String "pointerMove"
            , "duration" .= durationMs
            , "origin" .= origin
            , "x" .= x
            , "y" .= y
            ]
    toJSON (PointerDown button) =
        object
            [ "type" .= String "pointerDown"
            , "button" .= fromEnum button
            ]
    toJSON (PointerUp button) =
        object
            [ "type" .= String "pointerUp"
            , "button" .= fromEnum button
            ]

-- | One tick of a key input source's action list.
data KeyAction
    = KeyPause {durationMs :: Int}
    | KeyDown Key
    | KeyUp Key
    deriving stock (Show, Eq)

instance ToJSON KeyAction where
    toJSON KeyPause{..} =
        object
            [ "type" .= String "pause"
            , "duration" .= durationMs
            ]
    toJSON (KeyDown key) =
        object
            [ "type" .= String "keyDown"
            , "value" .= key
            ]
    toJSON (KeyUp key) =
        object
            [ "type" .= String "keyUp"
            , "value" .= key
            ]

-- | One tick of a wheel input source's action list.
data WheelAction
    = WheelPause {durationMs :: Int}
    | -- | Scroll by the given @(deltaX, deltaY)@ pixels, anchored at the
      -- given @(x, y)@ relative to the 'Origin', over the given number of
      -- milliseconds.
      Scroll
        { durationMs :: Int
        , origin :: Origin
        , x :: Int
        , y :: Int
        , deltaX :: Int
        , deltaY :: Int
        }
    deriving stock (Show, Eq)

instance ToJSON WheelAction where
    toJSON WheelPause{..} =
        object
            [ "type" .= String "pause"
            , "duration" .= durationMs
            ]
    toJSON Scroll{..} =
        object
            [ "type" .= String "scroll"
            , "duration" .= durationMs
            , "origin" .= origin
            , "x" .= x
            , "y" .= y
            , "deltaX" .= deltaX
            , "deltaY" .= deltaY
            ]

-- | One tick of a "none" input source's action list.
newtype NoneAction = NonePause {durationMs :: Int}
    deriving stock (Show, Eq)

instance ToJSON NoneAction where
    toJSON NonePause{..} =
        object
            [ "type" .= String "pause"
            , "duration" .= durationMs
            ]

-- | A virtual input device and the sequence of actions to dispatch on it.
data InputSource
    = PointerInput
        { sourceId :: Text
        , pointerType :: PointerType
        , pointerActions :: [PointerAction]
        }
    | KeyInput
        { sourceId :: Text
        , keyActions :: [KeyAction]
        }
    | WheelInput
        { sourceId :: Text
        , wheelActions :: [WheelAction]
        }
    | NoneInput
        { sourceId :: Text
        , noneActions :: [NoneAction]
        }
    deriving stock (Show, Eq)

instance ToJSON InputSource where
    toJSON PointerInput{..} =
        object
            [ "type" .= String "pointer"
            , "id" .= sourceId
            , "parameters" .= object ["pointerType" .= pointerType]
            , "actions" .= pointerActions
            ]
    toJSON KeyInput{..} =
        object
            [ "type" .= String "key"
            , "id" .= sourceId
            , "actions" .= keyActions
            ]
    toJSON WheelInput{..} =
        object
            [ "type" .= String "wheel"
            , "id" .= sourceId
            , "actions" .= wheelActions
            ]
    toJSON NoneInput{..} =
        object
            [ "type" .= String "none"
            , "id" .= sourceId
            , "actions" .= noneActions
            ]

-- | A single mouse pointer input source named @"mouse"@.
mouse :: [PointerAction] -> InputSource
mouse = PointerInput "mouse" Mouse

-- | A single pen pointer input source named @"pen"@.
pen :: [PointerAction] -> InputSource
pen = PointerInput "pen" Pen

-- | A single touch pointer input source named @"touch"@.
touch :: [PointerAction] -> InputSource
touch = PointerInput "touch" Touch

-- | A single keyboard input source named @"keyboard"@.
keyboard :: [KeyAction] -> InputSource
keyboard = KeyInput "keyboard"

-- | A single wheel input source named @"wheel"@.
wheel :: [WheelAction] -> InputSource
wheel = WheelInput "wheel"