module Test.GitHUD.Terminal.Prompt (
  terminalPromptTests
  ) where

import Test.Tasty
import Test.Tasty.HUnit

import Control.Monad.Reader (runReader)
import Control.Monad.Writer (runWriterT)

import GitHUD.Config.Types
import GitHUD.Git.Types
import GitHUD.Terminal.Prompt
import GitHUD.Terminal.Types
import GitHUD.Types

allPrompts :: [Shell]
allPrompts = [ZSH, BASH, TMUX, NONE, Other]

terminalPromptTests :: TestTree
terminalPromptTests = testGroup "Terminal Prompt Test"
  [ testResetPromptAtBeginning
    , testAddGitRepoIndicator
    , testAddNoTrackedUpstreamIndicator
    , testAddMergeBranchCommits
    , testAddLocalBranchName
    , testAddLocalCommits
    , testAddRepoState
    , testAddStashes
    , testPartialPrompt
  ]

testResetPromptAtBeginning :: TestTree
testResetPromptAtBeginning = testGroup "#resetPromptAtBeginning"
 [   testCase "ZSH: Should start the prompt with a reset color control sequence" $
       testWriterWithConfig (zeroOutputConfig ZSH) resetPromptAtBeginning @?= "%{\x1b[0;39m%}"
   , testCase "Other: Should start the prompt with a reset color control sequence" $
       testWriterWithConfig (zeroOutputConfig Other) resetPromptAtBeginning @?= "\x1b[0;39m"
   , testCase "TMUX: Should start the prompt with a reset color control sequence" $
       testWriterWithConfig (zeroOutputConfig TMUX) resetPromptAtBeginning @?= "#[fg=default]"
   , testCase "NONE: Should start the prompt with a reset color control sequence" $
       testWriterWithConfig (zeroOutputConfig NONE) resetPromptAtBeginning @?= ""
 ]

_testAddGitDefaultRepoIndicator :: Shell -> TestTree
_testAddGitDefaultRepoIndicator shell =
  testCase ((show shell) ++ ": default config: hardcoded character") $
    testWriterWithConfig (zeroOutputConfig shell) addGitRepoIndicator @?= "ᚴ "

_testAddGitCustomRepoIndicator :: Shell -> TestTree
_testAddGitCustomRepoIndicator shell =
  testCase ((show shell) ++ ": custom config: hardcoded character") $
    testWriterWithConfig
      (buildOutputConfig shell zeroGitRepoState $ defaultConfig { confRepoIndicator = "indic" })
      addGitRepoIndicator
    @?= "indic "

testAddGitRepoIndicator :: TestTree
testAddGitRepoIndicator = testGroup "#addGitRepoIndicator"
  [ testGroup "Default Config" $ fmap _testAddGitDefaultRepoIndicator allPrompts
    , testGroup "Custom Config" $ fmap _testAddGitCustomRepoIndicator allPrompts
  ]

customConfigNoTrackedUpstreamIndicator :: Config
customConfigNoTrackedUpstreamIndicator = defaultConfig {
  confNoTrackedUpstreamString = "foo"
  , confNoTrackedUpstreamStringColor = Cyan
  , confNoTrackedUpstreamStringIntensity = Dull
  , confNoTrackedUpstreamIndicator = "bar"
  , confNoTrackedUpstreamIndicatorColor = Green
  , confNoTrackedUpstreamIndicatorIntensity = Dull
}

_testUpstreamIndicatorWithUpstreamDefaultConfig :: Shell -> TestTree
_testUpstreamIndicatorWithUpstreamDefaultConfig shell =
  testCase ((show shell) ++ ": with an upstream") $
    testWriterWithConfig
            (buildOutputConfig shell (zeroGitRepoState { gitRemoteTrackingBranch = "foo" }) defaultConfig)
            addNoTrackedUpstreamIndicator
          @?= ""

_testUpstreamIndicatorWithUpstreamCustomConfig :: Shell -> TestTree
_testUpstreamIndicatorWithUpstreamCustomConfig shell =
  testCase ((show shell) ++ ": with an upstream") $
    testWriterWithConfig
            (buildOutputConfig shell (zeroGitRepoState { gitRemoteTrackingBranch = "foo" }) customConfigNoTrackedUpstreamIndicator)
            addNoTrackedUpstreamIndicator
          @?= ""

testAddNoTrackedUpstreamIndicator :: TestTree
testAddNoTrackedUpstreamIndicator = testGroup "#addTrackedUpstreamIndicator"
  [ testGroup "Default Config - upstream" $ fmap _testUpstreamIndicatorWithUpstreamDefaultConfig allPrompts
    , testGroup "Custom Config - upstream" $ fmap _testUpstreamIndicatorWithUpstreamCustomConfig allPrompts
    , testGroup "Default Config - no upstream"
      [ testCase "ZSH: with no upstream" $
          testWriterWithConfig
            (zeroOutputConfig ZSH) addNoTrackedUpstreamIndicator
          @?= "%{\x1b[1;31m%}upstream%{\x1b[0;39m%} %{\x1b[1;31m%}\9889%{\x1b[0;39m%} "

      , testCase "Other: with no upstream" $
          testWriterWithConfig
            (zeroOutputConfig Other) addNoTrackedUpstreamIndicator
          @?= "\x1b[1;31mupstream\x1b[0;39m \x1b[1;31m\9889\x1b[0;39m "

      , testCase "TMUX: with no upstream" $
          testWriterWithConfig
            (zeroOutputConfig TMUX) addNoTrackedUpstreamIndicator
          @?= "#[fg=brightred]upstream#[fg=default] #[fg=brightred]\9889#[fg=default] "

      , testCase "NONE: with no upstream" $
          testWriterWithConfig
            (zeroOutputConfig NONE) addNoTrackedUpstreamIndicator
          @?= "upstream \9889 "
      ]
    , testGroup "Custom Config - no upstream"
      [ testCase "ZSH: with no upstream" $
          testWriterWithConfig
            (buildOutputConfig ZSH (zeroGitRepoState) customConfigNoTrackedUpstreamIndicator)
            addNoTrackedUpstreamIndicator
          @?= "%{\x1b[36m%}foo%{\x1b[0;39m%} %{\x1b[32m%}bar%{\x1b[0;39m%} "

      , testCase "Other: with no upstream" $
          testWriterWithConfig
            (buildOutputConfig Other (zeroGitRepoState) customConfigNoTrackedUpstreamIndicator)
            addNoTrackedUpstreamIndicator
          @?= "\x1b[36mfoo\x1b[0;39m \x1b[32mbar\x1b[0;39m "

      , testCase "TMUX: with no upstream" $
          testWriterWithConfig
            (buildOutputConfig TMUX (zeroGitRepoState) customConfigNoTrackedUpstreamIndicator)
            addNoTrackedUpstreamIndicator
          @?= "#[fg=cyan]foo#[fg=default] #[fg=green]bar#[fg=default] "

      , testCase "NONE: with no upstream" $
          testWriterWithConfig
            (buildOutputConfig NONE (zeroGitRepoState) customConfigNoTrackedUpstreamIndicator)
            addNoTrackedUpstreamIndicator
          @?= "foo bar "
      ]
  ]

customConfigMergeBranchCommits :: Config
customConfigMergeBranchCommits = defaultConfig {
  confMergeBranchCommitsIndicator = "foo"
  , confMergeBranchCommitsOnlyPull = "pull"
  , confMergeBranchCommitsOnlyPush = "push"
  , confMergeBranchCommitsBothPullPush = "pull-push"
}

testAddMergeBranchCommits :: TestTree
testAddMergeBranchCommits = testGroup "#addMergeBranchCommits"
  [ testGroup "Default Config"
    [ testCase "ZSH: commits to pull" $
        testMergeBranchCommitsToPull ZSH defaultConfig @?=
        "\120366 %{\x1b[1;32m%}\8594%{\x1b[0;39m%} 2 "

    , testCase "ZSH: commits to push" $
        testMergeBranchCommitsToPush ZSH defaultConfig @?=
        "\120366 %{\x1b[1;32m%}\8592%{\x1b[0;39m%} 2 "

    , testCase "ZSH: commits to pull and to push" $
        testMergeBranchCommitsToPushAndPull ZSH defaultConfig @?=
        "\120366 4%{\x1b[1;32m%}\8644%{\x1b[0;39m%}4 "

    , testCase "Other: commits to pull" $
        testMergeBranchCommitsToPull Other defaultConfig @?=
        "\120366 \x1b[1;32m\8594\x1b[0;39m 2 "

    , testCase "Other: commits to push" $
        testMergeBranchCommitsToPush Other defaultConfig @?=
        "\120366 \x1b[1;32m\8592\x1b[0;39m 2 "

    , testCase "Other: commits to pull and to push" $
        testMergeBranchCommitsToPushAndPull Other defaultConfig @?=
        "\120366 4\x1b[1;32m\8644\x1b[0;39m4 "

    , testCase "TMUX: commits to pull" $
        testMergeBranchCommitsToPull TMUX defaultConfig @?=
        "\120366 #[fg=brightgreen]\8594#[fg=default] 2 "

    , testCase "TMUX: commits to push" $
        testMergeBranchCommitsToPush TMUX defaultConfig @?=
        "\120366 #[fg=brightgreen]\8592#[fg=default] 2 "

    , testCase "TMUX: commits to pull and to push" $
        testMergeBranchCommitsToPushAndPull TMUX defaultConfig @?=
        "\120366 4#[fg=brightgreen]\8644#[fg=default]4 "

    , testCase "NONE: commits to pull" $
        testMergeBranchCommitsToPull NONE defaultConfig @?=
        "\120366 \8594 2 "

    , testCase "NONE: commits to push" $
        testMergeBranchCommitsToPush NONE defaultConfig @?=
        "\120366 \8592 2 "

    , testCase "NONE: commits to pull and to push" $
        testMergeBranchCommitsToPushAndPull NONE defaultConfig @?=
        "\120366 4\8644\&4 "
    ]

    , testGroup "Custom Config"
        [ testCase "ZSH: commits to pull" $
            testMergeBranchCommitsToPull ZSH customConfigMergeBranchCommits @?=
            "foo %{\x1b[1;32m%}pull%{\x1b[0;39m%} 2 "

        , testCase "ZSH: commits to push" $
            testMergeBranchCommitsToPush ZSH customConfigMergeBranchCommits @?=
            "foo %{\x1b[1;32m%}push%{\x1b[0;39m%} 2 "

        , testCase "ZSH: commits to pull and to push" $
            testMergeBranchCommitsToPushAndPull ZSH customConfigMergeBranchCommits @?=
            "foo 4%{\x1b[1;32m%}pull-push%{\x1b[0;39m%}4 "

        , testCase "Other: commits to pull" $
            testMergeBranchCommitsToPull Other customConfigMergeBranchCommits @?=
            "foo \x1b[1;32mpull\x1b[0;39m 2 "

        , testCase "Other: commits to push" $
            testMergeBranchCommitsToPush Other customConfigMergeBranchCommits @?=
            "foo \x1b[1;32mpush\x1b[0;39m 2 "

        , testCase "Other: commits to pull and to push" $
            testMergeBranchCommitsToPushAndPull Other customConfigMergeBranchCommits @?=
            "foo 4\x1b[1;32mpull-push\x1b[0;39m4 "

        , testCase "TMUX: commits to pull" $
            testMergeBranchCommitsToPull TMUX customConfigMergeBranchCommits @?=
            "foo #[fg=brightgreen]pull#[fg=default] 2 "

        , testCase "TMUX: commits to push" $
            testMergeBranchCommitsToPush TMUX customConfigMergeBranchCommits @?=
            "foo #[fg=brightgreen]push#[fg=default] 2 "

        , testCase "TMUX: commits to pull and to push" $
            testMergeBranchCommitsToPushAndPull TMUX customConfigMergeBranchCommits @?=
            "foo 4#[fg=brightgreen]pull-push#[fg=default]4 "

        , testCase "NONE: commits to pull" $
            testMergeBranchCommitsToPull NONE customConfigMergeBranchCommits @?=
            "foo pull 2 "

        , testCase "NONE: commits to push" $
            testMergeBranchCommitsToPush NONE customConfigMergeBranchCommits @?=
            "foo push 2 "

        , testCase "NONE: commits to pull and to push" $
            testMergeBranchCommitsToPushAndPull NONE customConfigMergeBranchCommits @?=
            "foo 4pull-push4 "
        ]
  ]

customConfigLocalBranchName :: Config
customConfigLocalBranchName = defaultConfig {
  confLocalBranchColor = Cyan
  , confLocalDetachedColor = Magenta
  , confLocalBranchIntensity = Dull
  , confLocalDetachedIntensity = Dull
  , confLocalBranchNamePrefix = "{"
  , confLocalBranchNameSuffix = "}"
  , confLocalDetachedPrefix = "det#!"
}

testAddLocalBranchName :: TestTree
testAddLocalBranchName = testGroup "#addLocalBranchName"
  [ testGroup "Default Config"
    [   testCase "ZSH: should display the name of the current branch if we are at the HEAD of any" $
          testWriterWithConfig
            (buildOutputConfig ZSH (zeroGitRepoState { gitLocalBranch = "foo" }) defaultConfig)
            addLocalBranchName
          @?= "[%{\x1b[0;39m%}foo%{\x1b[0;39m%}] "

        , testCase "ZSH: should display the current commit tag if we are not on one" $
          testWriterWithConfig
            (buildOutputConfig ZSH (zeroGitRepoState { gitCommitTag = "v1.1.1" }) defaultConfig)
            addLocalBranchName
          @?= "[%{\x1b[1;33m%}detached@v1.1.1%{\x1b[0;39m%}] "

        , testCase "ZSH: should display the current commit SHA if we are not on a branch's HEAD" $
          testWriterWithConfig
            (buildOutputConfig ZSH (zeroGitRepoState { gitCommitShortSHA = "3d25ef" }) defaultConfig)
            addLocalBranchName
          @?= "[%{\x1b[1;33m%}detached@3d25ef%{\x1b[0;39m%}] "

        , testCase "ZSH: should prefer the current tag over the commit SHA if we are not on a branch's HEAD" $
          testWriterWithConfig
            (buildOutputConfig ZSH (zeroGitRepoState { gitCommitShortSHA = "3d25ef", gitCommitTag = "v1.2.3" }) defaultConfig)
            addLocalBranchName
          @?= "[%{\x1b[1;33m%}detached@v1.2.3%{\x1b[0;39m%}] "

        , testCase "Other: should display the name of the current branch if we are at the HEAD of any" $
          testWriterWithConfig
            (buildOutputConfig Other (zeroGitRepoState { gitLocalBranch = "foo" }) defaultConfig)
            addLocalBranchName
          @?= "[\x1b[0;39mfoo\x1b[0;39m] "

        , testCase "Other: should display the current commit tog if we are on one" $
          testWriterWithConfig
            (buildOutputConfig Other (zeroGitRepoState { gitCommitTag = "v1.1.1" }) defaultConfig)
            addLocalBranchName
          @?= "[\x1b[1;33mdetached@v1.1.1\x1b[0;39m] "

        , testCase "Other: should display the current commit SHA if we are not on a branch's HEAD" $
          testWriterWithConfig
            (buildOutputConfig Other (zeroGitRepoState { gitCommitShortSHA = "3d25ef" }) defaultConfig)
            addLocalBranchName
          @?= "[\x1b[1;33mdetached@3d25ef\x1b[0;39m] "

        , testCase "Other: should prefer the current tag over the commit SHA if we are not on a branch's HEAD" $
          testWriterWithConfig
            (buildOutputConfig Other (zeroGitRepoState { gitCommitShortSHA = "3d25ef", gitCommitTag = "v1.2.3" }) defaultConfig)
            addLocalBranchName
          @?= "[\x1b[1;33mdetached@v1.2.3\x1b[0;39m] "

        , testCase "TMUX: should display the name of the current branch if we are at the HEAD of any" $
          testWriterWithConfig
            (buildOutputConfig TMUX (zeroGitRepoState { gitLocalBranch = "foo" }) defaultConfig)
            addLocalBranchName
          @?= "[#[fg=default]foo#[fg=default]] "

        , testCase "TMUX: should display the current commit tog if we are on one" $
          testWriterWithConfig
            (buildOutputConfig TMUX (zeroGitRepoState { gitCommitTag = "v1.1.1" }) defaultConfig)
            addLocalBranchName
          @?= "[#[fg=brightyellow]detached@v1.1.1#[fg=default]] "

        , testCase "TMUX: should display the current commit SHA if we are not on a branch's HEAD" $
          testWriterWithConfig
            (buildOutputConfig TMUX (zeroGitRepoState { gitCommitShortSHA = "3d25ef" }) defaultConfig)
            addLocalBranchName
          @?= "[#[fg=brightyellow]detached@3d25ef#[fg=default]] "

        , testCase "TMUX: should prefer the current tag over the commit SHA if we are not on a branch's HEAD" $
          testWriterWithConfig
            (buildOutputConfig TMUX (zeroGitRepoState { gitCommitShortSHA = "3d25ef", gitCommitTag = "v1.2.3" }) defaultConfig)
            addLocalBranchName
          @?= "[#[fg=brightyellow]detached@v1.2.3#[fg=default]] "

        , testCase "NONE: should display the name of the current branch if we are at the HEAD of any" $
          testWriterWithConfig
            (buildOutputConfig NONE (zeroGitRepoState { gitLocalBranch = "foo" }) defaultConfig)
            addLocalBranchName
          @?= "[foo] "

        , testCase "NONE: should display the current commit tog if we are on one" $
          testWriterWithConfig
            (buildOutputConfig NONE (zeroGitRepoState { gitCommitTag = "v1.1.1" }) defaultConfig)
            addLocalBranchName
          @?= "[detached@v1.1.1] "

        , testCase "NONE: should display the current commit SHA if we are not on a branch's HEAD" $
          testWriterWithConfig
            (buildOutputConfig NONE (zeroGitRepoState { gitCommitShortSHA = "3d25ef" }) defaultConfig)
            addLocalBranchName
          @?= "[detached@3d25ef] "

        , testCase "NONE: should prefer the current tag over the commit SHA if we are not on a branch's HEAD" $
          testWriterWithConfig
            (buildOutputConfig NONE (zeroGitRepoState { gitCommitShortSHA = "3d25ef", gitCommitTag = "v1.2.3" }) defaultConfig)
            addLocalBranchName
          @?= "[detached@v1.2.3] "
    ]
    , testGroup "Custom Config"
    [   testCase "ZSH: should display the name of the current branch if we are at the HEAD of any" $
          testWriterWithConfig
            (buildOutputConfig ZSH (zeroGitRepoState { gitLocalBranch = "foo" }) customConfigLocalBranchName)
            addLocalBranchName
          @?= "{%{\x1b[36m%}foo%{\x1b[0;39m%}} "

        , testCase "ZSH: should display the current commit tag if we are not on one" $
          testWriterWithConfig
            (buildOutputConfig ZSH (zeroGitRepoState { gitCommitTag = "v1.1.1" }) customConfigLocalBranchName)
            addLocalBranchName
          @?= "{%{\x1b[35m%}det#!v1.1.1%{\x1b[0;39m%}} "

        , testCase "ZSH: should display the current commit SHA if we are not on a branch's HEAD" $
          testWriterWithConfig
            (buildOutputConfig ZSH (zeroGitRepoState { gitCommitShortSHA = "3d25ef" }) customConfigLocalBranchName)
            addLocalBranchName
          @?= "{%{\x1b[35m%}det#!3d25ef%{\x1b[0;39m%}} "

        , testCase "Other: should display the name of the current branch if we are at the HEAD of any" $
          testWriterWithConfig
            (buildOutputConfig Other (zeroGitRepoState { gitLocalBranch = "foo" }) customConfigLocalBranchName)
            addLocalBranchName
          @?= "{\x1b[36mfoo\x1b[0;39m} "

        , testCase "Other: should display the current commit tog if we are on one" $
          testWriterWithConfig
            (buildOutputConfig Other (zeroGitRepoState { gitCommitTag = "v1.1.1" }) customConfigLocalBranchName)
            addLocalBranchName
          @?= "{\x1b[35mdet#!v1.1.1\x1b[0;39m} "

        , testCase "Other: should display the current commit SHA if we are not on a branch's HEAD" $
          testWriterWithConfig
            (buildOutputConfig Other (zeroGitRepoState { gitCommitShortSHA = "3d25ef" }) customConfigLocalBranchName)
            addLocalBranchName
          @?= "{\x1b[35mdet#!3d25ef\x1b[0;39m} "

        , testCase "TMUX: should display the name of the current branch if we are at the HEAD of any" $
          testWriterWithConfig
            (buildOutputConfig TMUX (zeroGitRepoState { gitLocalBranch = "foo" }) customConfigLocalBranchName)
            addLocalBranchName
          @?= "{#[fg=cyan]foo#[fg=default]} "

        , testCase "TMUX: should display the current commit tog if we are on one" $
          testWriterWithConfig
            (buildOutputConfig TMUX (zeroGitRepoState { gitCommitTag = "v1.1.1" }) customConfigLocalBranchName)
            addLocalBranchName
          @?= "{#[fg=magenta]det#!v1.1.1#[fg=default]} "

        , testCase "TMUX: should display the current commit SHA if we are not on a branch's HEAD" $
          testWriterWithConfig
            (buildOutputConfig TMUX (zeroGitRepoState { gitCommitShortSHA = "3d25ef" }) customConfigLocalBranchName)
            addLocalBranchName
          @?= "{#[fg=magenta]det#!3d25ef#[fg=default]} "

        , testCase "NONE: should display the name of the current branch if we are at the HEAD of any" $
          testWriterWithConfig
            (buildOutputConfig NONE (zeroGitRepoState { gitLocalBranch = "foo" }) customConfigLocalBranchName)
            addLocalBranchName
          @?= "{foo} "

        , testCase "NONE: should display the current commit tog if we are on one" $
          testWriterWithConfig
            (buildOutputConfig NONE (zeroGitRepoState { gitCommitTag = "v1.1.1" }) customConfigLocalBranchName)
            addLocalBranchName
          @?= "{det#!v1.1.1} "

        , testCase "NONE: should display the current commit SHA if we are not on a branch's HEAD" $
          testWriterWithConfig
            (buildOutputConfig NONE (zeroGitRepoState { gitCommitShortSHA = "3d25ef" }) customConfigLocalBranchName)
            addLocalBranchName
          @?= "{det#!3d25ef} "
    ]
  ]

customConfigLocalCommits :: Config
customConfigLocalCommits = defaultConfig {
    confLocalCommitsPushSuffix = "push"
  , confLocalCommitsPushSuffixColor = Cyan
  , confLocalCommitsPushSuffixIntensity = Dull
  , confLocalCommitsPullSuffix = "pull"
  , confLocalCommitsPullSuffixColor = Magenta
  , confLocalCommitsPullSuffixIntensity = Dull
  , confLocalCommitsPushPullInfix = "push-pull"
  , confLocalCommitsPushPullInfixColor = White
  , confLocalCommitsPushPullInfixIntensity = Dull
}

testAddLocalCommits :: TestTree
testAddLocalCommits = testGroup "#addLocalCommits"
  [   testGroup "Default Config"
      [ testCase "ZSH: commits to pull" $
          testCommitsToPull ZSH defaultConfig @?=
          "2%{\x1b[1;31m%}\8595%{\x1b[0;39m%} "

      , testCase "ZSH: commits to push" $
          testCommitsToPush ZSH defaultConfig @?=
          "2%{\x1b[1;32m%}\8593%{\x1b[0;39m%} "

      , testCase "ZSH: commits to pull and to push" $
          testCommitsToPushAndPull ZSH defaultConfig @?=
          "4%{\x1b[1;32m%}⥯%{\x1b[0;39m%}4 "

      , testCase "Other: commits to pull" $
          testCommitsToPull Other defaultConfig @?=
          "2\x1b[1;31m\8595\x1b[0;39m "

      , testCase "Other: commits to push" $
          testCommitsToPush Other defaultConfig @?=
          "2\x1b[1;32m\8593\x1b[0;39m "

      , testCase "Other: commits to pull and to push" $
          testCommitsToPushAndPull Other defaultConfig @?=
          "4\x1b[1;32m⥯\x1b[0;39m4 "

      , testCase "TMUX: commits to pull" $
          testCommitsToPull TMUX defaultConfig @?=
          "2#[fg=brightred]\8595#[fg=default] "

      , testCase "TMUX: commits to push" $
          testCommitsToPush TMUX defaultConfig @?=
          "2#[fg=brightgreen]\8593#[fg=default] "

      , testCase "TMUX: commits to pull and to push" $
          testCommitsToPushAndPull TMUX defaultConfig @?=
          "4#[fg=brightgreen]⥯#[fg=default]4 "

      , testCase "NONE: commits to pull" $
          testCommitsToPull NONE defaultConfig @?=
          "2\8595 "

      , testCase "NONE: commits to push" $
          testCommitsToPush NONE defaultConfig @?=
          "2\8593 "

      , testCase "NONE: commits to pull and to push" $
          testCommitsToPushAndPull NONE defaultConfig @?=
          "4⥯4 "
      ]
    , testGroup "Custom Config"
      [ testCase "ZSH: commits to pull" $
          testCommitsToPull ZSH customConfigLocalCommits @?=
          "2%{\x1b[35m%}pull%{\x1b[0;39m%} "

      , testCase "ZSH: commits to push" $
          testCommitsToPush ZSH customConfigLocalCommits @?=
          "2%{\x1b[36m%}push%{\x1b[0;39m%} "

      , testCase "ZSH: commits to pull and to push" $
          testCommitsToPushAndPull ZSH customConfigLocalCommits @?=
          "4%{\x1b[37m%}push-pull%{\x1b[0;39m%}4 "

      , testCase "Other: commits to pull" $
          testCommitsToPull Other customConfigLocalCommits @?=
          "2\x1b[35mpull\x1b[0;39m "

      , testCase "Other: commits to push" $
          testCommitsToPush Other customConfigLocalCommits @?=
          "2\x1b[36mpush\x1b[0;39m "

      , testCase "Other: commits to pull and to push" $
          testCommitsToPushAndPull Other customConfigLocalCommits @?=
          "4\x1b[37mpush-pull\x1b[0;39m4 "

      , testCase "TMUX: commits to pull" $
          testCommitsToPull TMUX customConfigLocalCommits @?=
          "2#[fg=magenta]pull#[fg=default] "

      , testCase "TMUX: commits to push" $
          testCommitsToPush TMUX customConfigLocalCommits @?=
          "2#[fg=cyan]push#[fg=default] "

      , testCase "TMUX: commits to pull and to push" $
          testCommitsToPushAndPull TMUX customConfigLocalCommits @?=
          "4#[fg=white]push-pull#[fg=default]4 "

      , testCase "NONE: commits to pull" $
          testCommitsToPull NONE customConfigLocalCommits @?=
          "2pull "

      , testCase "NONE: commits to push" $
          testCommitsToPush NONE customConfigLocalCommits @?=
          "2push "

      , testCase "NONE: commits to pull and to push" $
          testCommitsToPushAndPull NONE customConfigLocalCommits @?=
          "4push-pull4 "
      ]
  ]

customChangeConfig :: Config
customChangeConfig = defaultConfig {
    confChangeIndexAddSuffix = "B"
  , confChangeIndexAddSuffixColor = Cyan
  , confChangeIndexAddSuffixIntensity = Dull
  , confChangeIndexModSuffix = "N"
  , confChangeIndexModSuffixColor = Cyan
  , confChangeIndexModSuffixIntensity = Dull
  , confChangeIndexDelSuffix = "E"
  , confChangeIndexDelSuffixColor = Cyan
  , confChangeIndexDelSuffixIntensity = Dull
  , confChangeLocalAddSuffix = "B"
  , confChangeLocalAddSuffixColor = Magenta
  , confChangeLocalAddSuffixIntensity = Dull
  , confChangeLocalModSuffix = "N"
  , confChangeLocalModSuffixColor = Blue
  , confChangeLocalModSuffixIntensity = Dull
  , confChangeLocalDelSuffix = "E"
  , confChangeLocalDelSuffixColor = Blue
  , confChangeLocalDelSuffixIntensity = Dull
  , confChangeRenamedSuffix = "S"
  , confChangeRenamedSuffixColor = Cyan
  , confChangeRenamedSuffixIntensity = Dull
  , confChangeConflictedSuffix = "D"
  , confChangeConflictedSuffixColor = Cyan
  , confChangeConflictedSuffixIntensity = Dull
}

testAddRepoState :: TestTree
testAddRepoState = testGroup "#addRepoState"
  [   testGroup "Default Config"
        [ testCase "ZSH: with Local Add Changes" $
            testLocalAddChange ZSH defaultConfig @?= "2%{\x1b[1;37m%}A%{\x1b[0;39m%} "

          , testCase "ZSH: with Local Mod Changes" $
            testLocalModChange ZSH defaultConfig @?= "2%{\x1b[1;31m%}M%{\x1b[0;39m%} "

          , testCase "ZSH: with Local Del Changes" $
            testLocalDelChange ZSH defaultConfig @?= "2%{\x1b[1;31m%}D%{\x1b[0;39m%} "

          , testCase "ZSH: with Index Add Changes" $
            testIndexAddChange ZSH defaultConfig @?= "2%{\x1b[1;32m%}A%{\x1b[0;39m%} "

          , testCase "ZSH: with Index Mod Changes" $
            testIndexModChange ZSH defaultConfig @?= "2%{\x1b[1;32m%}M%{\x1b[0;39m%} "

          , testCase "ZSH: with Index Del Changes" $
            testIndexDelChange ZSH defaultConfig @?= "2%{\x1b[1;32m%}D%{\x1b[0;39m%} "

          , testCase "ZSH: with Conflicted Changes" $
            testConflictedChange ZSH defaultConfig @?= "2%{\x1b[1;32m%}C%{\x1b[0;39m%} "

          , testCase "ZSH: with Renamed Changes" $
            testRenamedChange ZSH defaultConfig @?= "2%{\x1b[1;32m%}R%{\x1b[0;39m%} "

          , testCase "ZSH: with every kind of Changes" $
            testEveryRepoChange ZSH defaultConfig @?= "6%{\x1b[1;32m%}A%{\x1b[0;39m%}8%{\x1b[1;32m%}D%{\x1b[0;39m%}7%{\x1b[1;32m%}M%{\x1b[0;39m%}1%{\x1b[1;32m%}R%{\x1b[0;39m%} 5%{\x1b[1;31m%}D%{\x1b[0;39m%}4%{\x1b[1;31m%}M%{\x1b[0;39m%} 3%{\x1b[1;37m%}A%{\x1b[0;39m%} 2%{\x1b[1;32m%}C%{\x1b[0;39m%} "

          , testCase "Other: with Local Add Changes" $
            testLocalAddChange Other defaultConfig @?= "2\x1b[1;37mA\x1b[0;39m "

          , testCase "Other: with Local Mod Changes" $
            testLocalModChange Other defaultConfig @?= "2\x1b[1;31mM\x1b[0;39m "

          , testCase "Other: with Local Del Changes" $
            testLocalDelChange Other defaultConfig @?= "2\x1b[1;31mD\x1b[0;39m "

          , testCase "Other: with Index Add Changes" $
            testIndexAddChange Other defaultConfig @?= "2\x1b[1;32mA\x1b[0;39m "

          , testCase "Other: with Index Mod Changes" $
            testIndexModChange Other defaultConfig @?= "2\x1b[1;32mM\x1b[0;39m "

          , testCase "Other: with Index Del Changes" $
            testIndexDelChange Other defaultConfig @?= "2\x1b[1;32mD\x1b[0;39m "

          , testCase "Other: with Conflicted Changes" $
            testConflictedChange Other defaultConfig @?= "2\x1b[1;32mC\x1b[0;39m "

          , testCase "Other: with Renamed Changes" $
            testRenamedChange Other defaultConfig @?= "2\x1b[1;32mR\x1b[0;39m "

          , testCase "Other: with every kind of Changes" $
            testEveryRepoChange Other defaultConfig @?= "6\x1b[1;32mA\x1b[0;39m8\x1b[1;32mD\x1b[0;39m7\x1b[1;32mM\x1b[0;39m1\x1b[1;32mR\x1b[0;39m 5\x1b[1;31mD\x1b[0;39m4\x1b[1;31mM\x1b[0;39m 3\x1b[1;37mA\x1b[0;39m 2\x1b[1;32mC\x1b[0;39m "

          , testCase "TMUX: with Local Add Changes" $
            testLocalAddChange TMUX defaultConfig @?= "2#[fg=brightwhite]A#[fg=default] "

          , testCase "TMUX: with Local Mod Changes" $
            testLocalModChange TMUX defaultConfig @?= "2#[fg=brightred]M#[fg=default] "

          , testCase "TMUX: with Local Del Changes" $
            testLocalDelChange TMUX defaultConfig @?= "2#[fg=brightred]D#[fg=default] "

          , testCase "TMUX: with Index Add Changes" $
            testIndexAddChange TMUX defaultConfig @?= "2#[fg=brightgreen]A#[fg=default] "

          , testCase "TMUX: with Index Mod Changes" $
            testIndexModChange TMUX defaultConfig @?= "2#[fg=brightgreen]M#[fg=default] "

          , testCase "TMUX: with Index Del Changes" $
            testIndexDelChange TMUX defaultConfig @?= "2#[fg=brightgreen]D#[fg=default] "

          , testCase "TMUX: with Conflicted Changes" $
            testConflictedChange TMUX defaultConfig @?= "2#[fg=brightgreen]C#[fg=default] "

          , testCase "TMUX: with Renamed Changes" $
            testRenamedChange TMUX defaultConfig @?= "2#[fg=brightgreen]R#[fg=default] "

          , testCase "TMUX: with every kind of Changes" $
            testEveryRepoChange TMUX defaultConfig @?= "6#[fg=brightgreen]A#[fg=default]8#[fg=brightgreen]D#[fg=default]7#[fg=brightgreen]M#[fg=default]1#[fg=brightgreen]R#[fg=default] 5#[fg=brightred]D#[fg=default]4#[fg=brightred]M#[fg=default] 3#[fg=brightwhite]A#[fg=default] 2#[fg=brightgreen]C#[fg=default] "

          , testCase "NONE: with Local Add Changes" $
            testLocalAddChange NONE defaultConfig @?= "2A "

          , testCase "NONE: with Local Mod Changes" $
            testLocalModChange NONE defaultConfig @?= "2M "

          , testCase "NONE: with Local Del Changes" $
            testLocalDelChange NONE defaultConfig @?= "2D "

          , testCase "NONE: with Index Add Changes" $
            testIndexAddChange NONE defaultConfig @?= "2A "

          , testCase "NONE: with Index Mod Changes" $
            testIndexModChange NONE defaultConfig @?= "2M "

          , testCase "NONE: with Index Del Changes" $
            testIndexDelChange NONE defaultConfig @?= "2D "

          , testCase "NONE: with Conflicted Changes" $
            testConflictedChange NONE defaultConfig @?= "2C "

          , testCase "NONE: with Renamed Changes" $
            testRenamedChange NONE defaultConfig @?= "2R "

          , testCase "NONE: with every kind of Changes" $
            testEveryRepoChange NONE defaultConfig @?= "6A8D7M1R 5D4M 3A 2C "
        ]
    , testGroup "Custom Config"
        [ testCase "ZSH: with Local Add Changes" $
            testLocalAddChange ZSH customChangeConfig @?= "2%{\x1b[35m%}B%{\x1b[0;39m%} "

          , testCase "ZSH: with Local Mod Changes" $
            testLocalModChange ZSH customChangeConfig @?= "2%{\x1b[34m%}N%{\x1b[0;39m%} "

          , testCase "ZSH: with Local Del Changes" $
            testLocalDelChange ZSH customChangeConfig @?= "2%{\x1b[34m%}E%{\x1b[0;39m%} "

          , testCase "ZSH: with Index Add Changes" $
            testIndexAddChange ZSH customChangeConfig @?= "2%{\x1b[36m%}B%{\x1b[0;39m%} "

          , testCase "ZSH: with Index Mod Changes" $
            testIndexModChange ZSH customChangeConfig @?= "2%{\x1b[36m%}N%{\x1b[0;39m%} "

          , testCase "ZSH: with Index Del Changes" $
            testIndexDelChange ZSH customChangeConfig @?= "2%{\x1b[36m%}E%{\x1b[0;39m%} "

          , testCase "ZSH: with Conflicted Changes" $
            testConflictedChange ZSH customChangeConfig @?= "2%{\x1b[36m%}D%{\x1b[0;39m%} "

          , testCase "ZSH: with Renamed Changes" $
            testRenamedChange ZSH customChangeConfig @?= "2%{\x1b[36m%}S%{\x1b[0;39m%} "

          , testCase "ZSH: with every kind of Changes" $
            testEveryRepoChange ZSH customChangeConfig @?= "6%{\x1b[36m%}B%{\x1b[0;39m%}8%{\x1b[36m%}E%{\x1b[0;39m%}7%{\x1b[36m%}N%{\x1b[0;39m%}1%{\x1b[36m%}S%{\x1b[0;39m%} 5%{\x1b[34m%}E%{\x1b[0;39m%}4%{\x1b[34m%}N%{\x1b[0;39m%} 3%{\x1b[35m%}B%{\x1b[0;39m%} 2%{\x1b[36m%}D%{\x1b[0;39m%} "

          , testCase "Other: with Local Add Changes" $
            testLocalAddChange Other customChangeConfig @?= "2\x1b[35mB\x1b[0;39m "

          , testCase "Other: with Local Mod Changes" $
            testLocalModChange Other customChangeConfig @?= "2\x1b[34mN\x1b[0;39m "

          , testCase "Other: with Local Del Changes" $
            testLocalDelChange Other customChangeConfig @?= "2\x1b[34mE\x1b[0;39m "

          , testCase "Other: with Index Add Changes" $
            testIndexAddChange Other customChangeConfig @?= "2\x1b[36mB\x1b[0;39m "

          , testCase "Other: with Index Mod Changes" $
            testIndexModChange Other customChangeConfig @?= "2\x1b[36mN\x1b[0;39m "

          , testCase "Other: with Index Del Changes" $
            testIndexDelChange Other customChangeConfig @?= "2\x1b[36mE\x1b[0;39m "

          , testCase "Other: with Conflicted Changes" $
            testConflictedChange Other customChangeConfig @?= "2\x1b[36mD\x1b[0;39m "

          , testCase "Other: with Renamed Changes" $
            testRenamedChange Other customChangeConfig @?= "2\x1b[36mS\x1b[0;39m "

          , testCase "Other: with every kind of Changes" $
            testEveryRepoChange Other customChangeConfig @?= "6\x1b[36mB\x1b[0;39m8\x1b[36mE\x1b[0;39m7\x1b[36mN\x1b[0;39m1\x1b[36mS\x1b[0;39m 5\x1b[34mE\x1b[0;39m4\x1b[34mN\x1b[0;39m 3\x1b[35mB\x1b[0;39m 2\x1b[36mD\x1b[0;39m "

          , testCase "TMUX: with Local Add Changes" $
            testLocalAddChange TMUX customChangeConfig @?= "2#[fg=magenta]B#[fg=default] "

          , testCase "TMUX: with Local Mod Changes" $
            testLocalModChange TMUX customChangeConfig @?= "2#[fg=blue]N#[fg=default] "

          , testCase "TMUX: with Local Del Changes" $
            testLocalDelChange TMUX customChangeConfig @?= "2#[fg=blue]E#[fg=default] "

          , testCase "TMUX: with Index Add Changes" $
            testIndexAddChange TMUX customChangeConfig @?= "2#[fg=cyan]B#[fg=default] "

          , testCase "TMUX: with Index Mod Changes" $
            testIndexModChange TMUX customChangeConfig @?= "2#[fg=cyan]N#[fg=default] "

          , testCase "TMUX: with Index Del Changes" $
            testIndexDelChange TMUX customChangeConfig @?= "2#[fg=cyan]E#[fg=default] "

          , testCase "TMUX: with Conflicted Changes" $
            testConflictedChange TMUX customChangeConfig @?= "2#[fg=cyan]D#[fg=default] "

          , testCase "TMUX: with Renamed Changes" $
            testRenamedChange TMUX customChangeConfig @?= "2#[fg=cyan]S#[fg=default] "

          , testCase "TMUX: with every kind of Changes" $
            testEveryRepoChange TMUX customChangeConfig @?= "6#[fg=cyan]B#[fg=default]8#[fg=cyan]E#[fg=default]7#[fg=cyan]N#[fg=default]1#[fg=cyan]S#[fg=default] 5#[fg=blue]E#[fg=default]4#[fg=blue]N#[fg=default] 3#[fg=magenta]B#[fg=default] 2#[fg=cyan]D#[fg=default] "

          , testCase "NONE: with Local Add Changes" $
            testLocalAddChange NONE customChangeConfig @?= "2B "

          , testCase "NONE: with Local Mod Changes" $
            testLocalModChange NONE customChangeConfig @?= "2N "

          , testCase "NONE: with Local Del Changes" $
            testLocalDelChange NONE customChangeConfig @?= "2E "

          , testCase "NONE: with Index Add Changes" $
            testIndexAddChange NONE customChangeConfig @?= "2B "

          , testCase "NONE: with Index Mod Changes" $
            testIndexModChange NONE customChangeConfig @?= "2N "

          , testCase "NONE: with Index Del Changes" $
            testIndexDelChange NONE customChangeConfig @?= "2E "

          , testCase "NONE: with Conflicted Changes" $
            testConflictedChange NONE customChangeConfig @?= "2D "

          , testCase "NONE: with Renamed Changes" $
            testRenamedChange NONE customChangeConfig @?= "2S "

          , testCase "NONE: with every kind of Changes" $
            testEveryRepoChange NONE customChangeConfig @?= "6B8E7N1S 5E4N 3B 2D "
        ]
  ]

customStashConfig :: Config
customStashConfig = defaultConfig {
  confStashSuffix = "stash"
  , confStashSuffixColor = Cyan
  , confStashSuffixIntensity = Dull
}

testAddStashes :: TestTree
testAddStashes = testGroup "#addStashes"
  [ testGroup "Default Config"
      [ testCase "ZSH: hardcoded character" $
          testWriterWithConfig
            (buildOutputConfig ZSH (zeroGitRepoState { gitStashCount = 2 }) defaultConfig) addStashes
          @?= "2%{\x1b[1;32m%}\8801%{\x1b[0;39m%} "

        , testCase "Other: hardcoded character" $
          testWriterWithConfig
            (buildOutputConfig Other (zeroGitRepoState { gitStashCount = 2 }) defaultConfig) addStashes
          @?= "2\x1b[1;32m\8801\x1b[0;39m "

        , testCase "TMUX: hardcoded character" $
          testWriterWithConfig
            (buildOutputConfig TMUX (zeroGitRepoState { gitStashCount = 2 }) defaultConfig) addStashes
          @?= "2#[fg=brightgreen]\8801#[fg=default] "

        , testCase "NONE: hardcoded character" $
          testWriterWithConfig
            (buildOutputConfig NONE (zeroGitRepoState { gitStashCount = 2 }) defaultConfig) addStashes
          @?= "2\8801 "
      ]
  , testGroup "Custom Config"
      [ testCase "ZSH: hardcoded character" $
          testWriterWithConfig
            (buildOutputConfig ZSH (zeroGitRepoState { gitStashCount = 2 }) customStashConfig) addStashes
          @?= "2%{\x1b[36m%}stash%{\x1b[0;39m%} "

        , testCase "Other: hardcoded character" $
          testWriterWithConfig
            (buildOutputConfig Other (zeroGitRepoState { gitStashCount = 2 }) customStashConfig) addStashes
          @?= "2\x1b[36mstash\x1b[0;39m "

        , testCase "TMUX: hardcoded character" $
          testWriterWithConfig
            (buildOutputConfig TMUX (zeroGitRepoState { gitStashCount = 2 }) customStashConfig) addStashes
          @?= "2#[fg=cyan]stash#[fg=default] "

        , testCase "NONE: hardcoded character" $
          testWriterWithConfig
            (buildOutputConfig NONE (zeroGitRepoState { gitStashCount = 2 }) customStashConfig) addStashes
          @?= "2stash "
      ]
  ]

localChangesForPartialPrompt :: GitLocalRepoChanges
localChangesForPartialPrompt = GitLocalRepoChanges {
    localMod = 1
    , localAdd = 2
    , localDel = 3
    , indexMod = 4
    , indexAdd = 5
    , indexDel = 6
    , renamed  = 7
    , conflict = 8
  }

repoStateForPartialPrompt :: GitRepoState
repoStateForPartialPrompt = GitRepoState {
    gitLocalRepoChanges = localChangesForPartialPrompt
    , gitLocalBranch = "branch"
    {- The branch supercedes both the commit SHA and the tag -}
    , gitCommitShortSHA = "3de6ef"
    , gitCommitTag = "v1.3"
    , gitRemote = "origin"
    , gitRemoteTrackingBranch = "origin/branch"
    , gitStashCount = 3
    , gitCommitsToPull = 5
    , gitCommitsToPush = 6
    , gitMergeBranchCommitsToPull = 2
    , gitMergeBranchCommitsToPush = 1
  }

{- For reference here, the full prompt would be

 "%{\ESC[0;39m%}\5812 \120366 2%{\ESC[1;32m%}\8644%{\ESC[0;39m%}1 [%{\ESC[0;39m%}branch%{\ESC[0;39m%}] 5%{\ESC[1;32m%}⥯%{\ESC[0;39m%}6 5%{\ESC[1;32m%}A%{\ESC[0;39m%}6%{\ESC[1;32m%}D%{\ESC[0;39m%}4%{\ESC[1;32m%}M%{\ESC[0;39m%}7%{\ESC[1;32m%}R%{\ESC[0;39m%} 3%{\ESC[1;31m%}D%{\ESC[0;39m%}1%{\ESC[1;31m%}M%{\ESC[0;39m%} 2%{\ESC[1;37m%}A%{\ESC[0;39m%} 8%{\ESC[1;32m%}C%{\ESC[0;39m%} 3%{\ESC[1;32m%}\8801%{\ESC[0;39m%} "

-}
testPartialPrompt :: TestTree
testPartialPrompt = testGroup "Partial prompt display"
  [   testCase "w/out repo indicator" $
        testWriterWithConfig
          (buildOutputConfig ZSH repoStateForPartialPrompt defaultConfig { confShowPartRepoIndicator = False })
          buildPrompt
        @?= "%{\ESC[0;39m%}\120366 2%{\ESC[1;32m%}\8644%{\ESC[0;39m%}1 [%{\ESC[0;39m%}branch%{\ESC[0;39m%}] 5%{\ESC[1;32m%}⥯%{\ESC[0;39m%}6 5%{\ESC[1;32m%}A%{\ESC[0;39m%}6%{\ESC[1;32m%}D%{\ESC[0;39m%}4%{\ESC[1;32m%}M%{\ESC[0;39m%}7%{\ESC[1;32m%}R%{\ESC[0;39m%} 3%{\ESC[1;31m%}D%{\ESC[0;39m%}1%{\ESC[1;31m%}M%{\ESC[0;39m%} 2%{\ESC[1;37m%}A%{\ESC[0;39m%} 8%{\ESC[1;32m%}C%{\ESC[0;39m%} 3%{\ESC[1;32m%}\8801%{\ESC[0;39m%} "

    , testCase "w/out merge branch commits info" $
        testWriterWithConfig
          (buildOutputConfig ZSH repoStateForPartialPrompt defaultConfig { confShowPartMergeBranchCommitsDiff = False })
          buildPrompt
        @?= "%{\ESC[0;39m%}\5812 [%{\ESC[0;39m%}branch%{\ESC[0;39m%}] 5%{\ESC[1;32m%}⥯%{\ESC[0;39m%}6 5%{\ESC[1;32m%}A%{\ESC[0;39m%}6%{\ESC[1;32m%}D%{\ESC[0;39m%}4%{\ESC[1;32m%}M%{\ESC[0;39m%}7%{\ESC[1;32m%}R%{\ESC[0;39m%} 3%{\ESC[1;31m%}D%{\ESC[0;39m%}1%{\ESC[1;31m%}M%{\ESC[0;39m%} 2%{\ESC[1;37m%}A%{\ESC[0;39m%} 8%{\ESC[1;32m%}C%{\ESC[0;39m%} 3%{\ESC[1;32m%}\8801%{\ESC[0;39m%} "

    , testCase "w/out local branch info" $
        testWriterWithConfig
          (buildOutputConfig ZSH repoStateForPartialPrompt defaultConfig { confShowPartLocalBranch = False })
          buildPrompt
        @?= "%{\ESC[0;39m%}\5812 \120366 2%{\ESC[1;32m%}\8644%{\ESC[0;39m%}1 5%{\ESC[1;32m%}⥯%{\ESC[0;39m%}6 5%{\ESC[1;32m%}A%{\ESC[0;39m%}6%{\ESC[1;32m%}D%{\ESC[0;39m%}4%{\ESC[1;32m%}M%{\ESC[0;39m%}7%{\ESC[1;32m%}R%{\ESC[0;39m%} 3%{\ESC[1;31m%}D%{\ESC[0;39m%}1%{\ESC[1;31m%}M%{\ESC[0;39m%} 2%{\ESC[1;37m%}A%{\ESC[0;39m%} 8%{\ESC[1;32m%}C%{\ESC[0;39m%} 3%{\ESC[1;32m%}\8801%{\ESC[0;39m%} "

    , testCase "with a branch set to ignore its merge branch" $
        testWriterWithConfig
          (buildOutputConfig ZSH repoStateForPartialPrompt defaultConfig { confMergeBranchIgnoreBranches = ["branch"] })
          buildPrompt
        @?= "%{\ESC[0;39m%}\5812 [%{\ESC[0;39m%}branch%{\ESC[0;39m%}] 5%{\ESC[1;32m%}⥯%{\ESC[0;39m%}6 5%{\ESC[1;32m%}A%{\ESC[0;39m%}6%{\ESC[1;32m%}D%{\ESC[0;39m%}4%{\ESC[1;32m%}M%{\ESC[0;39m%}7%{\ESC[1;32m%}R%{\ESC[0;39m%} 3%{\ESC[1;31m%}D%{\ESC[0;39m%}1%{\ESC[1;31m%}M%{\ESC[0;39m%} 2%{\ESC[1;37m%}A%{\ESC[0;39m%} 8%{\ESC[1;32m%}C%{\ESC[0;39m%} 3%{\ESC[1;32m%}\8801%{\ESC[0;39m%} "

    , testCase "w/out commits push/pull info" $
        testWriterWithConfig
          (buildOutputConfig ZSH repoStateForPartialPrompt defaultConfig { confShowPartCommitsToOrigin = False })
          buildPrompt
        @?= "%{\ESC[0;39m%}\5812 \120366 2%{\ESC[1;32m%}\8644%{\ESC[0;39m%}1 [%{\ESC[0;39m%}branch%{\ESC[0;39m%}] 5%{\ESC[1;32m%}A%{\ESC[0;39m%}6%{\ESC[1;32m%}D%{\ESC[0;39m%}4%{\ESC[1;32m%}M%{\ESC[0;39m%}7%{\ESC[1;32m%}R%{\ESC[0;39m%} 3%{\ESC[1;31m%}D%{\ESC[0;39m%}1%{\ESC[1;31m%}M%{\ESC[0;39m%} 2%{\ESC[1;37m%}A%{\ESC[0;39m%} 8%{\ESC[1;32m%}C%{\ESC[0;39m%} 3%{\ESC[1;32m%}\8801%{\ESC[0;39m%} "

    , testCase "w/out local repo changes" $
        testWriterWithConfig
          (buildOutputConfig ZSH repoStateForPartialPrompt defaultConfig { confShowPartLocalChangesState = False })
          buildPrompt
        @?= "%{\ESC[0;39m%}\5812 \120366 2%{\ESC[1;32m%}\8644%{\ESC[0;39m%}1 [%{\ESC[0;39m%}branch%{\ESC[0;39m%}] 5%{\ESC[1;32m%}⥯%{\ESC[0;39m%}6 3%{\ESC[1;32m%}\8801%{\ESC[0;39m%} "

    , testCase "w/out stashes" $
        testWriterWithConfig
          (buildOutputConfig ZSH repoStateForPartialPrompt defaultConfig { confShowPartStashes = False })
          buildPrompt
        @?= "%{\ESC[0;39m%}\5812 \120366 2%{\ESC[1;32m%}\8644%{\ESC[0;39m%}1 [%{\ESC[0;39m%}branch%{\ESC[0;39m%}] 5%{\ESC[1;32m%}⥯%{\ESC[0;39m%}6 5%{\ESC[1;32m%}A%{\ESC[0;39m%}6%{\ESC[1;32m%}D%{\ESC[0;39m%}4%{\ESC[1;32m%}M%{\ESC[0;39m%}7%{\ESC[1;32m%}R%{\ESC[0;39m%} 3%{\ESC[1;31m%}D%{\ESC[0;39m%}1%{\ESC[1;31m%}M%{\ESC[0;39m%} 2%{\ESC[1;37m%}A%{\ESC[0;39m%} 8%{\ESC[1;32m%}C%{\ESC[0;39m%} "
  ]

-- | Utility function to test a ShellOutput function and gets the prompt built
testWriterWithConfig :: OutputConfig    -- ^ Starting reader state
                     -> ShellOutput     -- ^ Function under test
                     -> String               -- ^ Output of the function for the given config
testWriterWithConfig config functionUnderTest =
  runReader (runWriterT functionUnderTest >>= (\(_, out) -> return out)) config

zeroOutputConfig :: Shell
                 -> OutputConfig
zeroOutputConfig shell = buildOutputConfig shell zeroGitRepoState defaultConfig

testMergeBranchCommitsToPull :: Shell -> Config -> String
testMergeBranchCommitsToPull shell config = testWriterWithConfig
  (buildOutputConfig shell (zeroGitRepoState { gitMergeBranchCommitsToPull = 2 }) config)
  addMergeBranchCommits

testMergeBranchCommitsToPush :: Shell -> Config -> String
testMergeBranchCommitsToPush shell config = testWriterWithConfig
  (buildOutputConfig shell (zeroGitRepoState { gitMergeBranchCommitsToPush = 2 }) config)
  addMergeBranchCommits

testMergeBranchCommitsToPushAndPull :: Shell -> Config -> String
testMergeBranchCommitsToPushAndPull shell config = testWriterWithConfig
  (buildOutputConfig shell
    (zeroGitRepoState { gitMergeBranchCommitsToPull = 4, gitMergeBranchCommitsToPush = 4 })
    config
  )
  addMergeBranchCommits

testCommitsToPull :: Shell -> Config -> String
testCommitsToPull shell config = testWriterWithConfig
  (buildOutputConfig shell (zeroGitRepoState { gitCommitsToPull = 2 }) config)
  addLocalCommits

testCommitsToPush :: Shell -> Config -> String
testCommitsToPush shell config = testWriterWithConfig
  (buildOutputConfig shell (zeroGitRepoState { gitCommitsToPush = 2 }) config)
  addLocalCommits

testCommitsToPushAndPull :: Shell -> Config -> String
testCommitsToPushAndPull shell config = testWriterWithConfig
  (buildOutputConfig shell
    (zeroGitRepoState { gitCommitsToPull = 4, gitCommitsToPush = 4 })
    config
  )
  addLocalCommits

testLocalAddChange :: Shell -> Config -> String
testLocalAddChange shell config = testWriterWithConfig
  (buildOutputConfig shell
    (zeroGitRepoState { gitLocalRepoChanges =
      (zeroLocalRepoChanges { localAdd = 2 })
    })
    config
  )
  addRepoState

testLocalModChange :: Shell -> Config -> String
testLocalModChange shell config = testWriterWithConfig
  (buildOutputConfig shell
    (zeroGitRepoState { gitLocalRepoChanges =
      (zeroLocalRepoChanges { localMod = 2 })
    })
    config
  )
  addRepoState

testLocalDelChange :: Shell -> Config -> String
testLocalDelChange shell config = testWriterWithConfig
  (buildOutputConfig shell
    (zeroGitRepoState { gitLocalRepoChanges =
      (zeroLocalRepoChanges { localDel = 2 })
    })
    config
  )
  addRepoState

testIndexAddChange :: Shell -> Config -> String
testIndexAddChange shell config = testWriterWithConfig
  (buildOutputConfig shell
    (zeroGitRepoState { gitLocalRepoChanges =
      (zeroLocalRepoChanges { indexAdd = 2 })
    })
    config
  )
  addRepoState

testIndexModChange :: Shell -> Config -> String
testIndexModChange shell config = testWriterWithConfig
  (buildOutputConfig shell
    (zeroGitRepoState { gitLocalRepoChanges =
      (zeroLocalRepoChanges { indexMod = 2 })
    })
    config
  )
  addRepoState

testIndexDelChange :: Shell -> Config -> String
testIndexDelChange shell config = testWriterWithConfig
  (buildOutputConfig shell
    (zeroGitRepoState { gitLocalRepoChanges =
      (zeroLocalRepoChanges { indexDel = 2 })
    })
    config
  )
  addRepoState

testConflictedChange :: Shell -> Config -> String
testConflictedChange shell config = testWriterWithConfig
  (buildOutputConfig shell
    (zeroGitRepoState { gitLocalRepoChanges =
      (zeroLocalRepoChanges { conflict = 2 })
    })
    config
  )
  addRepoState

testRenamedChange :: Shell -> Config -> String
testRenamedChange shell config = testWriterWithConfig
  (buildOutputConfig shell
    (zeroGitRepoState { gitLocalRepoChanges =
      (zeroLocalRepoChanges { renamed = 2 })
    })
    config
  )
  addRepoState

testEveryRepoChange :: Shell -> Config -> String
testEveryRepoChange shell config = testWriterWithConfig
  (buildOutputConfig shell
    (zeroGitRepoState { gitLocalRepoChanges =
      (zeroLocalRepoChanges { renamed = 1
        , conflict = 2
        , localAdd = 3
        , localMod = 4
        , localDel = 5
        , indexAdd = 6
        , indexMod = 7
        , indexDel = 8
      })
    })
    config
  )
  addRepoState