packages feed

monomer-1.3.0.0: test/unit/Monomer/Widgets/Containers/StackSpec.hs

{-|
Module      : Monomer.Widgets.Containers.StackSpec
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 Stack widget.
-}
{-# LANGUAGE FlexibleContexts #-}

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

import Control.Lens ((&), (^.), (.~))
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.TestUtil
import Monomer.Widgets.Containers.Stack
import Monomer.Widgets.Singles.Label
import Monomer.Widgets.Singles.Spacer

import qualified Monomer.Lens as L

-- Event handling (ignoreEmptyClick) is tested in zstack
spec :: Spec
spec = describe "Stack" $ do
  getSizeReq
  resize

getSizeReq :: Spec
getSizeReq = describe "getSizeReq" $ do
  getSizeReqEmpty
  getSizeReqItems
  getSizeReqUpdater
  getSizeReqChildSpacing

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

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

  where
    wenv = mockWenv ()
    vstackNode = vstack []
    (sizeReqW, sizeReqH) = nodeGetSizeReq wenv vstackNode

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

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

  where
    wenv = mockWenv ()
    vstackNode = vstack [
        label "Hello",
        label "how",
        label "are you?"
      ]
    (sizeReqW, sizeReqH) = nodeGetSizeReq wenv vstackNode

getSizeReqUpdater :: Spec
getSizeReqUpdater = describe "getSizeReqUpdater" $ do
  it "should return width = Min 50 2" $
    sizeReqW1 `shouldBe` minSize 50 2

  it "should return height = Max 20" $
    sizeReqH1 `shouldBe` maxSize 20 3

  it "should return width = Min 50 10" $
    sizeReqW2 `shouldBe` minSize 50 10

  it "should return height = Max 20 15" $
    sizeReqH2 `shouldBe` maxSize 20 15

  where
    wenv = mockWenv ()
    updater (rw, rh) = (minSize (rw ^. L.fixed) 2, maxSize (rh ^. L.fixed) 3)
    vstackNode1 = vstack_ [sizeReqUpdater updater] [label "Label"]
    vstackNode2 = vstack_ [sizeReqUpdater (fixedToMinW 10), sizeReqUpdater (fixedToMaxH 15)] [label "Label"]
    (sizeReqW1, sizeReqH1) = nodeGetSizeReq wenv vstackNode1
    (sizeReqW2, sizeReqH2) = nodeGetSizeReq wenv vstackNode2

getSizeReqChildSpacing :: Spec
getSizeReqChildSpacing = describe "with childSpacing" $ do
  it "should not add spacing when empty" $
    testStackSizeReq [] `shouldBe` fixedSize 0

  it "should not add spacing when singleton" $
    testStackSizeReq [spacer3] `shouldBe` fixedSize 3

  it "should add spacing between children" $
    testStackSizeReq [spacer3, spacer3] `shouldBe` fixedSize 8
  
  it "should not add spacing between invisible children" $ do
    testStackSizeReq [spacer3, spacer3 `nodeVisible` False] `shouldBe` fixedSize 3
  
  where
    wenv = mockWenv ()
    spacer3 = spacer_ [width 3, resizeFactor 0]
    testStackSizeReq children = sizeReqH where
      (_sizeReqW, sizeReqH) = nodeGetSizeReq wenv vstackNode
      vstackNode = vstack_ [childSpacing_ 2] children

resize :: Spec
resize = describe "resize" $ do
  resizeEmpty
  resizeFlexibleH
  resizeFlexibleV
  resizeStrictFlexH
  resizeStrictFlexV
  resizeMixedH
  resizeMixedV
  resizeAllV
  resizeNoSpaceV
  resizeSpacerFlexH
  resizeSpacerFixedH
  resizeChildSpacing

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 ()
    -- Main axis is adjusted to content
    vp = Rect 0 0 640 0
    vstackNode = vstack []
    newNode = nodeInit wenv vstackNode
    viewport = newNode ^. L.info . L.viewport
    children = newNode ^. L.children

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

  it "should assign size proportional to requested size to each children" $
    childrenVp `shouldBe` Seq.fromList [cvp1, cvp2, cvp3]

  where
    wenv = mockWenv () & L.windowSize .~ Size 480 640
    vp   = Rect   0 0 480 640
    cvp1 = Rect   0 0 112 640
    cvp2 = Rect 112 0 256 640
    cvp3 = Rect 368 0 112 640
    hstackNode = hstack [
        label "Label 1" `styleBasic` [expandWidth 70],
        label "Label 2" `styleBasic` [expandWidth 160],
        label "Label 3" `styleBasic` [expandWidth 70]
      ]
    newNode = nodeInit wenv hstackNode
    viewport = newNode ^. L.info . L.viewport
    childrenVp = roundRectUnits . _wniViewport . _wnInfo <$> newNode ^. L.children

resizeFlexibleV :: Spec
resizeFlexibleV = describe "flexible items, vertical" $ do
  it "should have the provided viewport size" $
    viewport `shouldBe` vp

  it "should assign size proportional to requested size to each children" $
    childrenVp `shouldBe` Seq.fromList [cvp1, cvp2, cvp3]

  where
    wenv = mockWenv ()
    vp   = Rect 0   0 640 480
    cvp1 = Rect 0   0 640 160
    cvp2 = Rect 0 160 640 160
    cvp3 = Rect 0 320 640 160
    vstackNode = vstack [
        label "Label 1" `styleBasic` [flexHeight 20],
        label "Label Number Two" `styleBasic` [flexHeight 20],
        label "Label 3" `styleBasic` [flexHeight 20]
      ]
    newNode = nodeInit wenv vstackNode
    viewport = newNode ^. L.info . L.viewport
    childrenVp = (^. L.info . L.viewport) <$> newNode ^. L.children

resizeStrictFlexH :: Spec
resizeStrictFlexH = describe "strict/flexible items, horizontal" $ do
  it "should have the provided viewport size" $
    viewport `shouldBe` vp

  it "should assign requested size to the main labels and the rest to grid" $
    childrenVp `shouldBe` Seq.fromList [cvp1, cvp2, cvp3]

  where
    wenv = mockWenv ()
    vp   = Rect   0 0 640 480
    cvp1 = Rect   0 0 100 480
    cvp2 = Rect 100 0 100 480
    cvp3 = Rect 200 0 440 480
    hstackNode = hstack [
        label "Label 1" `styleBasic` [width 100],
        label "Label 2" `styleBasic` [width 100],
        label "Label 3" `styleBasic` [expandWidth 70]
      ]
    newNode = nodeInit wenv hstackNode
    viewport = newNode ^. L.info . L.viewport
    childrenVp = (^. L.info . L.viewport) <$> newNode ^. L.children

resizeStrictFlexV :: Spec
resizeStrictFlexV = describe "strict/flexible items, vertical" $ do
  it "should have the provided viewport size" $
    viewport `shouldBe` vp

  it "should assign requested size to the main labels and the rest to grid" $
    childrenVp `shouldBe` Seq.fromList [cvp1, cvp2, cvp3]

  where
    wenv = mockWenv ()
    vp   = Rect 0   0 640 480
    cvp1 = Rect 0   0 640 100
    cvp2 = Rect 0 100 640  20
    cvp3 = Rect 0 120 640 360
    vstackNode = vstack [
        label "Label 1" `styleBasic` [height 100],
        label "Label 2",
        label "Label 3" `styleBasic` [flexHeight 100]
      ]
    newNode = nodeInit wenv vstackNode
    viewport = newNode ^. L.info . L.viewport
    childrenVp = (^. L.info . L.viewport) <$> newNode ^. L.children

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

  it "should assign size proportional to requested size to each children" $
    childrenVp `shouldBe` Seq.fromList [cvp1, cvp2]

  where
    wenv = mockWenv ()
    vp   = Rect   0 0 640  20
    cvp1 = Rect   0 0 196  20
    cvp2 = Rect 196 0 444  20
    hstackNode = vstack [
        hstack [
          label "Label 1" `styleBasic` [expandWidth 110],
          label "Label 2" `styleBasic` [expandWidth 250]
        ]
      ]
    newNode = nodeInit wenv hstackNode
    viewport = newNode ^. L.info . L.viewport
    firstChild = Seq.index (newNode ^. L.children) 0
    childrenVp = roundRectUnits . _wniViewport . _wnInfo <$> firstChild ^. L.children

resizeMixedV :: Spec
resizeMixedV = describe "mixed items, vertical" $ do
  it "should have the provided viewport size" $
    viewport `shouldBe` vp

  it "should assign size proportional to requested size to each children" $
    childrenVp `shouldBe` Seq.fromList [cvp1, cvp2, cvp3]

  where
    wenv = mockWenv ()
    vp   = Rect 0   0 70 480
    cvp1 = Rect 0   0 70  20
    cvp2 = Rect 0  20 70 426
    cvp3 = Rect 0 446 70  34
    vstackNode = hstack [
        vstack [
          label "Label 1",
          label "Label 2" `styleBasic` [minHeight 250],
          label "Label 3" `styleBasic` [flexHeight 20]
        ]
      ]
    newNode = nodeInit wenv vstackNode
    viewport = newNode ^. L.info . L.viewport
    firstChild = Seq.index (newNode ^. L.children) 0
    childrenVp = roundRectUnits . _wniViewport . _wnInfo <$> firstChild ^. L.children

resizeAllV :: Spec
resizeAllV = describe "all kinds of sizeReq, vertical" $ do
  it "should have the provided viewport size" $
    viewport `shouldBe` vp

  it "should assign size proportional to requested size to each children" $
    childrenVp `shouldBe` Seq.fromList [cvp1, cvp2, cvp3, cvp4, cvp5]

  where
    wenv = mockWenv ()
    vp   = Rect 0   0 640 480
    cvp1 = Rect 0   0 640  50
    cvp2 = Rect 0  50 640 115
    cvp3 = Rect 0 165 640 135
    cvp4 = Rect 0 300 640  80
    cvp5 = Rect 0 380 640 100
    vstackNode = vstack [
        label "Label 1" `styleBasic` [width 50, height 50],
        label "Label 2" `styleBasic` [flexWidth 60, flexHeight 60],
        label "Label 3" `styleBasic` [minWidth 70, minHeight 70],
        label "Label 4" `styleBasic` [maxWidth 80, maxHeight 80],
        label "Label 5" `styleBasic` [rangeWidth 90 100, rangeHeight 90 100]
      ]
    newNode = nodeInit wenv vstackNode
    viewport = newNode ^. L.info . L.viewport
    childrenVp = roundRectUnits . _wniViewport . _wnInfo <$> newNode ^. L.children

resizeNoSpaceV :: Spec
resizeNoSpaceV = describe "vertical, without enough space" $ do
  it "should have a larger viewport size (parent should fix it)" $ do
    viewport `shouldBe` vp
    viewport `shouldBe` vp

  it "should assign size proportional to requested size to each children" $
    childrenVp `shouldBe` Seq.fromList [cvp1, cvp2, cvp3, cvp4, cvp5]

  where
    wenv = mockWenv ()
    vp   = Rect 0   0 640 800
    cvp1 = Rect 0   0 640 200
    cvp2 = Rect 0 200 640 200
    cvp3 = Rect 0 400 640   0
    cvp4 = Rect 0 400 640 200
    cvp5 = Rect 0 600 640 200
    vstackNode = vstack [
        label "Label 1" `styleBasic` [height 200],
        label "Label 2" `styleBasic` [height 200],
        label "Label 3" `styleBasic` [flexHeight 200],
        label "Label 4" `styleBasic` [height 200],
        label "Label 5" `styleBasic` [height 200]
      ]
    newNode = nodeInit wenv vstackNode
    viewport = newNode ^. L.info . L.viewport
    childrenVp = roundRectUnits . _wniViewport . _wnInfo <$> newNode ^. L.children

resizeSpacerFlexH :: Spec
resizeSpacerFlexH = describe "label flex and spacer, horizontal" $ do
  it "should have the provided viewport size" $
    roundRectUnits viewport `shouldBe` vp

  it "should assign size proportional to requested size to each children" $
    childrenVp `shouldBe` Seq.fromList [cvp1, cvp2, cvp3]

  where
    wenv = mockWenv ()
    vp   = Rect   0 0 640 480
    cvp1 = Rect   0 0 211 480
    cvp2 = Rect 211 0   8 480
    cvp3 = Rect 219 0 421 480
    hstackNode = hstack [
        label "Label" `styleBasic` [flexWidth 100],
        filler,
        label "Label" `styleBasic` [flexWidth 200]
      ]
    newNode = nodeInit wenv hstackNode
    viewport = newNode ^. L.info . L.viewport
    childrenVp = roundRectUnits . _wniViewport . _wnInfo <$> newNode ^. L.children

resizeSpacerFixedH :: Spec
resizeSpacerFixedH = describe "label fixed and spacer, horizontal" $ do
  it "should have the provided viewport size" $
    viewport `shouldBe` vp

  it "should assign size proportional to requested size to each children" $
    childrenVp `shouldBe` Seq.fromList [cvp1, cvp2, cvp3]

  where
    wenv = mockWenv ()
    vp   = Rect   0 0 640 480
    cvp1 = Rect   0 0 100 480
    cvp2 = Rect 100 0 340 480
    cvp3 = Rect 440 0 200 480
    hstackNode = hstack [
        label "Label" `styleBasic` [width 100],
        filler,
        label "Label" `styleBasic` [width 200]
      ]
    newNode = nodeInit wenv hstackNode
    viewport = newNode ^. L.info . L.viewport
    childrenVp = roundRectUnits . _wniViewport . _wnInfo <$> newNode ^. L.children

resizeChildSpacing :: Spec
resizeChildSpacing = describe "with childSpacing" $ do

  it "should add spacing between children" $
    testStackChildLocations [spacer3, spacer3] `shouldBe`
      Seq.fromList [Rect 0 0 640 3, Rect 0 5 640 3]
  
  it "should not add spacing between invisible children" $
    testStackChildLocations [spacer3, spacer3 `nodeVisible` False, spacer3] `shouldBe`
      Seq.fromList [Rect 0 0 640 3, Rect 0 0 0 0, Rect 0 5 640 3]
  
  where
    wenv = mockWenv ()
    spacer3 = spacer_ [width 3, resizeFactor 0]
    testStackChildLocations children = childLocations where
      vstackNode = vstack_ [childSpacing_ 2] children
      newNode = nodeInit wenv vstackNode
      childLocations = roundRectUnits . _wniViewport . _wnInfo <$> newNode ^. L.children