diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2011-2013 Simon Hengel <sol@typeful.net>
+Copyright (c) 2011-2023 Simon Hengel <sol@typeful.net>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.lhs b/README.lhs
deleted file mode 100644
--- a/README.lhs
+++ /dev/null
@@ -1,111 +0,0 @@
-# Catchy combinators for HUnit
-
-(inspired by [ScalaTest's ShouldMatchers](http://www.scalatest.org/))
-
-The three main primitives are `shouldBe`, `shouldSatisfy` and
-`shouldThrow`. They can be used with
-[HUnit](http://hackage.haskell.org/package/HUnit), or any framework that
-integrates with HUnit, like
-[test-framework](http://hackage.haskell.org/package/test-framework) or
-[Hspec](http://hackage.haskell.org/package/hspec).
-
-## An introductory example
-
-Here is an example that uses Hspec. It's a partial specification of
-itself.
-
-~~~ {.haskell .literate}
-import Test.Hspec
-import Control.Exception
-
-main :: IO ()
-main = hspec $ do
-
-  describe "shouldBe" $ do
-
-    it "asserts equality" $ do
-      "foo" `shouldBe` "foo"
-
-  describe "shouldSatisfy" $ do
-
-    it "asserts that a predicate holds" $ do
-      "bar" `shouldSatisfy` (not . null)
-
-  describe "shouldThrow" $ do
-
-    it "asserts that an exception is thrown" $ do
-      evaluate (1 `div` 0 :: Int) `shouldThrow` (== DivideByZero)
-~~~
-
-## shouldBe
-
-`shouldBe` is just an alias for HUnit's `@?=`.
-
-## shouldSatisfy
-
-`shouldSatisfy` asserts that some predicate holds for a given value.
-
-~~~ {.haskell}
-"bar" `shouldSatisfy` (not . null)
-~~~
-
-It is similar to HUnit's `assertBool`, but gives a useful error message.
-
-    >>> 23 `shouldSatisfy` (> 42)
-    *** Exception: HUnitFailure "23 did not satisfy predicate!"
-
-## shouldReturn
-
-`shouldReturn` asserts that an action returns a given value.
-
-~~~ {.haskell}
-launchMissiles `shouldReturn` Left "permission error"
-~~~
-
-## shouldThrow
-
-`shouldThrow` asserts that an exception is thrown. The precise nature of
-that exception is described with a `Selector`.
-
-~~~ {.haskell}
-error "foobar" `shouldThrow` anyException
-~~~
-
-A `Selector` is a predicate, it can simultaneously constrain the type
-and value of an exception.
-
-~~~ {.haskell}
-throw DivideByZero `shouldThrow` (== DivideByZero)
-~~~
-
-To select all exceptions of a given type, `const True` can be used.
-
-~~~ {.haskell}
-error "foobar" `shouldThrow` (const True :: Selector ErrorCall)
-~~~
-
-For convenience, predefined selectors for some standard exceptions are
-provided.
-
-~~~ {.haskell}
-error "foobar" `shouldThrow` anyErrorCall
-~~~
-
-Some exceptions (like `ErrorCall`) have no `Eq` instance, so checking
-for a specific value requires pattern matching.
-
-~~~ {.haskell}
-error "foobar" `shouldThrow` (\e -> case e of
-    ErrorCall "foobar" -> True
-    _ -> False
-    )
-~~~
-
-For such exceptions, combinators that construct selectors are provided.
-Each combinator corresponds to a constructor; it takes the same
-arguments, and has the same name (but starting with a lower-case
-letter).
-
-~~~ {.haskell}
-error "foobar" `shouldThrow` errorCall "foobar"
-~~~
diff --git a/hspec-expectations.cabal b/hspec-expectations.cabal
--- a/hspec-expectations.cabal
+++ b/hspec-expectations.cabal
@@ -1,57 +1,62 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.35.2.
+--
+-- see: https://github.com/sol/hpack
+
 name:             hspec-expectations
-version:          0.3.3
+version:          0.8.4
 synopsis:         Catchy combinators for HUnit
-description:      Catchy combinators for HUnit: <https://github.com/sol/hspec-expectations#readme>
+description:      Catchy combinators for HUnit: <https://github.com/hspec/hspec-expectations#readme>
+bug-reports:      https://github.com/hspec/hspec-expectations/issues
 license:          MIT
 license-file:     LICENSE
-copyright:        (c) 2011-2013 Simon Hengel
+copyright:        (c) 2011-2023 Simon Hengel
 author:           Simon Hengel <sol@typeful.net>
 maintainer:       Simon Hengel <sol@typeful.net>
 build-type:       Simple
 category:         Testing
-cabal-version:    >= 1.8
-homepage:         https://github.com/sol/hspec-expectations#readme
+homepage:         https://github.com/hspec/hspec-expectations#readme
 
 source-repository head
   type: git
-  location: https://github.com/sol/hspec-expectations
+  location: https://github.com/hspec/hspec-expectations
 
 library
-  ghc-options:
-      -Wall
-  build-depends:
-      base < 4.8
-    , HUnit
   hs-source-dirs:
       src
+  ghc-options: -Wall
+  build-depends:
+      HUnit
+    , base ==4.*
+    , call-stack
   exposed-modules:
       Test.Hspec.Expectations
-    , Test.Hspec.Expectations.Contrib
+      Test.Hspec.Expectations.Contrib
+  other-modules:
+      Test.Hspec.Expectations.Matcher
+      Paths_hspec_expectations
+  default-language: Haskell2010
 
 test-suite spec
-  main-is:
-      Spec.hs
-  type:
-      exitcode-stdio-1.0
-  ghc-options:
-      -Wall -Werror
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
   hs-source-dirs:
+      test
       src
-    , test
-  build-depends:
-      base
-    , HUnit
-    , silently
-    , hspec       >= 1.3
-
-test-suite readme
-  type:
-      exitcode-stdio-1.0
-  ghc-options:
-      -Wall -Werror -pgmL markdown-unlit -optL haskell+literate
-  main-is:
-      README.lhs
+  ghc-options: -Wall
   build-depends:
-      base
-    , hspec       >= 1.3
-    , markdown-unlit
+      HUnit >=1.5.0.0
+    , base ==4.*
+    , call-stack
+    , nanospec
+  other-modules:
+      Helper
+      Test.Hspec.Expectations.ContribSpec
+      Test.Hspec.Expectations.MatcherSpec
+      Test.Hspec.ExpectationsSpec
+      Test.Hspec.Expectations
+      Test.Hspec.Expectations.Contrib
+      Test.Hspec.Expectations.Matcher
+      Paths_hspec_expectations
+  default-language: Haskell2010
diff --git a/src/Test/Hspec/Expectations.hs b/src/Test/Hspec/Expectations.hs
--- a/src/Test/Hspec/Expectations.hs
+++ b/src/Test/Hspec/Expectations.hs
@@ -1,5 +1,9 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE ImplicitParams #-}
 -- |
--- Introductory documentation: <https://github.com/sol/hspec-expectations#readme>
+-- Introductory documentation: <https://github.com/hspec/hspec-expectations#readme>
 module Test.Hspec.Expectations (
 
 -- * Setting expectations
@@ -7,9 +11,17 @@
 , expectationFailure
 , shouldBe
 , shouldSatisfy
+, shouldStartWith
+, shouldEndWith
 , shouldContain
+, shouldMatchList
 , shouldReturn
 
+, shouldNotBe
+, shouldNotSatisfy
+, shouldNotContain
+, shouldNotReturn
+
 -- * Expecting exceptions
 , shouldThrow
 
@@ -34,48 +46,114 @@
 -- Each combinator corresponds to a constructor; it takes the same arguments,
 -- and has the same name (but starting with a lower-case letter).
 , errorCall
+
+-- * Re-exports
+, HasCallStack
 ) where
 
-import           Prelude
-import           Test.HUnit (Assertion, (@?=), assertBool, assertFailure)
+import qualified Test.HUnit
+import           Test.HUnit ((@?=))
 import           Control.Exception
 import           Data.Typeable
-import           Data.List (isInfixOf)
+import           Data.List
 
-type Expectation = Assertion
+import           Control.Monad (unless)
 
--- | This is just an alias for HUnit's `assertFailure`.
-expectationFailure :: String -> Expectation
-expectationFailure = assertFailure
+import           Test.Hspec.Expectations.Matcher
 
-infix 1 `shouldBe`, `shouldSatisfy`, `shouldReturn`, `shouldThrow`
+#if MIN_VERSION_HUnit(1,4,0)
+import           Data.CallStack (HasCallStack)
+#else
+#if MIN_VERSION_base(4,8,1)
+import qualified GHC.Stack as GHC
+type HasCallStack = (?loc :: GHC.CallStack)
+#else
+import GHC.Exts (Constraint)
+type HasCallStack = (() :: Constraint)
+#endif
+#endif
 
+type Expectation = Test.HUnit.Assertion
+
+expectationFailure :: HasCallStack => String -> Expectation
+expectationFailure = Test.HUnit.assertFailure
+
+expectTrue :: HasCallStack => String -> Bool -> Expectation
+expectTrue msg b = unless b (expectationFailure msg)
+
+infix 1 `shouldBe`, `shouldSatisfy`, `shouldStartWith`, `shouldEndWith`, `shouldContain`, `shouldMatchList`, `shouldReturn`, `shouldThrow`
+infix 1 `shouldNotBe`, `shouldNotSatisfy`, `shouldNotContain`, `shouldNotReturn`
+
 -- |
 -- @actual \`shouldBe\` expected@ sets the expectation that @actual@ is equal
--- to @expected@ (this is just an alias for `@?=`).
-shouldBe :: (Show a, Eq a) => a -> a -> Expectation
+-- to @expected@.
+shouldBe :: (HasCallStack, Show a, Eq a) => a -> a -> Expectation
 actual `shouldBe` expected = actual @?= expected
 
 -- |
 -- @v \`shouldSatisfy\` p@ sets the expectation that @p v@ is @True@.
-shouldSatisfy :: (Show a) => a -> (a -> Bool) -> Expectation
-v `shouldSatisfy` p = assertBool ("predicate failed on: " ++ show v) (p v)
+shouldSatisfy :: (HasCallStack, Show a) => a -> (a -> Bool) -> Expectation
+v `shouldSatisfy` p = expectTrue ("predicate failed on: " ++ show v) (p v)
 
+compareWith :: (HasCallStack, Show a) => (a -> a -> Bool) -> String -> a -> a -> Expectation
+compareWith comparator errorDesc result expected = expectTrue errorMsg (comparator expected result)
+  where
+    errorMsg = show result ++ " " ++ errorDesc ++ " " ++ show expected
+
 -- |
+-- @list \`shouldStartWith\` prefix@ sets the expectation that @list@ starts with @prefix@,
+shouldStartWith :: (HasCallStack, Show a, Eq a) => [a] -> [a] -> Expectation
+shouldStartWith = compareWith isPrefixOf "does not start with"
+
+-- |
+-- @list \`shouldEndWith\` suffix@ sets the expectation that @list@ ends with @suffix@,
+shouldEndWith :: (HasCallStack, Show a, Eq a) => [a] -> [a] -> Expectation
+shouldEndWith = compareWith isSuffixOf "does not end with"
+
+-- |
 -- @list \`shouldContain\` sublist@ sets the expectation that @sublist@ is contained,
--- wholly and intact, anywhere in the second.
-shouldContain :: (Show a, Eq a) => [a] -> [a] -> Expectation
-list `shouldContain` sublist = assertBool errorMsg (sublist `isInfixOf` list)
-  where
-    errorMsg = show list ++ " doesn't contain " ++ show sublist
+-- wholly and intact, anywhere in @list@.
+shouldContain :: (HasCallStack, Show a, Eq a) => [a] -> [a] -> Expectation
+shouldContain = compareWith isInfixOf "does not contain"
 
 -- |
+-- @xs \`shouldMatchList\` ys@ sets the expectation that @xs@ has the same
+-- elements that @ys@ has, possibly in another order
+shouldMatchList :: (HasCallStack, Show a, Eq a) => [a] -> [a] -> Expectation
+xs `shouldMatchList` ys = maybe (return ()) expectationFailure (matchList xs ys)
+
+-- |
 -- @action \`shouldReturn\` expected@ sets the expectation that @action@
 -- returns @expected@.
-shouldReturn :: (Show a, Eq a) => IO a -> a -> Expectation
+shouldReturn :: (HasCallStack, Show a, Eq a) => IO a -> a -> Expectation
 action `shouldReturn` expected = action >>= (`shouldBe` expected)
 
 -- |
+-- @actual \`shouldNotBe\` notExpected@ sets the expectation that @actual@ is not
+-- equal to @notExpected@
+shouldNotBe :: (HasCallStack, Show a, Eq a) => a -> a -> Expectation
+actual `shouldNotBe` notExpected = expectTrue ("not expected: " ++ show actual) (actual /= notExpected)
+
+-- |
+-- @v \`shouldNotSatisfy\` p@ sets the expectation that @p v@ is @False@.
+shouldNotSatisfy :: (HasCallStack, Show a) => a -> (a -> Bool) -> Expectation
+v `shouldNotSatisfy` p = expectTrue ("predicate succeeded on: " ++ show v) ((not . p) v)
+
+-- |
+-- @list \`shouldNotContain\` sublist@ sets the expectation that @sublist@ is not
+-- contained anywhere in @list@.
+shouldNotContain :: (HasCallStack, Show a, Eq a) => [a] -> [a] -> Expectation
+list `shouldNotContain` sublist = expectTrue errorMsg ((not . isInfixOf sublist) list)
+  where
+    errorMsg = show list ++ " does contain " ++ show sublist
+
+-- |
+-- @action \`shouldNotReturn\` notExpected@ sets the expectation that @action@
+-- does not return @notExpected@.
+shouldNotReturn :: (HasCallStack, Show a, Eq a) => IO a -> a -> Expectation
+action `shouldNotReturn` notExpected = action >>= (`shouldNotBe` notExpected)
+
+-- |
 -- A @Selector@ is a predicate; it can simultaneously constrain the type and
 -- value of an exception.
 type Selector a = (a -> Bool)
@@ -84,7 +162,7 @@
 -- @action \`shouldThrow\` selector@ sets the expectation that @action@ throws
 -- an exception.  The precise nature of the expected exception is described
 -- with a 'Selector'.
-shouldThrow :: Exception e => IO a -> Selector e -> Expectation
+shouldThrow :: (HasCallStack, Exception e) => IO a -> Selector e -> Expectation
 action `shouldThrow` p = do
   r <- try action
   case r of
@@ -92,10 +170,10 @@
       expectationFailure $
         "did not get expected exception: " ++ exceptionType
     Left e ->
-      (`assertBool` p e) $
-        "predicate failed on expected exception: " ++ exceptionType ++ " (" ++ show e ++ ")"
+      (`expectTrue` p e) $
+        "predicate failed on expected exception: " ++ exceptionType ++ "\n" ++ show e
   where
-    -- a string repsentation of the expected exception's type
+    -- a string representation of the expected exception's type
     exceptionType = (show . typeOf . instanceOf) p
       where
         instanceOf :: Selector a -> a
@@ -108,7 +186,11 @@
 anyErrorCall = const True
 
 errorCall :: String -> Selector ErrorCall
+#if MIN_VERSION_base(4,9,0)
+errorCall s (ErrorCallWithLocation msg _) = s == msg
+#else
 errorCall s (ErrorCall msg) = s == msg
+#endif
 
 anyIOException :: Selector IOException
 anyIOException = const True
diff --git a/src/Test/Hspec/Expectations/Contrib.hs b/src/Test/Hspec/Expectations/Contrib.hs
--- a/src/Test/Hspec/Expectations/Contrib.hs
+++ b/src/Test/Hspec/Expectations/Contrib.hs
@@ -1,22 +1,60 @@
+{-# LANGUAGE CPP #-}
 -- |
 -- Experimental combinators, that may become part of the main distribution, if
 -- they turn out to be useful for a wider audience.
 module Test.Hspec.Expectations.Contrib (
-
-  module Test.Hspec.Expectations
-
 -- * Predicates
 -- | (useful in combination with `shouldSatisfy`)
-, isLeft
+  isLeft
 , isRight
+
+-- * Annotating expectations
+, annotate
 ) where
 
-import Test.Hspec.Expectations
+import Control.Exception
+import Test.HUnit.Lang (HUnitFailure(..), FailureReason(..))
 
+#if MIN_VERSION_base(4,7,0)
+import Data.Either
+#else
+
 isLeft :: Either a b -> Bool
+{-# DEPRECATED isLeft "use Data.Either.Compat.isLeft from package base-compat instead" #-}
 isLeft (Left  _) = True
 isLeft (Right _) = False
 
 isRight :: Either a b -> Bool
+{-# DEPRECATED isRight "use Data.Either.Compat.isRight from package base-compat instead" #-}
 isRight (Left  _) = False
 isRight (Right _) = True
+#endif
+
+-- |
+-- If you have a test case that has multiple assertions, you can use the
+-- 'annotate' function to provide a string message that will be attached to
+-- the 'Expectation'.
+--
+-- @
+-- describe "annotate" $ do
+--   it "adds the message" $ do
+--     annotate "obvious falsehood" $ do
+--       True `shouldBe` False
+--
+-- ========>
+--
+-- 1) annotate, adds the message
+--       obvious falsehood
+--       expected: False
+--        but got: True
+-- @
+--
+-- @since 0.8.3
+annotate :: String -> IO a -> IO a
+annotate message = handle $ \ (HUnitFailure loc reason) -> throwIO . HUnitFailure loc $ case reason of
+  Reason err -> Reason $ addMessage err
+  ExpectedButGot err expected got -> ExpectedButGot (Just $ maybe message addMessage err) expected got
+  where
+    addMessage err
+      | null err = message
+      | otherwise = message ++ "\n" ++ err
diff --git a/src/Test/Hspec/Expectations/Matcher.hs b/src/Test/Hspec/Expectations/Matcher.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/Hspec/Expectations/Matcher.hs
@@ -0,0 +1,26 @@
+module Test.Hspec.Expectations.Matcher (matchList) where
+
+import           Prelude hiding (showList)
+import           Data.List
+
+matchList :: (Show a, Eq a) => [a] -> [a] -> Maybe String
+xs `matchList` ys
+  | null extra && null missing = Nothing
+  | otherwise = Just (err "")
+  where
+    extra   = xs \\ ys
+    missing = ys \\ xs
+
+    msgAndList msg zs = showString msg . showList zs . showString "\n"
+    optMsgList msg zs = if null zs then id else msgAndList msg zs
+
+    err :: ShowS
+    err =
+        showString "Actual list is not a permutation of expected list!\n"
+      . msgAndList "  expected elements: " ys
+      . msgAndList "  actual elements:   " xs
+      . optMsgList "  missing elements:  " missing
+      . optMsgList "  extra elements:    " extra
+
+showList :: Show a => [a] -> ShowS
+showList xs = showChar '[' . foldr (.) (showChar ']') (intersperse (showString ", ") $ map shows xs)
diff --git a/test/Helper.hs b/test/Helper.hs
new file mode 100644
--- /dev/null
+++ b/test/Helper.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE FlexibleContexts #-}
+
+module Helper where
+
+import           Test.HUnit.Lang
+import           Data.CallStack
+
+-- note: this function expects the callstacks to have identical line
+-- numbers, so you need to be careful to call expectationFailed on the same
+-- line that you throw any exceptions
+expectationFailed :: HasCallStack => FailureReason -> HUnitFailure -> Bool
+expectationFailed msg (HUnitFailure l m) = m == msg && (fmap setColumn l) == (fmap setColumn location)
+  where
+    location = case reverse callStack of
+      [] -> Nothing
+      (_, loc) : _ -> Just loc
+    location :: Maybe SrcLoc
+
+    setColumn loc_ = loc_{srcLocStartCol = 0, srcLocEndCol = 0}
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,1 +1,14 @@
-{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+module Main where
+
+import Test.Hspec
+
+import qualified Test.Hspec.ExpectationsSpec
+import qualified Test.Hspec.Expectations.MatcherSpec
+
+spec :: Spec
+spec = do
+  describe "Test.Hspec.ExpectationsSpec" Test.Hspec.ExpectationsSpec.spec
+  describe "Test.Hspec.Expectations.MatcherSpec" Test.Hspec.Expectations.MatcherSpec.spec
+
+main :: IO ()
+main = hspec spec
diff --git a/test/Test/Hspec/Expectations/ContribSpec.hs b/test/Test/Hspec/Expectations/ContribSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Hspec/Expectations/ContribSpec.hs
@@ -0,0 +1,30 @@
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+module Test.Hspec.Expectations.ContribSpec (spec) where
+
+import           Helper
+
+import           Test.HUnit.Lang
+import           Test.Hspec (Spec, describe, it)
+
+import           Test.Hspec.Expectations
+import           Test.Hspec.Expectations.Contrib
+
+spec :: Spec
+spec = do
+  describe "annotate" $ do
+    it "does not affect the running of a successful test" $ do
+      annotate "obviously correct" $
+        True `shouldBe` True
+
+    it "provides a message on expectation failure" $ do
+      (annotate "obvious falsehood" $ True `shouldBe` False) `shouldThrow` expectationFailed (ExpectedButGot (Just "obvious falsehood") (show False) (show True))
+
+    it "nests messages using newlines" $ do
+      let msg0 = "obvious falsehood"
+          msg1 = "welp"
+          msgs = msg0 ++ "\n" ++ msg1
+      (annotate msg0 $ annotate msg1 $ True `shouldBe` False) `shouldThrow` expectationFailed (ExpectedButGot (Just msgs) (show False) (show True))
+
+    it "appends messages to Reason" $ do
+      (annotate "welp" $ True `shouldSatisfy` (== False)) `shouldThrow` expectationFailed (Reason "welp\npredicate failed on: True")
diff --git a/test/Test/Hspec/Expectations/MatcherSpec.hs b/test/Test/Hspec/Expectations/MatcherSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Hspec/Expectations/MatcherSpec.hs
@@ -0,0 +1,34 @@
+module Test.Hspec.Expectations.MatcherSpec (main, spec) where
+
+import           Test.Hspec
+
+import           Test.Hspec.Expectations.Matcher
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec = do
+  describe "matchList" $ do
+    it "succeeds if arguments are empty lists" $ do
+      matchList [] ([] :: [Int]) `shouldBe` Nothing
+
+    it "succeeds if arguments are equal up to permutation" $ do
+      matchList [1, 2, 2, 3] [3, 2, 1, 2 :: Int] `shouldBe` Nothing
+
+    context "when arguments are not equal up to permutation" $ do
+      it "shows extra elements" $ do
+        [1, 2, 2, 3] `matchList` [1, 2, 3 :: Int] `shouldBe` (Just . unlines) [
+            "Actual list is not a permutation of expected list!"
+          , "  expected elements: [1, 2, 3]"
+          , "  actual elements:   [1, 2, 2, 3]"
+          , "  extra elements:    [2]"
+          ]
+
+      it "shows missing elements" $ do
+        [1, 2, 3] `matchList` [1, 2, 2, 3 :: Int] `shouldBe` (Just . unlines) [
+            "Actual list is not a permutation of expected list!"
+          , "  expected elements: [1, 2, 2, 3]"
+          , "  actual elements:   [1, 2, 3]"
+          , "  missing elements:  [2]"
+          ]
diff --git a/test/Test/Hspec/ExpectationsSpec.hs b/test/Test/Hspec/ExpectationsSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Hspec/ExpectationsSpec.hs
@@ -0,0 +1,105 @@
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+module Test.Hspec.ExpectationsSpec (spec) where
+
+import           Helper
+
+import           Control.Exception
+import           Test.HUnit.Lang
+import           Test.Hspec (Spec, describe, it)
+
+import           Test.Hspec.Expectations
+
+spec :: Spec
+spec = do
+  describe "shouldBe" $ do
+    it "succeeds if arguments are equal" $ do
+      "foo" `shouldBe` "foo"
+
+    it "fails if arguments are not equal" $ do
+      ("foo" `shouldBe` "bar") `shouldThrow` expectationFailed (ExpectedButGot Nothing "\"bar\"" "\"foo\"")
+
+  describe "shouldSatisfy" $ do
+    it "succeeds if value satisfies predicate" $ do
+      "" `shouldSatisfy` null
+
+    it "fails if value does not satisfy predicate" $ do
+      ("foo" `shouldSatisfy` null) `shouldThrow` expectationFailed (Reason "predicate failed on: \"foo\"")
+
+  describe "shouldReturn" $ do
+    it "succeeds if arguments represent equal values" $ do
+      return "foo" `shouldReturn` "foo"
+
+    it "fails if arguments do not represent equal values" $ do
+      (return "foo" `shouldReturn` "bar") `shouldThrow` expectationFailed (ExpectedButGot Nothing "\"bar\"" "\"foo\"")
+
+  describe "shouldStartWith" $ do
+    it "succeeds if second is prefix of first" $ do
+      "hello world" `shouldStartWith` "hello"
+
+    it "fails if second is not prefix of first" $ do
+      ("hello world" `shouldStartWith` "world") `shouldThrow` expectationFailed (Reason "\"hello world\" does not start with \"world\"")
+
+  describe "shouldEndWith" $ do
+    it "succeeds if second is suffix of first" $ do
+      "hello world" `shouldEndWith` "world"
+
+    it "fails if second is not suffix of first" $ do
+      ("hello world" `shouldEndWith` "hello") `shouldThrow` expectationFailed (Reason "\"hello world\" does not end with \"hello\"")
+
+  describe "shouldContain" $ do
+    it "succeeds if second argument is contained in the first" $ do
+      "I'm an hello world message" `shouldContain` "an hello"
+
+    it "fails if first argument does not contain the second" $ do
+      ("foo" `shouldContain` "bar") `shouldThrow` expectationFailed (Reason "\"foo\" does not contain \"bar\"")
+
+  describe "shouldNotBe" $ do
+    it "succeeds if arguments are not equal" $ do
+      "foo" `shouldNotBe` "bar"
+
+    it "fails if arguments are equal" $ do
+      ("foo" `shouldNotBe` "foo") `shouldThrow` expectationFailed (Reason "not expected: \"foo\"")
+
+  describe "shouldNotSatisfy" $ do
+    it "succeeds if value does not satisfy predicate" $ do
+      "bar" `shouldNotSatisfy` null
+
+    it "fails if the value does satisfy predicate" $ do
+      ("" `shouldNotSatisfy` null) `shouldThrow` expectationFailed (Reason "predicate succeeded on: \"\"")
+
+  describe "shouldNotReturn" $ do
+    it "succeeds if arguments does not represent equal values" $ do
+      return "foo" `shouldNotReturn` "bar"
+
+    it "fails if arguments do represent equal values" $ do
+      (return "foo" `shouldNotReturn` "foo") `shouldThrow` expectationFailed (Reason "not expected: \"foo\"")
+
+  describe "shouldNotContain" $ do
+    it "succeeds if second argument is not contained in the first" $ do
+      "I'm an hello world message" `shouldNotContain` "test"
+
+    it "fails if first argument does contain the second" $ do
+      ("foo abc def" `shouldNotContain` "def") `shouldThrow` expectationFailed (Reason "\"foo abc def\" does contain \"def\"")
+
+  describe "shouldThrow" $ do
+    it "can be used to require a specific exception" $ do
+      throwIO DivideByZero `shouldThrow` (== DivideByZero)
+
+    it "can be used to require any exception" $ do
+      error "foobar" `shouldThrow` anyException
+
+    it "can be used to require an exception of a specific type" $ do
+      error "foobar" `shouldThrow` anyErrorCall
+
+    it "can be used to require a specific exception" $ do
+      error "foobar" `shouldThrow` errorCall "foobar"
+
+    it "fails, if a required specific exception is not thrown" $ do
+      (throwIO Overflow `shouldThrow` (== DivideByZero)) `shouldThrow` expectationFailed (Reason "predicate failed on expected exception: ArithException\narithmetic overflow")
+
+    it "fails, if any exception is required, but no exception is thrown" $ do
+      (return () `shouldThrow` anyException) `shouldThrow` expectationFailed (Reason "did not get expected exception: SomeException")
+
+    it "fails, if an exception of a specific type is required, but no exception is thrown" $ do
+      (return () `shouldThrow` anyErrorCall) `shouldThrow` expectationFailed (Reason "did not get expected exception: ErrorCall")
