packages feed

nri-prelude-0.5.0.1: src/Test/Reporter/Internal.hs

module Test.Reporter.Internal where

import qualified Data.ByteString as BS
import qualified Data.ByteString.Builder as Builder
import qualified GHC.Stack as Stack
import qualified List
import NriPrelude
import qualified System.Console.ANSI as ANSI
import qualified System.Directory
import System.FilePath ((</>))
import qualified Test.Internal as Internal
import qualified Prelude

extraLinesOnFailure :: Int
extraLinesOnFailure = 2

readSrcLoc :: Internal.SingleTest Internal.Failure -> Prelude.IO (Maybe (Stack.SrcLoc, BS.ByteString))
readSrcLoc test =
  case Internal.body test of
    Internal.FailedAssertion _ (Just loc) -> do
      cwd <- System.Directory.getCurrentDirectory
      let path = cwd </> Stack.srcLocFile loc
      exists <- System.Directory.doesFileExist path
      if exists
        then do
          contents <- BS.readFile path
          Prelude.pure (Just (loc, contents))
        else Prelude.pure Nothing
    _ -> Prelude.pure Nothing

renderSrcLoc ::
  ([ANSI.SGR] -> Builder.Builder -> Builder.Builder) ->
  Stack.SrcLoc ->
  BS.ByteString ->
  Builder.Builder
renderSrcLoc styled loc contents = do
  let startLine = Prelude.fromIntegral (Stack.srcLocStartLine loc)
  let lines =
        contents
          |> BS.split 10 -- splitting newlines
          |> List.drop (startLine - extraLinesOnFailure - 1)
          |> List.take (extraLinesOnFailure * 2 + 1)
          |> List.indexedMap
            ( \i l ->
                Builder.intDec
                  ( Prelude.fromIntegral
                      <| startLine + i - extraLinesOnFailure
                  )
                  ++ ": "
                  ++ Builder.byteString l
            )
  case lines of
    [] -> ""
    lines' ->
      "\n"
        ++ "Expectation failed at "
        ++ Builder.stringUtf8 (Stack.srcLocFile loc)
        ++ ":"
        ++ Builder.intDec (Stack.srcLocStartLine loc)
        ++ "\n"
        ++ Prelude.foldMap
          ( \(nr, line) ->
              if nr == extraLinesOnFailure
                then styled [red] ("✗ " ++ line) ++ "\n"
                else "  " ++ styled [dullGrey] line ++ "\n"
          )
          (List.indexedMap (,) lines')
        ++ "\n"

sgr :: [ANSI.SGR] -> Builder.Builder
sgr = Builder.stringUtf8 << ANSI.setSGRCode

red :: ANSI.SGR
red = ANSI.SetColor ANSI.Foreground ANSI.Dull ANSI.Red

yellow :: ANSI.SGR
yellow = ANSI.SetColor ANSI.Foreground ANSI.Dull ANSI.Yellow

green :: ANSI.SGR
green = ANSI.SetColor ANSI.Foreground ANSI.Dull ANSI.Green

grey :: ANSI.SGR
grey = ANSI.SetColor ANSI.Foreground ANSI.Vivid ANSI.Black

dullGrey :: ANSI.SGR
dullGrey = ANSI.SetColor ANSI.Foreground ANSI.Dull ANSI.Black

black :: ANSI.SGR
black = ANSI.SetColor ANSI.Foreground ANSI.Dull ANSI.White

underlined :: ANSI.SGR
underlined = ANSI.SetUnderlining ANSI.SingleUnderline