{-| This module re-exports everything from "Test.Hspec", except with modified versions of the runner and expectations that operate over an arbitrary monad transformer stack with 'IO' at the base. This is especially useful in conjunction with 'Genesis.Test.Persist.dbExample' from "Genesis.Test.Persist", which runs an @hspec@ example in a monad that can interact with a database. -} module Genesis.Test.Hspec ( module Test.Hspec , hspec , expectationFailure , shouldBe , shouldSatisfy , shouldStartWith , shouldEndWith , shouldContain , shouldMatchList , shouldReturn , shouldNotBe , shouldNotSatisfy , shouldNotContain , shouldNotReturn , shouldThrow ) where import qualified Test.Hspec as Hspec import Control.Exception.Lifted (Exception, try) import Control.Monad (unless) import Control.Monad.Base (MonadBase, liftBase) import Control.Monad.Trans.Control (MonadBaseControl) import Data.Proxy (Proxy(..)) import Data.Typeable (typeRep) import GHC.Stack (HasCallStack) import Test.Hspec hiding ( hspec , expectationFailure , shouldBe , shouldSatisfy , shouldStartWith , shouldEndWith , shouldContain , shouldMatchList , shouldReturn , shouldNotBe , shouldNotSatisfy , shouldNotContain , shouldNotReturn , shouldThrow ) hspec :: MonadBase IO m => Spec -> m () hspec x = liftBase $ Hspec.hspec x expectationFailure :: (HasCallStack, MonadBase IO m) => String -> m () expectationFailure x = liftBase $ Hspec.expectationFailure x expectTrue :: (HasCallStack, MonadBase IO m) => String -> Bool -> m () expectTrue msg b = unless b (expectationFailure msg) shouldBe :: (HasCallStack, Show a, Eq a, MonadBase IO m) => a -> a -> m () shouldBe x y = liftBase $ Hspec.shouldBe x y shouldSatisfy :: (HasCallStack, Show a, MonadBase IO m) => a -> (a -> Bool) -> m () shouldSatisfy x y = liftBase $ Hspec.shouldSatisfy x y shouldStartWith :: (HasCallStack, Show a, Eq a, MonadBase IO m) => [a] -> [a] -> m () shouldStartWith x y = liftBase $ Hspec.shouldStartWith x y shouldEndWith :: (HasCallStack, Show a, Eq a, MonadBase IO m) => [a] -> [a] -> m () shouldEndWith x y = liftBase $ Hspec.shouldEndWith x y shouldContain :: (HasCallStack, Show a, Eq a, MonadBase IO m) => [a] -> [a] -> m () shouldContain x y = liftBase $ Hspec.shouldContain x y shouldMatchList :: (HasCallStack, Show a, Eq a, MonadBase IO m) => [a] -> [a] -> m () shouldMatchList x y = liftBase $ Hspec.shouldMatchList x y shouldNotBe :: (HasCallStack, Show a, Eq a, MonadBase IO m) => a -> a -> m () shouldNotBe x y = liftBase $ Hspec.shouldNotBe x y shouldNotSatisfy :: (HasCallStack, Show a, MonadBase IO m) => a -> (a -> Bool) -> m () shouldNotSatisfy x y = liftBase $ Hspec.shouldNotSatisfy x y shouldNotContain :: (HasCallStack, Show a, Eq a, MonadBase IO m) => [a] -> [a] -> m () shouldNotContain x y = liftBase $ Hspec.shouldNotContain x y shouldReturn :: (HasCallStack, Show a, Eq a, MonadBase IO m) => m a -> a -> m () shouldReturn action x = action >>= (`shouldBe` x) shouldNotReturn :: (HasCallStack, Show a, Eq a, MonadBase IO m) => m a -> a -> m () shouldNotReturn action x = action >>= (`shouldNotBe` x) -- impl modified from Test.Hspec.Expectations.shouldThrow shouldThrow :: forall e a m. (HasCallStack, Exception e, MonadBaseControl IO m) => m a -> Selector e -> m () shouldThrow action p = try action >>= \case Right _ -> expectationFailure $ "did not get expected exception: " ++ exceptionType Left e -> flip expectTrue (p e) $ "predicate failed on expected exception: " ++ exceptionType ++ " (" ++ show e ++ ")" where exceptionType = show $ typeRep (Proxy @e) -- a string repsentation of the expected exception's type