-- | Module for presenting test results on the console.
--
-- Lifted in large part from: https://github.com/stoeffel/tasty-test-reporter
module Test.Reporter.Stdout
  ( report,
  )
where

import qualified Control.Exception as Exception
import qualified Data.ByteString as BS
import qualified GHC.Stack as Stack
import qualified List
import qualified Maybe
import NriPrelude
import qualified Numeric
import qualified Platform.Internal
import qualified System.IO
import qualified Test.Internal as Internal
import Test.Reporter.Internal (black, green, grey, red, yellow)
import qualified Test.Reporter.Internal
import qualified Text
import Text.Colour (chunk)
import qualified Text.Colour
import qualified Text.Colour.Capabilities.FromEnv
import qualified Prelude

report :: System.IO.Handle -> Internal.SuiteResult -> Prelude.IO ()
report :: Handle -> SuiteResult -> IO ()
report Handle
handle SuiteResult
results = do
  terminalCapabilities <- Handle -> IO TerminalCapabilities
Text.Colour.Capabilities.FromEnv.getTerminalCapabilitiesFromHandle Handle
handle
  reportChunks <- renderReport results
  Text.Colour.hPutChunksUtf8With terminalCapabilities handle reportChunks
  System.IO.hFlush handle

renderReport :: Internal.SuiteResult -> Prelude.IO (List (Text.Colour.Chunk))
renderReport :: SuiteResult -> IO (List Chunk)
renderReport SuiteResult
results =
  let elapsed :: Text
elapsed = SuiteResult -> Text
formatElapsedDuration SuiteResult
results
   in case SuiteResult
results of
        Internal.AllPassed [SingleTest TracingSpan]
passed ->
          let amountPassed :: Int
amountPassed = [SingleTest TracingSpan] -> Int
forall a. List a -> Int
List.length [SingleTest TracingSpan]
passed
           in List Chunk -> IO (List Chunk)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
Prelude.pure
                [ Chunk -> Chunk
green (Chunk -> Chunk
Text.Colour.underline Chunk
"TEST RUN PASSED"),
                  Chunk
"\n\n",
                  Chunk -> Chunk
black (Chunk -> Chunk) -> Chunk -> Chunk
forall a b. (a -> b) -> a -> b
<| Text -> Chunk
chunk (Text -> Chunk) -> Text -> Chunk
forall a b. (a -> b) -> a -> b
<| Text
"Duration:  " Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
elapsed,
                  Chunk
"\n",
                  Chunk -> Chunk
black (Text -> Chunk
chunk (Text -> Chunk) -> Text -> Chunk
forall a b. (a -> b) -> a -> b
<| Text
"Passed:    " Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Int -> Text
Text.fromInt Int
amountPassed),
                  Chunk
"\n"
                ]
        Internal.OnlysPassed [SingleTest TracingSpan]
passed [SingleTest NotRan]
skipped ->
          let amountPassed :: Int
amountPassed = [SingleTest TracingSpan] -> Int
forall a. List a -> Int
List.length [SingleTest TracingSpan]
passed
              amountSkipped :: Int
amountSkipped = [SingleTest NotRan] -> Int
forall a. List a -> Int
List.length [SingleTest NotRan]
skipped
           in List Chunk -> IO (List Chunk)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
Prelude.pure (List Chunk -> IO (List Chunk)) -> List Chunk -> IO (List Chunk)
forall a b. (a -> b) -> a -> b
<|
                List (List Chunk) -> List Chunk
forall a. List (List a) -> List a
List.concat
                  [ (SingleTest TracingSpan -> List Chunk)
-> [SingleTest TracingSpan] -> List Chunk
forall a b. (a -> List b) -> List a -> List b
List.concatMap
                      ( \SingleTest TracingSpan
only ->
                          (Chunk -> Chunk) -> SingleTest TracingSpan -> List Chunk
forall a. (Chunk -> Chunk) -> SingleTest a -> List Chunk
prettyPath Chunk -> Chunk
yellow SingleTest TracingSpan
only
                            List Chunk -> List Chunk -> List Chunk
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ [ Chunk
"This test passed, but there is a `Test.only` in your test.\n",
                                 Chunk
"I failed the test, because it's easy to forget to remove `Test.only`.\n",
                                 Chunk
"\n\n"
                               ]
                      )
                      [SingleTest TracingSpan]
passed,
                    [ Chunk -> Chunk
yellow (Chunk -> Chunk
Text.Colour.underline (Chunk
"TEST RUN INCOMPLETE")),
                      Chunk -> Chunk
yellow Chunk
" because there is an `only` in your tests.",
                      Chunk
"\n\n",
                      Chunk -> Chunk
black (Chunk -> Chunk) -> Chunk -> Chunk
forall a b. (a -> b) -> a -> b
<| Text -> Chunk
chunk (Text -> Chunk) -> Text -> Chunk
forall a b. (a -> b) -> a -> b
<| Text
"Duration:  " Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
elapsed,
                      Chunk
"\n",
                      Chunk -> Chunk
black (Text -> Chunk
chunk (Text -> Chunk) -> Text -> Chunk
forall a b. (a -> b) -> a -> b
<| Text
"Passed:    " Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Int -> Text
Text.fromInt Int
amountPassed),
                      Chunk
"\n",
                      Chunk -> Chunk
black (Text -> Chunk
chunk (Text -> Chunk) -> Text -> Chunk
forall a b. (a -> b) -> a -> b
<| Text
"Skipped:   " Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Int -> Text
Text.fromInt Int
amountSkipped),
                      Chunk
"\n"
                    ]
                  ]
        Internal.PassedWithSkipped [SingleTest TracingSpan]
passed [SingleTest NotRan]
skipped ->
          let amountPassed :: Int
amountPassed = [SingleTest TracingSpan] -> Int
forall a. List a -> Int
List.length [SingleTest TracingSpan]
passed
              amountSkipped :: Int
amountSkipped = [SingleTest NotRan] -> Int
forall a. List a -> Int
List.length [SingleTest NotRan]
skipped
           in List Chunk -> IO (List Chunk)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
Prelude.pure (List Chunk -> IO (List Chunk)) -> List Chunk -> IO (List Chunk)
forall a b. (a -> b) -> a -> b
<|
                List (List Chunk) -> List Chunk
forall a. List (List a) -> List a
List.concat
                  [ (SingleTest NotRan -> List Chunk)
-> [SingleTest NotRan] -> List Chunk
forall a b. (a -> List b) -> List a -> List b
List.concatMap
                      ( \SingleTest NotRan
only ->
                          (Chunk -> Chunk) -> SingleTest NotRan -> List Chunk
forall a. (Chunk -> Chunk) -> SingleTest a -> List Chunk
prettyPath Chunk -> Chunk
yellow SingleTest NotRan
only
                            List Chunk -> List Chunk -> List Chunk
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ [ Chunk
"This test was skipped.",
                                 Chunk
"\n\n"
                               ]
                      )
                      [SingleTest NotRan]
skipped,
                    [ Chunk -> Chunk
yellow (Chunk -> Chunk
Text.Colour.underline Chunk
"TEST RUN INCOMPLETE"),
                      Chunk -> Chunk
yellow
                        ( Text -> Chunk
chunk (Text -> Chunk) -> Text -> Chunk
forall a b. (a -> b) -> a -> b
<| case [SingleTest NotRan] -> Int
forall a. List a -> Int
List.length [SingleTest NotRan]
skipped of
                            Int
1 -> Text
" because 1 test was skipped"
                            Int
n -> Text
" because " Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Int -> Text
Text.fromInt Int
n Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
" tests were skipped"
                        ),
                      Chunk
"\n\n",
                      Chunk -> Chunk
black (Chunk -> Chunk) -> Chunk -> Chunk
forall a b. (a -> b) -> a -> b
<| Text -> Chunk
chunk (Text -> Chunk) -> Text -> Chunk
forall a b. (a -> b) -> a -> b
<| Text
"Duration:  " Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
elapsed,
                      Chunk
"\n",
                      Chunk -> Chunk
black (Text -> Chunk
chunk (Text -> Chunk) -> Text -> Chunk
forall a b. (a -> b) -> a -> b
<| Text
"Passed:    " Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Int -> Text
Text.fromInt Int
amountPassed),
                      Chunk
"\n",
                      Chunk -> Chunk
black (Text -> Chunk
chunk (Text -> Chunk) -> Text -> Chunk
forall a b. (a -> b) -> a -> b
<| Text
"Skipped:   " Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Int -> Text
Text.fromInt Int
amountSkipped),
                      Chunk
"\n"
                    ]
                  ]
        Internal.TestsFailed [SingleTest TracingSpan]
passed [SingleTest NotRan]
skipped [SingleTest FailedSpan]
failed -> do
          let amountPassed :: Int
amountPassed = [SingleTest TracingSpan] -> Int
forall a. List a -> Int
List.length [SingleTest TracingSpan]
passed
          let amountFailed :: Int
amountFailed = [SingleTest FailedSpan] -> Int
forall a. List a -> Int
List.length [SingleTest FailedSpan]
failed
          let amountSkipped :: Int
amountSkipped = [SingleTest NotRan] -> Int
forall a. List a -> Int
List.length [SingleTest NotRan]
skipped
          let failures :: List (SingleTest Failure)
failures = (SingleTest FailedSpan -> SingleTest Failure)
-> [SingleTest FailedSpan] -> List (SingleTest Failure)
forall a b. (a -> b) -> List a -> List b
List.map ((FailedSpan -> Failure)
-> SingleTest FailedSpan -> SingleTest Failure
forall (m :: * -> *) a value.
Functor m =>
(a -> value) -> m a -> m value
map (\(Internal.FailedSpan TracingSpan
_ Failure
failure) -> Failure
failure)) [SingleTest FailedSpan]
failed
          srcLocs <- (SingleTest Failure -> IO (Maybe (SrcLoc, ByteString)))
-> List (SingleTest Failure) -> IO [Maybe (SrcLoc, ByteString)]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
Prelude.traverse SingleTest Failure -> IO (Maybe (SrcLoc, ByteString))
Test.Reporter.Internal.readSrcLoc List (SingleTest Failure)
failures
          let failuresSrcs = (Maybe (SrcLoc, ByteString) -> List Chunk)
-> [Maybe (SrcLoc, ByteString)] -> List (List Chunk)
forall a b. (a -> b) -> List a -> List b
List.map Maybe (SrcLoc, ByteString) -> List Chunk
renderFailureInFile [Maybe (SrcLoc, ByteString)]
srcLocs
          Prelude.pure <|
            List.concat
              [ List.concat <|
                  List.map2
                    ( \List Chunk
srcLines SingleTest Failure
test ->
                        (Chunk -> Chunk) -> SingleTest Failure -> List Chunk
forall a. (Chunk -> Chunk) -> SingleTest a -> List Chunk
prettyPath Chunk -> Chunk
red SingleTest Failure
test
                          List Chunk -> List Chunk -> List Chunk
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ List Chunk
srcLines
                          List Chunk -> List Chunk -> List Chunk
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ [SingleTest Failure -> Chunk
testFailure SingleTest Failure
test, Chunk
"\n\n"]
                    )
                    failuresSrcs
                    failures,
                [ red (Text.Colour.underline "TEST RUN FAILED"),
                  "\n\n",
                  black <| chunk <| "Duration:  " ++ elapsed,
                  "\n",
                  black (chunk <| "Passed:    " ++ Text.fromInt amountPassed),
                  "\n"
                ],
                if amountSkipped == 0
                  then []
                  else
                    [ black (chunk <| "Skipped:   " ++ Text.fromInt amountSkipped),
                      "\n"
                    ],
                [black (chunk <| "Failed:    " ++ Text.fromInt amountFailed), "\n"]
              ]
        SuiteResult
Internal.NoTestsInSuite ->
          List Chunk -> IO (List Chunk)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
Prelude.pure
            [ Chunk -> Chunk
yellow (Chunk -> Chunk
Text.Colour.underline Chunk
"TEST RUN INCOMPLETE"),
              Chunk -> Chunk
yellow Chunk
" because the test suite is empty.",
              Chunk
"\n"
            ]

renderFailureInFile :: Maybe (Stack.SrcLoc, BS.ByteString) -> List Text.Colour.Chunk
renderFailureInFile :: Maybe (SrcLoc, ByteString) -> List Chunk
renderFailureInFile Maybe (SrcLoc, ByteString)
maybeSrcLoc =
  case Maybe (SrcLoc, ByteString)
maybeSrcLoc of
    Just (SrcLoc
loc, ByteString
src) -> SrcLoc -> ByteString -> List Chunk
Test.Reporter.Internal.renderSrcLoc SrcLoc
loc ByteString
src
    Maybe (SrcLoc, ByteString)
Nothing -> []

prettyPath :: (Text.Colour.Chunk -> Text.Colour.Chunk) -> Internal.SingleTest a -> List Text.Colour.Chunk
prettyPath :: forall a. (Chunk -> Chunk) -> SingleTest a -> List Chunk
prettyPath Chunk -> Chunk
style SingleTest a
test =
  let loc :: SrcLoc
loc = SingleTest a -> SrcLoc
forall a. SingleTest a -> SrcLoc
Internal.loc SingleTest a
test
   in List (List Chunk) -> List Chunk
forall a. List (List a) -> List a
List.concat
        [ [ Chunk -> Chunk
grey (Chunk -> Chunk) -> Chunk -> Chunk
forall a b. (a -> b) -> a -> b
<|
              Text -> Chunk
chunk
                ( Text
"↓ "
                    Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ String -> Text
Text.fromList (SrcLoc -> String
Stack.srcLocFile SrcLoc
loc)
                    Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
":"
                    Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Int -> Text
Text.fromInt (Int -> Int
forall a b. (Integral a, Num b) => a -> b
Prelude.fromIntegral (SrcLoc -> Int
Stack.srcLocStartLine SrcLoc
loc))
                    Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
"\n"
                )
          ],
          [ Chunk -> Chunk
grey
              ( Text -> Chunk
chunk (Text -> Chunk) -> Text -> Chunk
forall a b. (a -> b) -> a -> b
<|
                  (Text -> Text) -> [Text] -> Text
forall m a. Monoid m => (a -> m) -> [a] -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
Prelude.foldMap
                    (\Text
text -> Text
"↓ " Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
text Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
"\n")
                    (SingleTest a -> [Text]
forall a. SingleTest a -> [Text]
Internal.describes SingleTest a
test)
              ),
            Chunk -> Chunk
style (Text -> Chunk
chunk (Text
"✗ " Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ SingleTest a -> Text
forall a. SingleTest a -> Text
Internal.name SingleTest a
test)),
            Chunk
"\n"
          ]
        ]

testFailure :: Internal.SingleTest Internal.Failure -> Text.Colour.Chunk
testFailure :: SingleTest Failure -> Chunk
testFailure SingleTest Failure
test =
  Text -> Chunk
chunk (Text -> Chunk) -> Text -> Chunk
forall a b. (a -> b) -> a -> b
<|
    case SingleTest Failure -> Failure
forall a. SingleTest a -> a
Internal.body SingleTest Failure
test of
      Internal.FailedAssertion Text
msg SrcLoc
_ -> Text
msg
      Internal.ThrewException SomeException
exception ->
        Text
"Test threw an exception\n"
          Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ String -> Text
Text.fromList (SomeException -> String
forall e. Exception e => e -> String
Exception.displayException SomeException
exception)
      Failure
Internal.TookTooLong -> Text
"Test timed out"
      Internal.TestRunnerMessedUp Text
msg ->
        Text
"Test runner encountered an unexpected error:\n"
          Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
msg
          Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
"\n"
          Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
"This is a bug.\n\n"
          Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
"If you have some time to report the bug it would be much appreciated!\n"
          Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
"You can do so here: https://github.com/NoRedInk/haskell-libraries/issues"

formatElapsedDuration :: Internal.SuiteResult -> Text
formatElapsedDuration :: SuiteResult -> Text
formatElapsedDuration SuiteResult
result =
  SuiteResult
result SuiteResult
-> (SuiteResult -> List TracingSpan) -> List TracingSpan
forall a b. a -> (a -> b) -> b
|> SuiteResult -> List TracingSpan
resultSpans List TracingSpan -> (List TracingSpan -> Float) -> Float
forall a b. a -> (a -> b) -> b
|> List TracingSpan -> Float
elapsedMilliseconds Float -> (Float -> Text) -> Text
forall a b. a -> (a -> b) -> b
|> Float -> Text
formatElapsedMilliseconds

elapsedMilliseconds :: List Platform.Internal.TracingSpan -> Float
elapsedMilliseconds :: List TracingSpan -> Float
elapsedMilliseconds List TracingSpan
spans =
  let startTime :: MonotonicTime
startTime =
        List TracingSpan
spans
          List TracingSpan
-> (List TracingSpan -> List MonotonicTime) -> List MonotonicTime
forall a b. a -> (a -> b) -> b
|> (TracingSpan -> MonotonicTime)
-> List TracingSpan -> List MonotonicTime
forall a b. (a -> b) -> List a -> List b
List.map TracingSpan -> MonotonicTime
Platform.Internal.started
          List MonotonicTime
-> (List MonotonicTime -> Maybe MonotonicTime)
-> Maybe MonotonicTime
forall a b. a -> (a -> b) -> b
|> List MonotonicTime -> Maybe MonotonicTime
forall a. Ord a => List a -> Maybe a
List.minimum
          Maybe MonotonicTime
-> (Maybe MonotonicTime -> MonotonicTime) -> MonotonicTime
forall a b. a -> (a -> b) -> b
|> MonotonicTime -> Maybe MonotonicTime -> MonotonicTime
forall a. a -> Maybe a -> a
Maybe.withDefault MonotonicTime
0
      finishTime :: MonotonicTime
finishTime =
        List TracingSpan
spans
          List TracingSpan
-> (List TracingSpan -> List MonotonicTime) -> List MonotonicTime
forall a b. a -> (a -> b) -> b
|> (TracingSpan -> MonotonicTime)
-> List TracingSpan -> List MonotonicTime
forall a b. (a -> b) -> List a -> List b
List.map TracingSpan -> MonotonicTime
Platform.Internal.finished
          List MonotonicTime
-> (List MonotonicTime -> Maybe MonotonicTime)
-> Maybe MonotonicTime
forall a b. a -> (a -> b) -> b
|> List MonotonicTime -> Maybe MonotonicTime
forall a. Ord a => List a -> Maybe a
List.maximum
          Maybe MonotonicTime
-> (Maybe MonotonicTime -> MonotonicTime) -> MonotonicTime
forall a b. a -> (a -> b) -> b
|> MonotonicTime -> Maybe MonotonicTime -> MonotonicTime
forall a. a -> Maybe a -> a
Maybe.withDefault MonotonicTime
0
   in MonotonicTime
finishTime MonotonicTime -> MonotonicTime -> MonotonicTime
forall number. Num number => number -> number -> number
- MonotonicTime
startTime MonotonicTime -> (MonotonicTime -> Float) -> Float
forall a b. a -> (a -> b) -> b
|> MonotonicTime -> Float
forall a b. (Integral a, Num b) => a -> b
Prelude.fromIntegral Float -> (Float -> Float) -> Float
forall a b. a -> (a -> b) -> b
|> (Float -> Float -> Float
/ Float
1000)

resultSpans :: Internal.SuiteResult -> List Platform.Internal.TracingSpan
resultSpans :: SuiteResult -> List TracingSpan
resultSpans SuiteResult
result =
  case SuiteResult
result of
    Internal.AllPassed [SingleTest TracingSpan]
passed ->
      (SingleTest TracingSpan -> TracingSpan)
-> [SingleTest TracingSpan] -> List TracingSpan
forall a b. (a -> b) -> List a -> List b
List.map SingleTest TracingSpan -> TracingSpan
forall a. SingleTest a -> a
Internal.body [SingleTest TracingSpan]
passed
    Internal.OnlysPassed [SingleTest TracingSpan]
passed [SingleTest NotRan]
_skipped ->
      (SingleTest TracingSpan -> TracingSpan)
-> [SingleTest TracingSpan] -> List TracingSpan
forall a b. (a -> b) -> List a -> List b
List.map SingleTest TracingSpan -> TracingSpan
forall a. SingleTest a -> a
Internal.body [SingleTest TracingSpan]
passed
    Internal.PassedWithSkipped [SingleTest TracingSpan]
passed [SingleTest NotRan]
_skipped ->
      (SingleTest TracingSpan -> TracingSpan)
-> [SingleTest TracingSpan] -> List TracingSpan
forall a b. (a -> b) -> List a -> List b
List.map SingleTest TracingSpan -> TracingSpan
forall a. SingleTest a -> a
Internal.body [SingleTest TracingSpan]
passed
    Internal.TestsFailed [SingleTest TracingSpan]
passed [SingleTest NotRan]
_skipped [SingleTest FailedSpan]
failed -> do
      (SingleTest TracingSpan -> TracingSpan)
-> [SingleTest TracingSpan] -> List TracingSpan
forall a b. (a -> b) -> List a -> List b
List.map SingleTest TracingSpan -> TracingSpan
forall a. SingleTest a -> a
Internal.body [SingleTest TracingSpan]
passed
        List TracingSpan -> List TracingSpan -> List TracingSpan
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ ([SingleTest FailedSpan]
failed [SingleTest FailedSpan]
-> ([SingleTest FailedSpan] -> List FailedSpan) -> List FailedSpan
forall a b. a -> (a -> b) -> b
|> (SingleTest FailedSpan -> FailedSpan)
-> [SingleTest FailedSpan] -> List FailedSpan
forall a b. (a -> b) -> List a -> List b
List.map SingleTest FailedSpan -> FailedSpan
forall a. SingleTest a -> a
Internal.body List FailedSpan
-> (List FailedSpan -> List TracingSpan) -> List TracingSpan
forall a b. a -> (a -> b) -> b
|> (FailedSpan -> TracingSpan) -> List FailedSpan -> List TracingSpan
forall a b. (a -> b) -> List a -> List b
List.map (\(Internal.FailedSpan TracingSpan
span Failure
_) -> TracingSpan
span))
    SuiteResult
Internal.NoTestsInSuite ->
      []

-- and maybe add something like
-- for rendering the elapsed time into a human readable format not only to ms
formatElapsedMilliseconds :: Float -> Text
formatElapsedMilliseconds :: Float -> Text
formatElapsedMilliseconds Float
ms =
  if Float
ms Float -> Float -> Bool
forall comparable.
Ord comparable =>
comparable -> comparable -> Bool
< Float
1000
    then Int -> Text
Text.fromInt (Float -> Int
round Float
ms) Text -> Text -> Text
forall appendable.
Semigroup appendable =>
appendable -> appendable -> appendable
++ Text
" ms"
    else String -> Text
Text.fromList (Maybe Int -> Float -> ShowS
forall a. RealFloat a => Maybe Int -> a -> ShowS
Numeric.showFFloat (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
2) (Float
ms Float -> Float -> Float
/ Float
1000) String
" s")