content: {-|
  Module      : Monomer.Widgets.CompositeSpec
  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 Composite widget.
  -}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE FlexibleInstances #-}
  {-# LANGUAGE FunctionalDependencies #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE TemplateHaskell #-}
  
  module Monomer.Widgets.CompositeSpec (spec) where
  
  import Control.Lens hiding ((|>), deep)
  import Control.Lens.TH (abbreviatedFields, makeLensesWith)
  import Data.Default
  import Data.Foldable (toList)
  import Data.List (find)
  import Data.Maybe
  import Data.Text (Text)
  import Data.Typeable (Typeable, cast)
  import TextShow
  import Test.Hspec
  
  import qualified Data.Map as Map
  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.Box
  import Monomer.Widgets.Containers.Grid
  import Monomer.Widgets.Containers.Stack
  import Monomer.Widgets.Containers.ZStack
  import Monomer.Widgets.Singles.Button
  import Monomer.Widgets.Singles.Checkbox
  import Monomer.Widgets.Singles.Label
  import Monomer.Widgets.Singles.Spacer
  import Monomer.Widgets.Singles.TextField
  import Monomer.Widgets.Util.Widget
  
  import qualified Monomer.Lens as L
  import qualified Monomer.Widgets.Single as SG
  
  data MainEvt
    = MainBtnClicked
    | ChildClicked
    | MainResize Rect
    | OnInit
    | OnDispose
    | OnChange MainModel
    | ReportOnChange MainModel MainModel
    deriving (Eq, Show)
  
  data ChildEvt
    = ChildBtnClicked
    | ChildMessage String
    | ChildResize Rect
    | ChildDispose
    deriving (Eq, Show)
  
  data DeepEvt
    = DeepInit
    deriving (Eq, Show)
  
  data MainModel = MainModel {
    _tmClicks :: Int,
    _tmChild :: ChildModel
  } deriving (Eq, Show)
  
  instance Default MainModel where
    def = MainModel {
      _tmClicks = 0,
      _tmChild = def
    }
  
  data ChildModel = ChildModel {
    _cmClicks :: Int,
    _cmMessage :: String,
    _cmDeep :: DeepModel
  } deriving (Eq, Show)
  
  instance Default ChildModel where
    def = ChildModel {
      _cmClicks = 0,
      _cmMessage = "",
      _cmDeep = def
    }
  
  newtype DeepModel = DeepModel {
    _dmClicked :: Bool
  } deriving (Eq, Show)
  
  instance Default DeepModel where
    def = DeepModel {
      _dmClicked = False
    }
  
  data TestModel = TestModel {
    _tmItems :: [Int],
    _tmText1 :: Text,
    _tmText2 :: Text
  } deriving (Eq, Show)
  
  instance Default TestModel where
    def = TestModel {
      _tmItems = [],
      _tmText1 = "",
      _tmText2 = ""
    }
  
  msgWidget = defaultWidgetNode "msgWidget" $ SG.createSingle () def {
    SG.singleHandleMessage = msgWidgetHandleMessage
  }
  
  msgWidgetHandleMessage wenv node target message = Just (resultEvts node evts) where
    val = fromMaybe "" (cast message)
    evts = [ChildMessage val]
  
  makeLensesWith abbreviatedFields ''MainModel
  makeLensesWith abbreviatedFields ''ChildModel
  makeLensesWith abbreviatedFields ''DeepModel
  makeLensesWith abbreviatedFields ''TestModel
  
  baseLens idx = L.children . ix 0 . L.children . ix idx . L.children . ix 0
  pathLens idx = baseLens idx . L.info . L.path
  widLens idx = baseLens idx . L.info . L.widgetId
  
  spec :: Spec
  spec = describe "Composite" $ do
    handleEvent
    handleMessage
    findByPoint
    findByPath
    findNextFocus
    mergeUserResize
    getSizeReq
    resize
  
  handleEvent :: Spec
  handleEvent = describe "handleEvent" $ do
    handleEventBasic
    handleEventOnInit
    handleEventOnDispose
    handleEventOnChange
    handleEventNewRoot
    handleEventChild
    handleEventResize
    handleEventLocalKey
  
  handleEventBasic :: Spec
  handleEventBasic = describe "handleEventBasic" $ do
    it "should not generate an event if clicked outside" $ do
      model [evtClick (Point 3000 3000)] ^. clicks `shouldBe` 0
      reqs [evtClick (Point 3000 3000)] `shouldBe` Seq.Empty
  
    it "should generate a user provided event when clicked" $ do
      model [evtClick (Point 10 10)] ^. clicks `shouldBe` 1
      reqs [evtClick (Point 10 10)] `shouldSatisfy` (== 3) . length
  
    where
      wenv = mockWenv def
      handleEvent
        :: WidgetEnv MainModel MainEvt
        -> WidgetNode MainModel MainEvt
        -> MainModel
        -> MainEvt
        -> [EventResponse MainModel MainEvt MainModel MainEvt]
      handleEvent wenv node model evt = case evt of
        MainBtnClicked -> [Model (model & clicks %~ (+1))]
        _ -> []
      buildUI wenv model = button "Click" MainBtnClicked
      cmpNode = composite "main" id buildUI handleEvent
      model es = nodeHandleEventModel wenv es cmpNode
      reqs es = nodeHandleEventReqs wenv es cmpNode
  
  handleEventOnInit :: Spec
  handleEventOnInit = describe "handleEventOnInit" $ do
    it "should generate an init event" $ do
      evts [] `shouldBe` Seq.singleton OnInit
  
    where
      wenv = mockWenv def
      handleEvent
        :: WidgetEnv MainModel MainEvt
        -> WidgetNode MainModel MainEvt
        -> MainModel
        -> MainEvt
        -> [EventResponse MainModel MainEvt MainModel MainEvt]
      handleEvent wenv node model evt = case evt of
        OnInit{} -> [Report evt]
        _ -> []
      buildUI wenv model = vstack []
      cmpNode = composite_ "main" id buildUI handleEvent [onInit OnInit]
      evts es = nodeHandleEventEvts_ wenv WInitKeepFirst es cmpNode
  
  handleEventOnDispose :: Spec
  handleEventOnDispose = describe "handleEventOnDispose" $ do
    it "should generate a dispose event" $ do
      evts ^? L.requests . ix 0 `shouldBe` Just RenderOnce
      evts ^? L.requests . ix 1 `shouldBe` Just (RaiseEvent ChildClicked)
      evts ^? L.requests . ix 2 `shouldBe` Just (SetClipboard ClipboardEmpty)
  
    where
      wenv = mockWenv def
      handleEvent
        :: WidgetEnv MainModel MainEvt
        -> WidgetNode MainModel MainEvt
        -> MainModel
        -> MainEvt
        -> [EventResponse MainModel MainEvt MainModel MainEvt]
      handleEvent wenv node model evt = case evt of
        OnDispose{} -> [ Request RenderOnce, Report ChildClicked ]
        _ -> []
      buildUI wenv model = vstack []
      cmpNode = nodeInit wenv
        $ composite_ "main" id buildUI handleEvent [onDispose OnDispose, onDisposeReq (SetClipboard ClipboardEmpty)]
      evts = widgetDispose (cmpNode ^. L.widget) wenv cmpNode
  
  handleEventOnChange :: Spec
  handleEventOnChange = describe "handleEventOnChange" $ do
    it "should not generate an event if model did not change" $ do
      evts [evtClick (Point 10 30)] `shouldBe` Seq.empty
  
    it "should generate an event if model changed" $ do
      let items = toList $ evts [evtClick (Point 10 10)]
      let ReportOnChange oldModel newModel = head items
  
      oldModel `shouldNotBe` newModel
  
    where
      wenv = mockWenv def
      handleEvent
        :: WidgetEnv MainModel MainEvt
        -> WidgetNode MainModel MainEvt
        -> MainModel
        -> MainEvt
        -> [EventResponse MainModel MainEvt MainModel MainEvt]
      handleEvent wenv node model evt = case evt of
        MainBtnClicked -> [Model (model & clicks %~ (+1))]
        ChildClicked -> [Model model]
        OnChange oldModel -> [Report $ ReportOnChange oldModel model]
        _ -> []
      buildUI wenv model = vstack [
          button "Click main" MainBtnClicked,
          button "Click secondary" ChildClicked
        ]
      cmpNode = composite_ "main" id buildUI handleEvent [onChange OnChange]
      evts es = nodeHandleEventEvts wenv es cmpNode
  
  handleEventNewRoot :: Spec
  handleEventNewRoot = describe "handleEventNewRoot" $ do
    it "should generate a resize request when the widgetType of the root widget changes" $ do
      reqs [evtClick (Point 10 10)] `shouldSatisfy` (== 4) . length
      reqs [evtClick (Point 10 10)] `shouldSatisfy` (== 1) . length . Seq.filter isResizeWidgets
  
    where
      wenv = mockWenv def
      handleEvent
        :: WidgetEnv MainModel MainEvt
        -> WidgetNode MainModel MainEvt
        -> MainModel
        -> MainEvt
        -> [EventResponse MainModel MainEvt MainModel MainEvt]
      handleEvent wenv node model evt = case evt of
        MainBtnClicked -> [Model (model & clicks %~ (+1))]
        _ -> []
      buildUI wenv model
        | even (model ^. clicks) = button "Click" MainBtnClicked
        | otherwise = label "Test"
      cmpNode = composite "main" id buildUI handleEvent
      model es = nodeHandleEventModel wenv es cmpNode
      reqs es = nodeHandleEventReqs wenv es cmpNode
  
  handleEventChild :: Spec
  handleEventChild = describe "handleEventChild" $ do
    it "should not generate an event if clicked outside" $ do
      model [evtClick (Point 3000 3000)] ^. clicks `shouldBe` 0
      model [evtClick (Point 3000 3000)] ^. child . clicks `shouldBe` 0
      model [evtClick (Point 3000 3000)] ^. child . deep . clicked `shouldBe` False
  
    it "should generate a main event when clicked in main button" $ do
      model [evtClick (Point 10 10)] ^. clicks `shouldBe` 1
      model [evtClick (Point 10 10)] ^. child . clicks `shouldBe` 0
      model [evtClick (Point 10 10)] ^. child . deep . clicked `shouldBe` False
  
    it "should generate a child event when clicked in child button" $ do
      model [evtClick (Point 10 30)] ^. clicks `shouldBe` 0
      model [evtClick (Point 10 30)] ^. child . clicks `shouldBe` 1
      model [evtClick (Point 10 30)] ^. child . deep . clicked `shouldBe` False
  
    it "should update the nested model when the checkbox is clicked" $ do
      model [evtClick (Point 10 50)] ^. clicks `shouldBe` 0
      model [evtClick (Point 10 50)] ^. child . clicks `shouldBe` 0
      model [evtClick (Point 10 50)] ^. child . deep . clicked `shouldBe` True
  
    where
      wenv = mockWenv def
  
      handleDeep
        :: WidgetEnv DeepModel DeepEvt
        -> WidgetNode DeepModel DeepEvt
        -> DeepModel
        -> DeepEvt
        -> [EventResponse DeepModel DeepEvt ChildModel ChildEvt]
      handleDeep wenv node model evt = []
      buildDeep wenv model = checkbox clicked
  
      handleChild
        :: WidgetEnv ChildModel ChildEvt
        -> WidgetNode ChildModel ChildEvt
        -> ChildModel
        -> ChildEvt
        -> [EventResponse ChildModel ChildEvt MainModel MainEvt]
      handleChild wenv node model evt = [Model (model & clicks %~ (+1))]
      buildChild wenv model = vstack [
          button "Click" ChildBtnClicked,
          composite "deep" deep buildDeep handleDeep
        ]
  
      handleEvent
        :: WidgetEnv MainModel MainEvt
        -> WidgetNode MainModel MainEvt
        -> MainModel
        -> MainEvt
        -> [EventResponse MainModel MainEvt MainModel ()]
      handleEvent wenv node model evt = [Model (model & clicks %~ (+1))]
      buildUI wenv model = vstack [
          button "Click" MainBtnClicked,
          composite "child" child buildChild handleChild
        ]
  
      cmpNode = composite "main" id buildUI handleEvent
      model es = nodeHandleEventModel wenv es cmpNode
  
  handleEventResize :: Spec
  handleEventResize = describe "handleEventResize" $ do
    it "should not generate a resize event on init" $
      events [] `shouldBe` Seq.fromList []
  
    it "should generate a resize event when size changes" $
      events [evtClick (Point 10 10)] `shouldBe` Seq.fromList [MainResize cvp]
  
    where
      wenv = mockWenv def
      handleChild
        :: WidgetEnv ChildModel ChildEvt
        -> WidgetNode ChildModel ChildEvt
        -> ChildModel
        -> ChildEvt
        -> [EventResponse ChildModel ChildEvt MainModel MainEvt]
      handleChild wenv node model evt = case evt of
        ChildResize rect -> [Report (MainResize rect)]
        _ -> [Model (model & clicks %~ (+1))]
      buildChild wenv model = vstack [
          button "Click" ChildBtnClicked,
          label "Test" `styleBasic` [height 3000] `nodeVisible` (model ^. clicks > 0)
        ]
      handleEvent
        :: WidgetEnv MainModel MainEvt
        -> WidgetNode MainModel MainEvt
        -> MainModel
        -> MainEvt
        -> [EventResponse MainModel MainEvt MainModel MainEvt]
      handleEvent wenv node model evt = case evt of
        MainResize{} -> [Report evt]
        _ -> [Model (model & clicks %~ (+1))]
      buildUI wenv model = vstack [
          composite_ "child" child buildChild handleChild [onResize ChildResize]
        ]
      cmpNode = composite_ "main" id buildUI handleEvent [onResize MainResize]
      model es = nodeHandleEventModel wenv es cmpNode
      events es = nodeHandleEventEvts wenv es cmpNode
      vp = Rect 0 0 640 480
      cvp = Rect 0 0 640 3020
  
  handleEventLocalKey :: Spec
  handleEventLocalKey = describe "handleEventLocalKey" $ do
    handleEventLocalKeySingleState
    handleEventLocalKeyRemoveItem
  
  handleEventLocalKeySingleState :: Spec
  handleEventLocalKeySingleState = describe "handleEventLocalKeySingleState" $
    it "should insert new text at the end, since its merged with a local key" $ do
      wenv1 ^. L.model . text1 `shouldBe` "aacc"
      wenv1 ^. L.model . text2 `shouldBe` ""
      wenv2 ^. L.model . text1 `shouldBe` "aaccbb"
      wenv2 ^. L.model . text2 `shouldBe` ""
      newInstRoot ^? pathLens 0 `shouldBe` Just (Seq.fromList [0, 0, 0, 0])
      newInstRoot ^? pathLens 1 `shouldBe` Just (Seq.fromList [0, 0, 1, 0])
      newInstRoot ^? widLens 0 `shouldBe` Just (WidgetId 0 (Seq.fromList [0, 0, 1, 0]))
      newInstRoot ^? widLens 1 `shouldBe` Just (WidgetId 0 (Seq.fromList [0, 0, 0, 0]))
  
    where
      wenv = mockWenv def
      handleEvent
        :: WidgetEnv TestModel ()
        -> WidgetNode TestModel ()
        -> TestModel
        -> ()
        -> [EventResponse TestModel () TestModel ()]
      handleEvent wenv node model evt = []
      buildUI1 wenv model = hstack [
          vstack [
            textField text1
          ] `nodeKey` "localTxt1",
          vstack [
            textField text1
          ] `nodeKey` "localTxt2"
        ]
      buildUI2 wenv model = hstack [
          vstack [
            textField text1
          ] `nodeKey` "localTxt2",
          vstack [
            textField text1
          ] `nodeKey` "localTxt1"
        ]
      cmpNode1 = composite "main" id buildUI1 handleEvent
      cmpNode2 = composite_ "main" id buildUI2 handleEvent [mergeRequired (\_ _ _ -> True)]
      evts1 = [evtK keyTab, evtT "aacc", moveCharL, moveCharL]
      (wenv1, root1, _) = fst $ nodeHandleEvents wenv WInit evts1 cmpNode1
      cntNodeM = nodeMerge wenv1 cmpNode2 root1
      evts2 = [evtK keyTab, evtK keyTab, evtT "bb"]
      (wenv2, root2, _) = fst $ nodeHandleEvents wenv1 WNoInit evts2 cntNodeM
      newInstRoot = widgetGetInstanceTree (root2 ^. L.widget) wenv1 root2
  
  handleEventLocalKeyRemoveItem :: Spec
  handleEventLocalKeyRemoveItem = describe "handleEventLocalKeyRemoveItem" $
    it "should remove an element and keep the correct keys" $ do
      getKeys oldInstRoot `shouldBe` ["key0", "key1", "key2", "key3"]
      getKeys newInstRoot `shouldBe` ["key0", "key2", "key3"]
      length (newCtx ^. L.widgetPaths) `shouldBe` 2
  
    where
      initModel = def & items .~ [0..3]
      wenv = mockWenv initModel
      handleEvent
        :: WidgetEnv TestModel MainEvt
        -> WidgetNode TestModel MainEvt
        -> TestModel
        -> MainEvt
        -> [EventResponse TestModel MainEvt TestModel MainEvt]
      handleEvent wenv node model evt = case evt of
        MainBtnClicked -> [Model $ model & items .~ [0, 2, 3]]
        _ -> []
      buildUI wenv model = vstack (button "Button" MainBtnClicked : (keyedLabel <$> model ^. items))
      keyedLabel idx = label "Test" `nodeKey` ("key" <> showt idx)
      node = composite "main" id buildUI handleEvent
      evts = [evtClick (Point 100 10)]
      oldNode = nodeInit wenv node
      ((_, newRoot, _), newCtx) = nodeHandleEvents wenv WInit evts node
      oldInstRoot = widgetGetInstanceTree (oldNode ^. L.widget) wenv oldNode
      newInstRoot = widgetGetInstanceTree (newRoot ^. L.widget) wenv newRoot
      getKeys inst = inst ^.. L.children . ix 0 . L.children . folded . L.info . L.key . _Just . L._WidgetKey
  
  handleMessage :: Spec
  handleMessage = describe "handleMessage" $ do
    it "should not generate an event if clicked outside" $ do
      model [evtClick (Point 50 10)] ^. child . message `shouldBe` "Test"
  
    where
      wenv = mockWenv def
      msg :: String
      msg = "Test"
      path = Seq.fromList [0, 0, 1, 0, 0]
      handleChild
        :: WidgetEnv ChildModel ChildEvt
        -> WidgetNode ChildModel ChildEvt
        -> ChildModel
        -> ChildEvt
        -> [EventResponse ChildModel ChildEvt MainModel MainEvt]
      handleChild wenv node model evt = case evt of
        ChildMessage m -> [Model (model & message .~ m)]
        _ -> []
      buildChild wenv model = vstack [ msgWidget ]
      handleEvent
        :: WidgetEnv MainModel MainEvt
        -> WidgetNode MainModel MainEvt
        -> MainModel
        -> MainEvt
        -> [EventResponse MainModel MainEvt MainModel ()]
      handleEvent wenv node model evt = [Request (SendMessage wid msg)] where
        wnis = wenv ^. L.findBranchByPath $ path
        wid = maybe def (^. L.widgetId) (Seq.lookup (length wnis - 1) wnis)
      buildUI wenv model = vstack [
          button "Start" MainBtnClicked,
          composite "child" child buildChild handleChild
        ]
      cmpNode = composite "main" id buildUI handleEvent
      model es = nodeHandleEventModel wenv es cmpNode
  
  findByPoint :: Spec
  findByPoint = describe "findByPoint" $ do
    it "should return Nothing" $
      wni emptyPath (Point 3000 3000) `shouldBe` Nothing
  
    it "should return item number 5" $ do
      let res = wni emptyPath (Point 320 240)
      res ^? _Just . L.path ^.. traverse . traverse `shouldBe` [0, 0, 0, 0, 1, 1]
  
    it "should return item number 8" $ do
      let res = wni emptyPath (Point 600 240)
      res ^? _Just . L.path ^.. traverse . traverse `shouldBe` [0, 0, 0, 0, 1, 2]
  
    it "should return background item" $ do
      let res = wni emptyPath (Point 560 400)
      res ^? _Just . L.path ^.. traverse . traverse `shouldBe` [0, 0, 0, 1]
  
    it "should return item number 9 when starting from the second level" $ do
      let res = wni (Seq.fromList [0, 0]) (Point 560 340)
      res ^? _Just . L.path ^.. traverse . traverse `shouldBe` [0, 0, 0, 0, 2, 2, 0, 1]
  
    it "should return Nothing if start path is not valid" $ do
      let res = wni (Seq.fromList [0, 1]) (Point 600 400)
      res `shouldBe` Nothing
  
    where
      wenv = mockWenvEvtUnit def
      cmpNode = findByHelperUI
      wni start point = res where
        inode = nodeInit wenv cmpNode
        res = widgetFindByPoint (inode ^. L.widget) wenv inode start point
  
  findByPath :: Spec
  findByPath = describe "findByPath" $ do
    it "should return Nothing" $ do
      wni emptyPath `shouldBe` Nothing
      wni (Seq.fromList [1]) `shouldBe` Nothing
      wni (Seq.fromList [0, 100]) `shouldBe` Nothing
  
    it "should return item number 5" $ do
      let res = wni (Seq.fromList [0, 0, 0, 0, 1, 1])
      roundRectUnits <$> res ^? _Just . L.viewport `shouldBe` Just (Rect 213 160 213 160)
  
    it "should return item number 8" $ do
      let res = wni (Seq.fromList [0, 0, 0, 0, 1, 2])
      roundRectUnits <$> res ^? _Just . L.viewport `shouldBe` Just (Rect 427 160 213 160)
  
    it "should return background item" $ do
      let res = wni (Seq.fromList [0, 0, 0, 1])
      roundRectUnits <$> res ^? _Just . L.viewport `shouldBe` Just (Rect 0 0 640 480)
  
    it "should return item number 9 when starting from the second level" $ do
      let res = wni (Seq.fromList [0, 0, 0, 0, 2, 2, 0, 1])
      roundRectUnits <$> res ^? _Just . L.viewport `shouldBe` Just (Rect 427 340 213 20)
  
    where
      wenv = mockWenvEvtUnit def
      cmpNode = findByHelperUI
      wni path = res where
        inode = nodeInit wenv cmpNode
        res = findChildNodeInfoByPath wenv inode path
  
  findNextFocus :: Spec
  findNextFocus = describe "findNextFocus" $ do
    it "should return the first textfield" $ do
      let res = wni FocusFwd emptyPath
      roundRectUnits <$> res ^? _Just . L.viewport `shouldBe` Just (Rect 0 0 213 160)
      res ^? _Just . L.path `shouldBe` Just (Seq.fromList [0, 0, 0, 0, 0, 0])
  
    it "should return the second textfield" $ do
      let res = wni FocusFwd (Seq.fromList [0, 0, 0, 0, 0, 0])
      roundRectUnits <$> res ^? _Just . L.viewport `shouldBe` Just (Rect 213 160 213 160)
      res ^? _Just . L.path `shouldBe` Just (Seq.fromList [0, 0, 0, 0, 1, 1])
  
    it "should return the third textfield" $ do
      let res = wni FocusFwd (Seq.fromList [0, 0, 0, 0, 1, 1])
      roundRectUnits <$> res ^? _Just . L.viewport `shouldBe` Just (Rect 427 360 213 20)
      res ^? _Just . L.path `shouldBe` Just (Seq.fromList [0, 0, 0, 0, 2, 2, 0, 2])
  
    it "should return the third textfield (starts backwards)" $ do
      let res = wni FocusBwd emptyPath
      roundRectUnits <$> res ^? _Just . L.viewport `shouldBe` Just (Rect 427 360 213 20)
      res ^? _Just . L.path `shouldBe` Just (Seq.fromList [0, 0, 0, 0, 2, 2, 0, 2])
  
    it "should return the second textfield (starts backwards)" $ do
      let res = wni FocusBwd (Seq.fromList [0, 0, 0, 0, 2, 2, 0, 2])
      roundRectUnits <$> res ^? _Just . L.viewport `shouldBe` Just (Rect 213 160 213 160)
      res ^? _Just . L.path `shouldBe` Just (Seq.fromList [0, 0, 0, 0, 1, 1])
  
    it "should return the first textfield (starts backwards)" $ do
      let res = wni FocusBwd (Seq.fromList [0, 0, 0, 0, 1, 1])
      roundRectUnits <$> res ^? _Just . L.viewport `shouldBe` Just (Rect 0 0 213 160)
      res ^? _Just . L.path `shouldBe` Just (Seq.fromList [0, 0, 0, 0, 0, 0])
  
    where
      wenv = mockWenvEvtUnit def
      cmpNode = findByHelperUI
      wni dir start = res where
        inode = nodeInit wenv cmpNode
        res = widgetFindNextFocus (inode ^. L.widget) wenv inode dir start
  
  findByHelperUI :: Typeable ep => WidgetNode TestModel ep
  findByHelperUI = composite "main" id buildUI handleEvent where
    handleEvent wenv node model evt = []
    buildLabels :: WidgetEnv TestModel () -> TestModel -> WidgetNode TestModel ()
    buildLabels wenv model = vstack_ [ignoreEmptyArea] [
        label "a", label "b", textField text1
      ]
    cmpLabels = composite "main" id buildLabels handleEvent
    buildUI :: WidgetEnv TestModel () -> TestModel -> WidgetNode TestModel ()
    buildUI wenv model = box_ [ignoreEmptyArea, expandContent] $
      zstack_ [onlyTopActive_ False] [
        label "Background",
        vgrid [
            hgrid [ textField text1, label "2", label "3" ],
            hgrid [ label "4", textField text1, label "6" ],
            hgrid [ label "7", label "8", cmpLabels ]
          ]
      ]
  
  mergeUserResize :: Spec
  mergeUserResize = describe "merge resize" $ do
    it "should not generate a request if user size did not change" $ do
      let result = mergeSizeReq [] []
      find isResizeWidgets (result ^. L.requests) `shouldBe` Nothing
  
    it "should generate a ResizeWidgets request if user size changed" $ do
      let result = mergeSizeReq [width 100] []
      find isResizeWidgets (result ^. L.requests) `shouldNotBe` Nothing
  
    it "should generate a ResizeWidgets request if user size changed" $ do
      let result = mergeSizeReq [width 100] [width 200]
      find isResizeWidgets (result ^. L.requests) `shouldNotBe` Nothing
  
    where
      wenv = mockWenv def
      handleEvent
        :: WidgetEnv MainModel MainEvt
        -> WidgetNode MainModel MainEvt
        -> MainModel
        -> MainEvt
        -> [EventResponse MainModel MainEvt MainModel MainEvt]
      handleEvent wenv node model evt = []
      buildUI wenv model = spacer
      oldNode = composite "main" id buildUI handleEvent
      newNode = composite_ "main" id buildUI handleEvent [mergeRequired (const . const . const True)]
      mergeSizeReq oldStyle newStyle = result where
        oldNode2 = nodeInit wenv $
          oldNode `styleBasic` oldStyle
        newNode2 = newNode
          `styleBasic` newStyle
        result = widgetMerge (newNode2 ^. L.widget) wenv newNode2 oldNode2
  
  getSizeReq :: Spec
  getSizeReq = describe "getSizeReq" $ do
    it "should return width = Fixed 70" $
      sizeReqW `shouldBe` fixedSize 70
  
    it "should return height = Fixed 20" $
      sizeReqH `shouldBe` fixedSize 40
  
    where
      wenv = mockWenvEvtUnit ()
      handleEvent wenv node model evt = []
      buildUI :: WidgetEnv () () -> () -> WidgetNode () ()
      buildUI wenv model = vstack [
          label "label 1",
          label "label 2"
        ]
      cmpNode = composite "main" id buildUI handleEvent
      (sizeReqW, sizeReqH) = nodeGetSizeReq wenv cmpNode
  
  resize :: Spec
  resize = describe "resize" $ do
    it "should have the provided viewport size" $
      viewport `shouldBe` vp
  
    it "should assign the same viewport size to its child" $
      childrenVp `shouldBe` Seq.singleton cvp1
  
    where
      wenv = mockWenvEvtUnit ()
        & L.windowSize .~ Size 640 480
      vp   = Rect 0 0 640 480
      cvp1 = Rect 0 0 640 480
      handleEvent wenv node model evt = []
      buildUI :: WidgetEnv () () -> () -> WidgetNode () ()
      buildUI wenv model = hstack []
      cmpNode = composite "main" id buildUI handleEvent
      tmpNode = nodeInit wenv cmpNode
      newNode = widgetGetInstanceTree (tmpNode ^. L.widget) wenv tmpNode
      viewport = newNode ^. L.info . L.viewport
      childrenVp = (^. L.info . L.viewport) <$> newNode ^. L.children
  