packages feed

monomer-1.2.0.0: test/unit/Monomer/Widgets/Containers/ZStackSpec.hs

{-|
Module      : Monomer.Widgets.Containers.ZStackSpec
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 ZStack widget.
-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}

module Monomer.Widgets.Containers.ZStackSpec (spec) where

import Control.Lens ((&), (^.), (.~))
import Control.Lens.TH (abbreviatedFields, makeLensesWith)
import Data.Text (Text)
import Test.Hspec

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.Composite
import Monomer.Widgets.Containers.Confirm
import Monomer.Widgets.Containers.Stack
import Monomer.Widgets.Containers.ZStack
import Monomer.Widgets.Singles.Button
import Monomer.Widgets.Singles.Label
import Monomer.Widgets.Singles.TextField

import qualified Monomer.Lens as L

newtype BtnEvent
  = BtnClick Int
  deriving (Eq, Show)

data TestModel = TestModel {
  _tmTextValue1 :: Text,
  _tmTextValue2 :: Text
} deriving (Eq, Show)

makeLensesWith abbreviatedFields ''TestModel

spec :: Spec
spec = describe "ZStack" $ do
  handleEvent
  getSizeReq
  resize

handleEvent :: Spec
handleEvent = describe "handleEvent" $ do
  handleEventFirstVisible
  handleEventAllLayersActive
  handleEventFocusTop
  handleEventFocusAll
  handleEventFocusChange
  handleEventFocusKeep

handleEventFirstVisible :: Spec
handleEventFirstVisible = describe "handleEventFirstVisible" $ do
  it "should not generate an event if clicked outside" $
    clickEvts (Point 3000 3000) `shouldBe` Seq.empty

  it "should click the second layer, since top is not visible" $
    clickEvts (Point 100 100) `shouldBe` Seq.singleton (BtnClick 2)

  where
    wenv = mockWenv ()
    zstackNode = zstack [
        button "Click 1" (BtnClick 1),
        button "Click 2" (BtnClick 2),
        button "Click 3" (BtnClick 3) `nodeVisible` False
      ]
    clickEvts p = nodeHandleEventEvts wenv [evtClick p] zstackNode

handleEventAllLayersActive :: Spec
handleEventAllLayersActive = describe "handleEventAllLayersActive" $ do
  it "should not generate an event if clicked outside" $
    clickEvts (Point 3000 3000) `shouldBe` Seq.empty

  it "should click the first layer, since top is not visible and second does not have widgets in that location" $
    clickEvts (Point 200 15) `shouldBe` Seq.singleton (BtnClick 1)

  where
    wenv = mockWenv ()
    zstackNode = zstack_ [onlyTopActive_ False] [
        button "Click 1" (BtnClick 1),
        vstack [
          button "Click 2" (BtnClick 2) `styleBasic` [height 10]
        ],
        button "Click 3" (BtnClick 3) `nodeVisible` False
      ]
    clickEvts p = nodeHandleEventEvts wenv [evtClick p] zstackNode

handleEventFocusTop :: Spec
handleEventFocusTop = describe "handleEventFocusTop" $
  it "should not attempt to set focus on lower layers" $ do
    let steps = [evtK keyTab, evtK keyTab, evtT "abc"]
    model steps ^. textValue1 `shouldBe` ""
    model steps ^. textValue2 `shouldBe` "abc"

  where
    wenv = mockWenvEvtUnit (TestModel "" "")
    zstackNode = zstack [
        textField textValue1,
        textField textValue2
      ]
    model es = nodeHandleEventModel wenv es zstackNode

handleEventFocusAll :: Spec
handleEventFocusAll = describe "handleEventFocusAll" $
  it "should set focus on second layer, since it's enabled" $ do
    let steps = [evtK keyTab, evtT "abc"]
    model steps ^. textValue1 `shouldBe` "abc"
    model steps ^. textValue2 `shouldBe` ""

  where
    wenv = mockWenvEvtUnit (TestModel "" "")
    zstackNode = zstack_ [onlyTopActive_ False] [
        textField textValue1,
        textField textValue2
      ]
    model es = nodeHandleEventModel wenv es zstackNode

handleEventFocusChange :: Spec
handleEventFocusChange = describe "handleEventFocusChange" $
  it "should restore focus when switching between layers" $ do
    let steps = [ evtK keyTab, evtK keyReturn, evtK keyTab, evtK keyReturn, evtK keyReturn, evtK keyReturn ]
    evts steps `shouldBe` Seq.fromList [BtnClick 2, BtnClick 4, BtnClick 2, BtnClick 4]

  where
    wenv = mockWenv 10
    handleEvent
      :: WidgetEnv Int BtnEvent
      -> WidgetNode Int BtnEvent
      -> Int
      -> BtnEvent
      -> [EventResponse Int BtnEvent Int BtnEvent]
    handleEvent wenv _ model (BtnClick idx) = [Report (BtnClick idx), Model idx]
    buildUI wenv model = zstack [
        hstack [
          button "3" (BtnClick 3),
          button "4" (BtnClick 4)
        ],
        hstack [
          button "1" (BtnClick 1),
          button "2" (BtnClick 2)
        ] `nodeVisible` (model > 2)
      ]
    cmpNode = composite "main" id buildUI handleEvent
    evts es = nodeHandleEventEvts wenv es cmpNode

handleEventFocusKeep :: Spec
handleEventFocusKeep = describe "handleEventFocusKeep" $
  it "should not restore focus when switching between layers if a focus change request is detected" $ do
    let steps = [ evtK keyTab, evtK keyReturn, evtK keyTab, evtK keyReturn, evtK keyReturn, evtK keyReturn ]
    evts steps `shouldBe` Seq.fromList [BtnClick 2, BtnClick 4, BtnClick 2, BtnClick 3]

  where
    wenv = mockWenv 10
    handleEvent
      :: WidgetEnv Int BtnEvent
      -> WidgetNode Int BtnEvent
      -> Int
      -> BtnEvent
      -> [EventResponse Int BtnEvent Int BtnEvent]
    handleEvent wenv _ model (BtnClick idx) = [Report (BtnClick idx), Model idx]
    buildUI wenv model = zstack [
        hstack [
          confirmMsg "Message" (BtnClick 3) (BtnClick 4)
        ] `nodeVisible` (model <= 2),
        hstack [
          button "1" (BtnClick 1),
          button "2" (BtnClick 2)
        ] `nodeVisible` (model > 2)
      ]
    cmpNode = composite "main" id buildUI handleEvent
    evts es = nodeHandleEventEvts wenv es cmpNode

getSizeReq :: Spec
getSizeReq = describe "getSizeReq" $ do
  getSizeReqEmpty
  getSizeReqItems
  getSizeReqItemsFixed

getSizeReqEmpty :: Spec
getSizeReqEmpty = describe "empty" $ do
  it "should return width = Fixed 0" $
    sizeReqW `shouldBe` fixedSize 0

  it "should return height = Fixed 0" $
    sizeReqH `shouldBe` fixedSize 0

  where
    wenv = mockWenv ()
    zstackNode = zstack []
    (sizeReqW, sizeReqH) = nodeGetSizeReq wenv zstackNode

getSizeReqItems :: Spec
getSizeReqItems = describe "several items, horizontal" $ do
  it "should return width = Fixed 130" $
    sizeReqW `shouldBe` fixedSize 130

  it "should return height = Fixed 60" $
    sizeReqH `shouldBe` fixedSize 60

  where
    wenv = mockWenv ()
    zstackNode = zstack [
        vstack [
          label "Label a1"
        ],
        vstack [
          label "Long label b1",
          label "Long label b2"
        ],
        vstack [
          label "Label c1",
          label "Label c2",
          label "Label c3"
        ]
      ]
    (sizeReqW, sizeReqH) = nodeGetSizeReq wenv zstackNode

getSizeReqItemsFixed :: Spec
getSizeReqItemsFixed = describe "several items, horizontal" $ do
  it "should return width = Fixed 300" $
    sizeReqW `shouldBe` fixedSize 300

  it "should return height = Fixed 40" $
    sizeReqH `shouldBe` fixedSize 40

  where
    wenv = mockWenv ()
    zstackNode = zstack [
        vstack [
          label "Label a1",
          label "Label a2"
        ],
        vstack [
          label "Long b1",
          label "Long b2"
        ] `styleBasic` [width 300]
      ]
    (sizeReqW, sizeReqH) = nodeGetSizeReq wenv zstackNode

resize :: Spec
resize = describe "resize" $ do
  resizeEmpty
  resizeItems

resizeEmpty :: Spec
resizeEmpty = describe "empty" $ do
  it "should have the provided viewport size" $
    viewport `shouldBe` vp

  it "should not have children" $
    children `shouldSatisfy` Seq.null

  where
    wenv = mockWenv ()
    vp = Rect 0 0 640 480
    zstackNode = zstack []
    newNode = nodeInit wenv zstackNode
    viewport = newNode ^. L.info . L.viewport
    children = newNode ^. L.children

resizeItems :: Spec
resizeItems = describe "several items, horizontal" $ do
  it "should have the provided viewport size" $
    viewport `shouldBe` vp

  it "should assign the same viewport size to each children" $
    childrenRa `shouldBe` Seq.fromList [vp, vp, vp]

  where
    wenv = mockWenv ()
    vp   = Rect 0 0 640 480
    zstackNode = zstack [
        label "Label 1",
        label "Label Number Two",
        label "Label 3"
      ]
    newNode = nodeInit wenv zstackNode
    viewport = newNode ^. L.info . L.viewport
    childrenRa = (^. L.info . L.viewport) <$> newNode ^. L.children