packages feed

hspec-core-2.2.3: test/Test/Hspec/Core/FormattersSpec.hs

{-# LANGUAGE CPP #-}
module Test.Hspec.Core.FormattersSpec (main, spec) where

import           Prelude ()
import           Helper
import qualified Control.Exception as E

import qualified Test.Hspec.Core.Spec as H
import qualified Test.Hspec.Core.Runner as H
import qualified Test.Hspec.Core.Formatters as H

#ifndef mingw32_HOST_OS
import           System.Console.ANSI
#endif

main :: IO ()
main = hspec spec

testSpec :: H.Spec
testSpec = do
  H.describe "Example" $ do
    H.it "success"    (H.Success)
    H.it "fail 1"     (H.Fail Nothing "fail message")
    H.it "pending"    (H.pendingWith "pending message")
    H.it "fail 2"     (H.Fail Nothing "")
    H.it "exceptions" (undefined :: H.Result)
    H.it "fail 3"     (H.Fail Nothing "")

spec :: Spec
spec = do
  describe "silent" $ do
    let runSpec = fmap fst . capture . H.hspecWithResult H.defaultConfig {H.configFormatter = Just H.silent}
    it "produces no output" $ do
      runSpec testSpec `shouldReturn` ""

  describe "failed_examples" $ do
    failed_examplesSpec H.failed_examples

  describe "progress" $ do
    let runSpec = captureLines . H.hspecWithResult H.defaultConfig {H.configFormatter = Just H.progress}

    it "produces '..F...FF.F' style output" $ do
      r <- runSpec testSpec
      head r `shouldBe` ".F.FFF"

    context "same as failed_examples" $ do
      failed_examplesSpec H.progress

  describe "specdoc" $ do
    let runSpec = captureLines . H.hspecWithResult H.defaultConfig {H.configFormatter = Just H.specdoc}

    it "displays a header for each thing being described" $ do
      _:x:_ <- runSpec testSpec
      x `shouldBe` "Example"

    it "displays one row for each behavior" $ do
      r <- runSpec $ do
        H.describe "List as a Monoid" $ do
          H.describe "mappend" $ do
            H.it "is associative" True
          H.describe "mempty" $ do
            H.it "is a left identity" True
            H.it "is a right identity" True
        H.describe "Maybe as a Monoid" $ do
          H.describe "mappend" $ do
            H.it "is associative" True
          H.describe "mempty" $ do
            H.it "is a left identity" True
            H.it "is a right identity" True
      normalizeSummary r `shouldBe` [
          ""
        , "List as a Monoid"
        , "  mappend"
        , "    is associative"
        , "  mempty"
        , "    is a left identity"
        , "    is a right identity"
        , "Maybe as a Monoid"
        , "  mappend"
        , "    is associative"
        , "  mempty"
        , "    is a left identity"
        , "    is a right identity"
        , ""
        , "Finished in 0.0000 seconds"
        , "6 examples, 0 failures"
        ]

    it "outputs an empty line at the beginning (even for non-nested specs)" $ do
      r <- runSpec $ do
        H.it "example 1" True
        H.it "example 2" True
      normalizeSummary r `shouldBe` [
          ""
        , "example 1"
        , "example 2"
        , ""
        , "Finished in 0.0000 seconds"
        , "2 examples, 0 failures"
        ]

    it "displays a row for each successfull, failed, or pending example" $ do
      r <- runSpec testSpec
      r `shouldSatisfy` any (== "  fail 1 FAILED [1]")
      r `shouldSatisfy` any (== "  success")

    it "displays a '#' with an additional message for pending examples" $ do
      r <- runSpec testSpec
      r `shouldSatisfy` any (== "     # PENDING: pending message")

    context "with an empty group" $ do
      it "omits that group from the report" $ do
        r <- runSpec $ do
          H.describe "foo" $ do
            H.it "example 1" True
          H.describe "bar" $ do
            return ()
          H.describe "baz" $ do
            H.it "example 2" True

        normalizeSummary r `shouldBe` [
            ""
          , "foo"
          , "  example 1"
          , "baz"
          , "  example 2"
          , ""
          , "Finished in 0.0000 seconds"
          , "2 examples, 0 failures"
          ]

    context "same as failed_examples" $ do
      failed_examplesSpec H.progress

failed_examplesSpec :: H.Formatter -> Spec
failed_examplesSpec formatter = do
  let runSpec = captureLines . H.hspecWithResult H.defaultConfig {H.configFormatter = Just formatter}

  it "summarizes the time it takes to finish" $ do
    r <- runSpec (return ())
    normalizeSummary r `shouldSatisfy` any (== "Finished in 0.0000 seconds")

  context "displays a detailed list of failures" $ do
    it "prints all requirements that are not met" $ do
      r <- runSpec testSpec
      r `shouldSatisfy` any (== "  1) Example fail 1")

    it "prints the exception type for requirements that fail due to an uncaught exception" $ do
      r <- runSpec $ do
        H.it "foobar" (E.throw (E.ErrorCall "baz") :: Bool)
      r `shouldContain` [
          "  1) foobar"
        , "       uncaught exception: ErrorCall (baz)"
        ]

    it "prints all descriptions when a nested requirement fails" $ do
      r <- runSpec $
        H.describe "foo" $ do
          H.describe "bar" $ do
            H.it "baz" False
      r `shouldSatisfy` any (== "  1) foo.bar baz")


    context "when a failed example has a source location" $ do
      let bestEffortExplanation = "Source locations marked with \"best-effort\" are calculated heuristically and may be incorrect."

      it "includes the source locations above the error messages" $ do
        let loc = H.Location "test/FooSpec.hs" 23 0 H.ExactLocation
            addLoc e = e {H.itemLocation = Just loc}
        r <- runSpec $ H.mapSpecItem_ addLoc $ do
          H.it "foo" False
        r `shouldContain` ["  test/FooSpec.hs:23: ", "  1) foo"]

      context "when source location is exact" $ do
        it "includes that source locations" $ do
          let loc = H.Location "test/FooSpec.hs" 23 0 H.ExactLocation
              addLoc e = e {H.itemLocation = Just loc}
          r <- runSpec $ H.mapSpecItem_ addLoc $ do
            H.it "foo" False
          r `shouldSatisfy` any (== "  test/FooSpec.hs:23: ")

        it "does not include 'best-effort' explanation" $ do
          let loc = H.Location "test/FooSpec.hs" 23 0 H.ExactLocation
              addLoc e = e {H.itemLocation = Just loc}
          r <- runSpec $ H.mapSpecItem_ addLoc $ do
            H.it "foo" False
          r `shouldSatisfy` all (/= bestEffortExplanation)

      context "when source location is best-effort" $ do
        it "marks that source location as 'best-effort'" $ do
          let loc = H.Location "test/FooSpec.hs" 23 0 H.BestEffort
              addLoc e = e {H.itemLocation = Just loc}
          r <- runSpec $ H.mapSpecItem_ addLoc $ do
            H.it "foo" False
          r `shouldSatisfy` any (== "  test/FooSpec.hs:23: (best-effort)")

        it "includes 'best-effort' explanation" $ do
          let loc = H.Location "test/FooSpec.hs" 23 0 H.BestEffort
              addLoc e = e {H.itemLocation = Just loc}
          r <- runSpec $ H.mapSpecItem_ addLoc $ do
            H.it "foo" False
          r `shouldSatisfy` any (== bestEffortExplanation)

  it "summarizes the number of examples and failures" $ do
    r <- runSpec testSpec
    r `shouldSatisfy` any (== "6 examples, 4 failures, 1 pending")

  -- Windows has no support for ANSI escape codes.  The Console API is used for
  -- colorized output, hence the following tests do not work on Windows.
#ifndef mingw32_HOST_OS
  it "shows summary in green if there are no failures" $ do
    r <- captureLines $ H.hspecWithResult H.defaultConfig {H.configColorMode = H.ColorAlways} $ do
      H.it "foobar" True
    r `shouldSatisfy` any (== (green ++ "1 example, 0 failures" ++ reset))

  it "shows summary in yellow if there are pending examples" $ do
    r <- captureLines $ H.hspecWithResult H.defaultConfig {H.configColorMode = H.ColorAlways} $ do
      H.it "foobar" H.pending
    r `shouldSatisfy` any (== (yellow ++ "1 example, 0 failures, 1 pending" ++ reset))

  it "shows summary in red if there are failures" $ do
    r <- captureLines $ H.hspecWithResult H.defaultConfig {H.configColorMode = H.ColorAlways} $ do
      H.it "foobar" False
    r `shouldSatisfy` any (== (red ++ "1 example, 1 failure" ++ reset))

  it "shows summary in red if there are both failures and pending examples" $ do
    r <- captureLines $ H.hspecWithResult H.defaultConfig {H.configColorMode = H.ColorAlways} $ do
      H.it "foo" False
      H.it "bar" H.pending
    r `shouldSatisfy` any (== (red ++ "2 examples, 1 failure, 1 pending" ++ reset))
  where
    green  = setSGRCode [SetColor Foreground Dull Green]
    yellow = setSGRCode [SetColor Foreground Dull Yellow]
    red    = setSGRCode [SetColor Foreground Dull Red]
    reset  = setSGRCode [Reset]
#endif