packages feed

reflex-vty-1.2.0.0: test/Reflex/Vty/Widget/MouseSpec.hs

{-# LANGUAGE OverloadedStrings #-}

module Reflex.Vty.Widget.MouseSpec (spec) where

import qualified Graphics.Vty as V
import Test.Hspec

import Reflex.Vty.Widget.Input.Mouse

-- | Replay a list of vty events through 'stepDrag', keeping the previous drag
-- state when an event is irrelevant (mirroring how 'drag' holds its state).
-- Returns the 'Drag' emitted for each relevant event, in order.
runDrag :: V.Button -> [V.Event] -> [Drag]
runDrag btn = go Nothing
  where
    go _ [] = []
    go prev (e : es) = case stepDrag btn prev e of
      Just d -> d : go (Just d) es
      Nothing -> go prev es

down :: Int -> Int -> V.Button -> V.Event
down x y b = V.EvMouseDown x y b []

up :: Int -> Int -> Maybe V.Button -> V.Event
up x y b = V.EvMouseUp x y b

spec :: Spec
spec = describe "Reflex.Vty.Widget.Input.Mouse" $ do
  describe "Drag" $ do
    it "has correct field types" $ do
      let d = Drag (0, 0) (1, 1) V.BLeft [] DragStart
      _drag_from d `shouldBe` (0, 0)
      _drag_to d `shouldBe` (1, 1)
      _drag_button d `shouldBe` V.BLeft
      _drag_state d `shouldBe` DragStart

  describe "DragState" $ do
    it "enumerates start, dragging, end in order" $
      [minBound .. maxBound] `shouldBe` [DragStart, Dragging, DragEnd]

  describe "stepDrag" $ do
    it "starts a drag on the first matching mouse-down" $
      stepDrag V.BLeft Nothing (down 3 4 V.BLeft)
        `shouldBe` Just (Drag (3, 4) (3, 4) V.BLeft [] DragStart)

    it "ignores a mouse-down for a different button" $
      stepDrag V.BLeft Nothing (down 3 4 V.BRight) `shouldBe` Nothing

    it "ignores non-mouse events" $
      stepDrag V.BLeft Nothing (V.EvKey (V.KChar 'a') []) `shouldBe` Nothing

    it "continues as Dragging on subsequent moves, preserving the origin" $ do
      let start = Drag (3, 4) (3, 4) V.BLeft [] DragStart
      stepDrag V.BLeft (Just start) (down 5 6 V.BLeft)
        `shouldBe` Just (Drag (3, 4) (5, 6) V.BLeft [] Dragging)

    it "ends the drag on mouse-up (button specified)" $ do
      let dragging = Drag (3, 4) (5, 6) V.BLeft [] Dragging
      stepDrag V.BLeft (Just dragging) (up 7 8 (Just V.BLeft))
        `shouldBe` Just (Drag (3, 4) (7, 8) V.BLeft [] DragEnd)

    it "ends the drag on mouse-up (button unspecified)" $ do
      let dragging = Drag (3, 4) (5, 6) V.BLeft [] Dragging
      stepDrag V.BLeft (Just dragging) (up 7 8 Nothing)
        `shouldBe` Just (Drag (3, 4) (7, 8) V.BLeft [] DragEnd)

    it "ignores a stray mouse-up after the drag already ended" $ do
      let ended = Drag (3, 4) (7, 8) V.BLeft [] DragEnd
      stepDrag V.BLeft (Just ended) (up 9 9 (Just V.BLeft)) `shouldBe` Nothing

    it "starts a fresh drag on a mouse-down after the previous one ended" $ do
      let ended = Drag (3, 4) (7, 8) V.BLeft [] DragEnd
      stepDrag V.BLeft (Just ended) (down 1 1 V.BLeft)
        `shouldBe` Just (Drag (1, 1) (1, 1) V.BLeft [] DragStart)

    it "produces Start -> Dragging -> End across a full drag" $ do
      let ds = runDrag V.BLeft [down 1 1 V.BLeft, down 2 2 V.BLeft, down 3 3 V.BLeft, up 3 3 (Just V.BLeft)]
      map _drag_state ds `shouldBe` [DragStart, Dragging, Dragging, DragEnd]
      map _drag_from ds `shouldBe` replicate 4 (1, 1)
      _drag_to (last ds) `shouldBe` (3, 3)

    it "tracks two consecutive drags, each with its own Start and End" $ do
      let evs =
            [ down 1 1 V.BLeft
            , up 1 1 (Just V.BLeft)
            , down 5 5 V.BLeft
            , down 6 6 V.BLeft
            , up 6 6 Nothing
            ]
      map _drag_state (runDrag V.BLeft evs)
        `shouldBe` [DragStart, DragEnd, DragStart, Dragging, DragEnd]

  describe "MouseDown" $ do
    it "stores button, coordinates, and modifiers" $ do
      let md = MouseDown V.BLeft (5, 10) [V.MShift]
      _mouseDown_button md `shouldBe` V.BLeft
      _mouseDown_coordinates md `shouldBe` (5, 10)
      _mouseDown_modifiers md `shouldBe` [V.MShift]

  describe "MouseUp" $ do
    it "stores optional button and coordinates" $ do
      let mu = MouseUp (Just V.BLeft) (3, 7)
      _mouseUp_button mu `shouldBe` Just V.BLeft
      _mouseUp_coordinates mu `shouldBe` (3, 7)

  describe "ScrollDirection" $ do
    it "has up and down constructors" $ do
      ScrollDirection_Up `shouldNotBe` ScrollDirection_Down