{-| 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