monomer-1.5.1.0: test/unit/Monomer/Widgets/Containers/DropdownSpec.hs
{-|
Module : Monomer.Widgets.Containers.DropdownSpec
Copyright : (c) 2018 Francisco Vallarino
License : BSD-3-Clause (see the LICENSE file)
Maintainer : fjvallarino@gmail.com
Stability : experimental
Portability : non-portable
Unit tests for Dropdown widget.
-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
module Monomer.Widgets.Containers.DropdownSpec (spec) where
import Control.Lens ((&), (^.), (.~))
import Control.Lens.TH (abbreviatedFields, makeLensesWith)
import Data.Default
import Data.Functor ((<&>))
import Data.Text (Text)
import Test.Hspec
import TextShow
import qualified Data.Sequence as Seq
import Monomer.Core
import Monomer.Core.Combinators
import Monomer.Event
import Monomer.TestEventUtil
import Monomer.TestUtil
import Monomer.Widgets.Containers.Dropdown
import Monomer.Widgets.Containers.Stack
import Monomer.Widgets.Singles.Button
import Monomer.Widgets.Singles.Label
import qualified Monomer.Lens as L
data TestEvt
= ItemSel Int TestItem
| GotFocus Path
| LostFocus Path
deriving (Eq, Show)
newtype TestItem = TestItem {
_tiCode :: Int
} deriving (Eq, Show)
instance TextShow TestItem where
showb (TestItem c) = "TestItem: " <> showb c
newtype TestModel = TestModel {
_tmSelectedItem :: TestItem
} deriving (Eq, Show)
makeLensesWith abbreviatedFields ''TestItem
makeLensesWith abbreviatedFields ''TestModel
testItems = [0..50] <&> TestItem
testItem0 = head testItems
testItem3 = testItems!!3
testItem7 = testItems!!7
testItem10 = testItems!!10
spec :: Spec
spec = describe "Dropdown" $ do
handleEvent
handleEventValue
getSizeReq
testWidgetType
handleEvent :: Spec
handleEvent = describe "handleEvent" $ do
let mainP = Point 50 10
it "should not update the model if not clicked" $
model [evtClick (Point 3000 3000)] ^. selectedItem `shouldBe` testItem0
it "should not update the model if clicked outside the list" $
model [evtClick mainP, evtClick (Point 300 500)] ^. selectedItem `shouldBe` testItem0
it "should update the model when clicked" $ do
let itemP = Point 50 90
model [evtClick mainP, evtClick itemP] ^. selectedItem `shouldBe` testItem3
it "should update the model when clicked, after wheel scrolled" $ do
let itemP = Point 50 90
let steps = [evtClick mainP, WheelScroll itemP (Point 0 (-14)) WheelNormal, evtClick itemP]
model steps ^. selectedItem `shouldBe` testItem10
it "should update the model when clicked, after list is displaced because of arrow press" $ do
let p = Point 50 30
let steps = [evtClick mainP] ++ replicate 12 (evtK keyDown) ++ [evtClick p]
model steps ^. selectedItem `shouldBe` testItem3
it "should update the model when Enter/Space is pressed, after navigating to an element" $ do
let steps = [evtClick mainP] ++ replicate 11 (evtK keyDown) ++ [evtK keyUp, evtK keySpace]
model steps ^. selectedItem `shouldBe` testItem10
it "should generate an event when focus is received" $ do
eventsCnt [evtK keyTab] `shouldBe` Seq.singleton (GotFocus $ Seq.fromList [0, 0])
it "should generate an event when focus is lost and list is not open" $ do
let path = Seq.fromList [0, 0]
eventsCnt [evtK keyTab, evtK keyTab] `shouldBe` Seq.fromList [GotFocus path, LostFocus path]
where
wenv = mockWenv (TestModel testItem0)
labelItem = label . showt
ddNode :: WidgetNode TestModel TestEvt
ddNode = vstack [
dropdown_ selectedItem testItems labelItem labelItem [maxHeight 200]
]
cntNode = vstack [
button "Test" (ItemSel 0 testItem0),
dropdown_ selectedItem testItems labelItem labelItem [onFocus GotFocus, onBlur LostFocus]
]
model es = nodeHandleEventModel wenv es ddNode
eventsCnt evts = nodeHandleEventEvts wenv evts cntNode
handleEventValue :: Spec
handleEventValue = describe "handleEventValue" $ do
let outP = Point 3000 3000
let mainP = Point 50 10
it "should not generate an event if clicked outside" $
clickEvts outP `shouldBe` Seq.empty
it "should not generate an event when clicked outside, after being opened with keyboard" $ do
let itemP = Point 50 90
events [evtK keyDown, evtClick outP, evtClick itemP] `shouldBe` Seq.empty
it "should generate an event when clicked, after being opened with keyboard" $ do
let itemP = Point 50 90
events [evtK keyDown, evtClick itemP] `shouldBe` Seq.singleton (ItemSel 3 testItem3)
it "should generate an event when Enter/Space is pressed, after navigating to an element" $ do
let steps = [evtK keyDown] ++ replicate 7 (evtK keyDown) ++ [evtK keySpace]
events steps `shouldBe` Seq.singleton (ItemSel 7 testItem7)
it "should generate a focus lost event when opened, canceled, and navigated away" $ do
let steps = [evtK keyDown, evtK keyEscape, evtK keyTab]
events steps `shouldBe` Seq.singleton (LostFocus $ Seq.fromList [0, 1])
it "should generate an event when focus is lost and list is open. The navigation also generates a change event" $ do
let steps = [evtK keyDown] ++ replicate 3 (evtK keyDown) ++ [evtK keyTab]
events steps `shouldBe` Seq.fromList [ItemSel 3 testItem3, LostFocus $ Seq.fromList [0, 1]]
where
wenv = mockWenv (TestModel testItem0)
labelItem = label . showt
ddNode = vstack [
dropdownV_ testItem0 ItemSel testItems labelItem labelItem [maxHeight 200, onBlur LostFocus],
button "Test" (ItemSel 0 testItem0)
]
clickEvts p = nodeHandleEventEvts wenv [evtClick p] ddNode
events es = nodeHandleEventEvts wenv es ddNode
getSizeReq :: Spec
getSizeReq = describe "getSizeReq" $ do
it "should return width = Flex 120" $
sizeReqW `shouldBe` expandSize 120 1
it "should return height = Flex 20 1" $
sizeReqH `shouldBe` fixedSize 20
where
wenv = mockWenvEvtUnit (TestModel testItem0)
labelItem l = label_ (showt l) [resizeFactorW 0.01]
ddNode = dropdown selectedItem testItems labelItem labelItem
(sizeReqW, sizeReqH) = nodeGetSizeReq wenv ddNode
testWidgetType :: Spec
testWidgetType = describe "testWidgetType" $ do
it "should set the correct widgetType" $
node ^. L.info . L.widgetType `shouldBe` "dropdown-TestItem"
where
labelItem l = label_ (showt l) []
node :: WidgetNode TestModel TestEvt
node = dropdown selectedItem testItems labelItem labelItem