content: {-|
  Module      : Monomer.Widgets.Util.TextSpec
  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 Text handling.
  -}
  module Monomer.Widgets.Util.TextSpec (spec) where
  
  import Control.Lens ((^.), ix)
  import Data.Default
  import Data.Text (Text)
  import Test.Hspec
  
  import qualified Data.Sequence as Seq
  
  import Monomer.Core
  import Monomer.Core.Combinators
  import Monomer.Graphics
  import Monomer.Widgets.Util.Text
  import Monomer.TestUtil
  
  import qualified Monomer.Lens as L
  
  spec :: Spec
  spec = describe "Text" $ do
    fitTextSingle
    fitTextMulti
    fitTextSpace
  
  fitTextSingle :: Spec
  fitTextSingle = describe "fitTextToSize single line" $ do
    it "should return the same empty text, trimmed (ellipsis)" $
      elpsTrim "" ^. ix 0 . L.text `shouldBe` ""
  
    it "should return the same empty text, untrimmed (ellipsis)" $
      elpsKeep "" ^. ix 0 . L.text `shouldBe` ""
  
    it "should return the same empty text, trimmed (clip)" $
      clipTrim "" ^. ix 0 . L.text `shouldBe` ""
  
    it "should return the same empty text, untrimmed (clip)" $
      clipKeep "" ^. ix 0 . L.text `shouldBe` ""
  
    it "should return the same text, trimmed, if it fits (ellipsis)" $
      elpsTrim "Text " ^. ix 0 . L.text `shouldBe` "Text"
  
    it "should return the same text, untrimmed, if it fits (ellipsis)" $
      elpsKeep "Text " ^. ix 0 . L.text `shouldBe` "Text "
  
    it "should return the same text, trimmed, if it fits (clip)" $
      clipTrim "Text " ^. ix 0 . L.text `shouldBe` "Text"
  
    it "should return the same text, untrimmed, if it fits (clip)" $
      clipKeep "Text " ^. ix 0 . L.text `shouldBe` "Text "
  
    it "should return text with ellipsis, trimmed, if it does not fit" $ do
      elpsTrim "This is longer\nMore" `shouldSatisfy` singleElement
      elpsTrim "This is longer\nMore" ^. ix 0 . L.text `shouldBe` "This is l..."
      elpsTrim "This is a bit longer\nMore" `shouldSatisfy` singleElement
      elpsTrim "This is a bit longer\nMore" ^. ix 0 . L.text `shouldBe` "This is a..."
  
    it "should return text with ellipsis, untrimmed, if it does not fit" $ do
      elpsKeep "This is longer\nMore" `shouldSatisfy` singleElement
      elpsKeep "This is longer\nMore" ^. ix 0 . L.text `shouldBe` "This is l..."
      elpsKeep "This is a bit longer\nMore" `shouldSatisfy` singleElement
      elpsKeep "This is a bit longer\nMore" ^. ix 0 . L.text `shouldBe` "This is a..."
  
    it "should return text clipped, trimmed, if it does not fit" $ do
      clipTrim "This is longer\nMore" `shouldSatisfy` singleElement
      clipTrim "This is longer\nMore" ^. ix 0 . L.text `shouldBe` "This is long"
      clipTrim "This is not a bit longer\nMore" `shouldSatisfy` singleElement
      clipTrim "This is not a bit longer\nMore" ^. ix 0 . L.text `shouldBe` "This is not"
  
    it "should return text clipped, untrimmed, if it does not fit" $ do
      clipKeep "This is longer\nMore" `shouldSatisfy` singleElement
      clipKeep "This is longer\nMore" ^. ix 0 . L.text `shouldBe` "This is long"
      clipKeep "This is not a bit longer\nMore" `shouldSatisfy` singleElement
      clipKeep "This is not a bit longer\nMore" ^. ix 0 . L.text `shouldBe` "This is not "
  
    where
      wenv = mockWenv ()
      fontMgr = wenv ^. L.fontManager
      style = def
      sizeE = Size 120 20
      sizeC = Size 120 10
      elpsTrim text = fitTextToSize fontMgr style Ellipsis SingleLine TrimSpaces Nothing sizeE text
      elpsKeep text = fitTextToSize fontMgr style Ellipsis SingleLine KeepSpaces Nothing sizeE text
      clipTrim text = fitTextToSize fontMgr style ClipText SingleLine TrimSpaces Nothing sizeC text
      clipKeep text = fitTextToSize fontMgr style ClipText SingleLine KeepSpaces Nothing sizeC text
      singleElement sq = Seq.length sq == 1
  
  fitTextMulti :: Spec
  fitTextMulti = describe "fitTextToSize multi line" $ do
    it "should return the same text, trimmed, if it fits" $ do
      elpsTrim "Text " `shouldSatisfy` elementCount 1
      elpsTrim "Text " ^. ix 0 . L.text `shouldBe` "Text"
  
    it "should return the same text, untrimmed, if it fits" $ do
      elpsKeep "Text " `shouldSatisfy` elementCount 1
      elpsKeep "Text " ^. ix 0 . L.text `shouldBe` "Text "
  
    -- Text.lines does not return an extra line if the last element is \n
    it "should return several empty lines" $ do
      elpsTrim_ sizeTall "Text\n\n\n\n" `shouldSatisfy` elementCount 4
      elpsKeep_ sizeTall "Text\n\n\n\n" `shouldSatisfy` elementCount 4
      clipTrim_ sizeTall "Text\n\n\n\n" `shouldSatisfy` elementCount 4
      clipKeep_ sizeTall "Text\n\n\n\n" `shouldSatisfy` elementCount 4
  
    it "should return several empty lines and one with text" $ do
      elpsTrim_ sizeTall "Text\n\n\n\nend" `shouldSatisfy` elementCount 5
      elpsKeep_ sizeTall "Text\n\n\n\nend" `shouldSatisfy` elementCount 5
      clipTrim_ sizeTall "Text\n\n\n\nend" `shouldSatisfy` elementCount 5
      clipKeep_ sizeTall "Text\n\n\n\nend" `shouldSatisfy` elementCount 5
  
    it "should return text with ellipsis, trimmed, if it does not fit" $ do
      elpsTrim "This is    really-long\nMore" `shouldSatisfy` elementCount 2
      elpsTrim "This is    really-long\nMore" ^. ix 0 . L.text `shouldBe` "This is"
      elpsTrim "This is    really-long\nMore" ^. ix 1 . L.text `shouldBe` "reall..."
      elpsTrim "This is    a tad bit longer\nMore" `shouldSatisfy` elementCount 2
      elpsTrim "This is    a tad bit longer\nMore" ^. ix 0 . L.text `shouldBe` "This is"
      elpsTrim "This is    a tad bit longer\nMore" ^. ix 1 . L.text `shouldBe` "a tad..."
  
    it "should return text with ellipsis, untrimmed, if it does not fit" $ do
      elpsKeep "This is    really-long\nMore" `shouldSatisfy` elementCount 2
      elpsKeep "This is    really-long\nMore" ^. ix 0 . L.text `shouldBe` "This is "
      elpsKeep "This is    really-long\nMore" ^. ix 1 . L.text `shouldBe` "   ..."
      elpsKeep "This is    a tad bit longer\nMore" `shouldSatisfy` elementCount 2
      elpsKeep "This is    a tad bit longer\nMore" ^. ix 0 . L.text `shouldBe` "This is "
      elpsKeep "This is    a tad bit longer\nMore" ^. ix 1 . L.text `shouldBe` "   a ..."
  
    it "should return text clipped, trimmed, if it does not fit" $ do
      clipTrim "This is    really-long\nMore" `shouldSatisfy` elementCount 3
      clipTrim "This is    really-long\nMore" ^. ix 0 . L.text `shouldBe` "This is"
      clipTrim "This is    really-long\nMore" ^. ix 1 . L.text `shouldBe` "really-long"
      clipTrim "This is    really-long\nMore" ^. ix 2 . L.text `shouldBe` "More"
      clipTrim "This is    a tad bit longer\nMore" `shouldSatisfy` elementCount 3
      clipTrim "This is    a tad bit longer\nMore" ^. ix 0 . L.text `shouldBe` "This is"
      clipTrim "This is    a tad bit longer\nMore" ^. ix 1 . L.text `shouldBe` "a tad"
      clipTrim "This is    a tad bit longer\nMore" ^. ix 2 . L.text `shouldBe` "bit"
  
    it "should return text clipped, untrimmed, if it does not fit" $ do
      clipKeep "This is    really-long\nMore" `shouldSatisfy` elementCount 3
      clipKeep "This is    really-long\nMore" ^. ix 0 . L.text `shouldBe` "This is "
      clipKeep "This is    really-long\nMore" ^. ix 1 . L.text `shouldBe` "   "
      clipKeep "This is    really-long\nMore" ^. ix 2 . L.text `shouldBe` "really-long"
      clipKeep "This is    a tad bit longer\nMore" `shouldSatisfy` elementCount 3
      clipKeep "This is    a tad bit longer\nMore" ^. ix 0 . L.text `shouldBe` "This is "
      clipKeep "This is    a tad bit longer\nMore" ^. ix 1 . L.text `shouldBe` "   a tad"
      clipKeep "This is    a tad bit longer\nMore" ^. ix 2 . L.text `shouldBe` " bit "
  
    it "should return text broken even in the middle of words, clipped, trimmed, if it does not fit" $ do
      breakOnChar "This is    really-long\nMore" `shouldSatisfy` elementCount 3
      breakOnChar "This is    really-long\nMore" ^. ix 0 . L.text `shouldBe` "This is"
      breakOnChar "This is    really-long\nMore" ^. ix 1 . L.text `shouldBe` "reall"
      breakOnChar "This is    really-long\nMore" ^. ix 2 . L.text `shouldBe` "y-long"
      breakOnChar "This is    a tad bit longer\nMore" `shouldSatisfy` elementCount 3
      breakOnChar "This is    a tad bit longer\nMore" ^. ix 0 . L.text `shouldBe` "This is"
      breakOnChar "This is    a tad bit longer\nMore" ^. ix 1 . L.text `shouldBe` "a tad"
      breakOnChar "This is    a tad bit longer\nMore" ^. ix 2 . L.text `shouldBe` "bit lon"
  
    where
      wenv = mockWenv ()
      fontMgr = wenv ^. L.fontManager
      style = def
      sizeE = Size 80 40
      sizeC = Size 80 50
      sizeTall = Size 80 200
      elpsTrim text = elpsTrim_ sizeE text
      elpsKeep text = elpsKeep_ sizeE text
      clipTrim text = clipTrim_ sizeC text
      clipKeep text = clipKeep_ sizeC text
      elpsTrim_ size text = fitTextToSize fontMgr style Ellipsis MultiLine TrimSpaces Nothing size text
      elpsKeep_ size text = fitTextToSize fontMgr style Ellipsis MultiLine KeepSpaces Nothing size text
      clipTrim_ size text = fitTextToSize fontMgr style ClipText MultiLine TrimSpaces Nothing size text
      clipKeep_ size text = fitTextToSize fontMgr style ClipText MultiLine KeepSpaces Nothing size text
      breakOnChar text = fitTextToSize fontMgr (textLineBreak OnCharacters) ClipText MultiLine TrimSpaces Nothing sizeC text
      elementCount count sq = Seq.length sq == count
  
  fitTextSpace :: Spec
  fitTextSpace = describe "fitTextToWidth with spacing" $ do
    it "should fit text in (100, 80)" $
      textSize style longText `shouldBe` Size 100 80
  
    it "should fit text in (96, 120)" $
      textSize styleH longText `shouldBe` Size 96 120
  
    it "should fit text in (100, 95)" $
      textSize styleV longText `shouldBe` Size 100 95
  
    it "should fit text in (96, 145)" $
      textSize styleHV longText `shouldBe` Size 96 145
  
    where
      wenv = mockWenv ()
      fontMgr = wenv ^. L.fontManager
      style = def
      styleH = textSpaceH 2
      styleV = textSpaceV 5
      styleHV = textSpaceH 2 <> textSpaceV 5
      longText = "This is a long piece of text to test space"
      textSize style text = getTextLinesSize (fitTextToWidth fontMgr style 100 TrimSpaces text)
  