{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ConstraintKinds #-}
-- |
-- Stability: unstable
--
-- This module provides access to Hspec's internals.  It is less stable than
-- other parts of the API. For most users @Test.Hspec@ is more suitable!
module Test.Hspec.Core.Spec (

-- * Defining a spec
  it
, specify
, describe
, context
, pending
, pendingWith
, xit
, xspecify
, xdescribe
, xcontext

, focus
, fit
, fspecify
, fdescribe
, fcontext

, parallel
, sequential

-- * The @SpecM@ monad
, Test.Hspec.Core.Spec.Monad.Spec
, Test.Hspec.Core.Spec.Monad.SpecWith
, Test.Hspec.Core.Spec.Monad.SpecM(..)
, Test.Hspec.Core.Spec.Monad.runSpecM
, Test.Hspec.Core.Spec.Monad.fromSpecList
, Test.Hspec.Core.Spec.Monad.runIO
, Test.Hspec.Core.Spec.Monad.mapSpecForest
, Test.Hspec.Core.Spec.Monad.mapSpecItem
, Test.Hspec.Core.Spec.Monad.mapSpecItem_
, Test.Hspec.Core.Spec.Monad.modifyParams
, Test.Hspec.Core.Spec.Monad.modifyConfig
, getSpecDescriptionPath

-- * A type class for examples
, Test.Hspec.Core.Example.Example (..)
, Test.Hspec.Core.Example.Params (..)
, Test.Hspec.Core.Example.defaultParams
, Test.Hspec.Core.Example.ActionWith
, Test.Hspec.Core.Example.Progress
, Test.Hspec.Core.Example.ProgressCallback
, Test.Hspec.Core.Example.Result(..)
, Test.Hspec.Core.Example.ResultStatus (..)
, Test.Hspec.Core.Example.Location (..)
, Test.Hspec.Core.Example.FailureReason (..)
, Test.Hspec.Core.Example.safeEvaluate
, Test.Hspec.Core.Example.safeEvaluateExample

-- * Internal representation of a spec tree
, Test.Hspec.Core.Tree.SpecTree
, Test.Hspec.Core.Tree.Tree (..)
, Test.Hspec.Core.Tree.Item (..)
, Test.Hspec.Core.Tree.specGroup
, Test.Hspec.Core.Tree.specItem
, Test.Hspec.Core.Tree.bimapTree
, Test.Hspec.Core.Tree.bimapForest
, Test.Hspec.Core.Tree.filterTree
, Test.Hspec.Core.Tree.filterForest
, Test.Hspec.Core.Tree.filterTreeWithLabels
, Test.Hspec.Core.Tree.filterForestWithLabels
, Test.Hspec.Core.Tree.pruneTree -- unused
, Test.Hspec.Core.Tree.pruneForest -- unused
, Test.Hspec.Core.Tree.location

, focusForest

-- * Re-exports
, HasCallStack
, Expectation
) where

import           Prelude ()
import           Test.Hspec.Core.Compat

import           Control.Monad.Trans.Class (lift)
import           Control.Monad.Trans.Reader (asks)

import           Test.Hspec.Expectations (Expectation)

import           Test.Hspec.Core.Example
import           Test.Hspec.Core.Hooks
import           Test.Hspec.Core.Tree
import           Test.Hspec.Core.Spec.Monad

-- | The @describe@ function combines a list of specs into a larger spec.
describe :: HasCallStack => String -> SpecWith a -> SpecWith a
describe :: forall a. HasCallStack => String -> SpecWith a -> SpecWith a
describe String
label = forall a r. (Env -> Env) -> SpecM a r -> SpecM a r
withEnv Env -> Env
pushLabel forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b r.
([SpecTree a] -> [SpecTree b]) -> SpecM a r -> SpecM b r
mapSpecForest (forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. HasCallStack => String -> [SpecTree a] -> SpecTree a
specGroup String
label)
  where
    pushLabel :: Env -> Env
pushLabel (Env [String]
labels) = [String] -> Env
Env forall a b. (a -> b) -> a -> b
$ String
label forall a. a -> [a] -> [a]
: [String]
labels

-- | @context@ is an alias for `describe`.
context :: HasCallStack => String -> SpecWith a -> SpecWith a
context :: forall a. HasCallStack => String -> SpecWith a -> SpecWith a
context = forall a. HasCallStack => String -> SpecWith a -> SpecWith a
describe

-- |
-- Changing `describe` to `xdescribe` marks all spec items of the corresponding subtree as pending.
--
-- This can be used to temporarily disable spec items.
xdescribe :: HasCallStack => String -> SpecWith a -> SpecWith a
xdescribe :: forall a. HasCallStack => String -> SpecWith a -> SpecWith a
xdescribe String
label SpecWith a
spec = forall a. IO () -> SpecWith a -> SpecWith a
before_ IO ()
pending_ forall a b. (a -> b) -> a -> b
$ forall a. HasCallStack => String -> SpecWith a -> SpecWith a
describe String
label SpecWith a
spec

-- | @xcontext@ is an alias for `xdescribe`.
xcontext :: HasCallStack => String -> SpecWith a -> SpecWith a
xcontext :: forall a. HasCallStack => String -> SpecWith a -> SpecWith a
xcontext = forall a. HasCallStack => String -> SpecWith a -> SpecWith a
xdescribe

-- | The @it@ function creates a spec item.
--
-- A spec item consists of:
--
-- * a textual description of a desired behavior
--
-- * an example for that behavior
--
-- > describe "absolute" $ do
-- >   it "returns a positive number when given a negative number" $
-- >     absolute (-1) == 1
it :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a)
it :: forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
label a
action = forall a. [SpecTree a] -> SpecWith a
fromSpecList [forall e.
(HasCallStack, Example e) =>
String -> e -> SpecTree (Arg e)
specItem String
label a
action]

-- | @specify@ is an alias for `it`.
specify :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a)
specify :: forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
specify = forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it

-- |
-- Changing `it` to `xit` marks the corresponding spec item as pending.
--
-- This can be used to temporarily disable a spec item.
xit :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a)
xit :: forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
xit String
label a
action = forall a. IO () -> SpecWith a -> SpecWith a
before_ IO ()
pending_ forall a b. (a -> b) -> a -> b
$ forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
label a
action

-- | @xspecify@ is an alias for `xit`.
xspecify :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a)
xspecify :: forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
xspecify = forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
xit

-- | `focus` focuses all spec items of the given spec.
--
-- Applying `focus` to a spec with focused spec items has no effect.
focus :: SpecWith a -> SpecWith a
focus :: forall a. SpecWith a -> SpecWith a
focus = forall a b r.
([SpecTree a] -> [SpecTree b]) -> SpecM a r -> SpecM b r
mapSpecForest forall a. [SpecTree a] -> [SpecTree a]
focusForest

focusForest :: [SpecTree a] -> [SpecTree a]
focusForest :: forall a. [SpecTree a] -> [SpecTree a]
focusForest [SpecTree a]
xs
  | forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any forall a. Item a -> Bool
itemIsFocused) [SpecTree a]
xs = [SpecTree a]
xs
  | Bool
otherwise = forall a b c d. (a -> b) -> (c -> d) -> [Tree a c] -> [Tree b d]
bimapForest forall a. a -> a
id (\ Item a
item -> Item a
item {itemIsFocused :: Bool
itemIsFocused = Bool
True}) [SpecTree a]
xs

-- | @fit@ is an alias for @fmap focus . it@
fit :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a)
fit :: forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
fit = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecWith a -> SpecWith a
focus forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it

-- | @fspecify@ is an alias for `fit`.
fspecify :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a)
fspecify :: forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
fspecify = forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
fit

-- | @fdescribe@ is an alias for @fmap focus . describe@
fdescribe :: HasCallStack => String -> SpecWith a -> SpecWith a
fdescribe :: forall a. HasCallStack => String -> SpecWith a -> SpecWith a
fdescribe = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecWith a -> SpecWith a
focus forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. HasCallStack => String -> SpecWith a -> SpecWith a
describe

-- | @fcontext@ is an alias for `fdescribe`.
fcontext :: HasCallStack => String -> SpecWith a -> SpecWith a
fcontext :: forall a. HasCallStack => String -> SpecWith a -> SpecWith a
fcontext = forall a. HasCallStack => String -> SpecWith a -> SpecWith a
fdescribe

-- | `parallel` marks all spec items of the given spec to be safe for parallel
-- evaluation.
parallel :: SpecWith a -> SpecWith a
parallel :: forall a. SpecWith a -> SpecWith a
parallel = forall a b. (Item a -> Item b) -> SpecWith a -> SpecWith b
mapSpecItem_ (forall a. Bool -> Item a -> Item a
setParallelizable Bool
True)

-- | `sequential` marks all spec items of the given spec to be evaluated sequentially.
sequential :: SpecWith a -> SpecWith a
sequential :: forall a. SpecWith a -> SpecWith a
sequential = forall a b. (Item a -> Item b) -> SpecWith a -> SpecWith b
mapSpecItem_ (forall a. Bool -> Item a -> Item a
setParallelizable Bool
False)

setParallelizable :: Bool -> Item a -> Item a
setParallelizable :: forall a. Bool -> Item a -> Item a
setParallelizable Bool
value Item a
item = Item a
item {itemIsParallelizable :: Maybe Bool
itemIsParallelizable = forall a. Item a -> Maybe Bool
itemIsParallelizable Item a
item forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall a. a -> Maybe a
Just Bool
value}

-- | `pending` can be used to mark a spec item as pending.
--
-- If you want to textually specify a behavior but do not have an example yet,
-- use this:
--
-- > describe "fancyFormatter" $ do
-- >   it "can format text in a way that everyone likes" $
-- >     pending
pending :: HasCallStack => Expectation
pending :: HasCallStack => IO ()
pending = forall e a. Exception e => e -> IO a
throwIO (Maybe Location -> Maybe String -> ResultStatus
Pending HasCallStack => Maybe Location
location forall a. Maybe a
Nothing)

pending_ :: Expectation
pending_ :: IO ()
pending_ = (forall e a. Exception e => e -> IO a
throwIO (Maybe Location -> Maybe String -> ResultStatus
Pending forall a. Maybe a
Nothing forall a. Maybe a
Nothing))

-- |
-- `pendingWith` is similar to `pending`, but it takes an additional string
-- argument that can be used to specify the reason for why the spec item is pending.
pendingWith :: HasCallStack => String -> Expectation
pendingWith :: HasCallStack => String -> IO ()
pendingWith = forall e a. Exception e => e -> IO a
throwIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Location -> Maybe String -> ResultStatus
Pending HasCallStack => Maybe Location
location forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> Maybe a
Just

-- | Get the path of `describe` labels, from the root all the way in to the
-- call-site of this function.
--
-- ==== __Example__
-- >>> :{
-- runSpecM $ do
--   describe "foo" $ do
--     describe "bar" $ do
--       getSpecDescriptionPath >>= runIO . print
-- :}
-- ["foo","bar"]
--
-- @since 2.10.0
getSpecDescriptionPath :: SpecM a [String]
getSpecDescriptionPath :: forall a. SpecM a [String]
getSpecDescriptionPath = forall a r.
WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r -> SpecM a r
SpecM forall a b. (a -> b) -> a -> b
$ forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall a b. (a -> b) -> a -> b
$ forall a. [a] -> [a]
reverse forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) r a. Monad m => (r -> a) -> ReaderT r m a
asks Env -> [String]
envSpecDescriptionPath