{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Test.Hspec.Core.Spec.Monad (
-- RE-EXPORTED from Test.Hspec.Core.Spec
  Spec
, SpecWith
, SpecM (SpecM)
, runSpecM
, fromSpecList
, runIO

, mapSpecForest
, mapSpecItem
, mapSpecItem_
, modifyParams

, modifyConfig
-- END RE-EXPORTED from Test.Hspec.Core.Spec

, Env(..)
, withEnv
) where

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

import           Control.Arrow
import           Control.Monad.IO.Class (liftIO)
import           Control.Monad.Trans.Reader
import           Control.Monad.Trans.Writer

import           Test.Hspec.Core.Example
import           Test.Hspec.Core.Tree

import           Test.Hspec.Core.Config.Definition (Config)

type Spec = SpecWith ()

type SpecWith a = SpecM a ()

-- |
-- @since 2.10.0
modifyConfig :: (Config -> Config) -> SpecWith a
modifyConfig :: (Config -> Config) -> SpecWith a
modifyConfig Config -> Config
f = WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) ()
-> SpecWith a
forall a r.
WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r -> SpecM a r
SpecM (WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) ()
 -> SpecWith a)
-> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) ()
-> SpecWith a
forall a b. (a -> b) -> a -> b
$ (Endo Config, [SpecTree a])
-> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) ()
forall (m :: * -> *) w. Monad m => w -> WriterT w m ()
tell ((Config -> Config) -> Endo Config
forall a. (a -> a) -> Endo a
Endo Config -> Config
f, [SpecTree a]
forall a. Monoid a => a
mempty)

-- | A writer monad for `SpecTree` forests
newtype SpecM a r = SpecM { SpecM a r -> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
unSpecM :: WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r }
  deriving (a -> SpecM a b -> SpecM a a
(a -> b) -> SpecM a a -> SpecM a b
(forall a b. (a -> b) -> SpecM a a -> SpecM a b)
-> (forall a b. a -> SpecM a b -> SpecM a a) -> Functor (SpecM a)
forall a b. a -> SpecM a b -> SpecM a a
forall a b. (a -> b) -> SpecM a a -> SpecM a b
forall a a b. a -> SpecM a b -> SpecM a a
forall a a b. (a -> b) -> SpecM a a -> SpecM a b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> SpecM a b -> SpecM a a
$c<$ :: forall a a b. a -> SpecM a b -> SpecM a a
fmap :: (a -> b) -> SpecM a a -> SpecM a b
$cfmap :: forall a a b. (a -> b) -> SpecM a a -> SpecM a b
Functor, Functor (SpecM a)
a -> SpecM a a
Functor (SpecM a)
-> (forall a. a -> SpecM a a)
-> (forall a b. SpecM a (a -> b) -> SpecM a a -> SpecM a b)
-> (forall a b c.
    (a -> b -> c) -> SpecM a a -> SpecM a b -> SpecM a c)
-> (forall a b. SpecM a a -> SpecM a b -> SpecM a b)
-> (forall a b. SpecM a a -> SpecM a b -> SpecM a a)
-> Applicative (SpecM a)
SpecM a a -> SpecM a b -> SpecM a b
SpecM a a -> SpecM a b -> SpecM a a
SpecM a (a -> b) -> SpecM a a -> SpecM a b
(a -> b -> c) -> SpecM a a -> SpecM a b -> SpecM a c
forall a. Functor (SpecM a)
forall a. a -> SpecM a a
forall a a. a -> SpecM a a
forall a b. SpecM a a -> SpecM a b -> SpecM a a
forall a b. SpecM a a -> SpecM a b -> SpecM a b
forall a b. SpecM a (a -> b) -> SpecM a a -> SpecM a b
forall a a b. SpecM a a -> SpecM a b -> SpecM a a
forall a a b. SpecM a a -> SpecM a b -> SpecM a b
forall a a b. SpecM a (a -> b) -> SpecM a a -> SpecM a b
forall a b c. (a -> b -> c) -> SpecM a a -> SpecM a b -> SpecM a c
forall a a b c.
(a -> b -> c) -> SpecM a a -> SpecM a b -> SpecM a c
forall (f :: * -> *).
Functor f
-> (forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: SpecM a a -> SpecM a b -> SpecM a a
$c<* :: forall a a b. SpecM a a -> SpecM a b -> SpecM a a
*> :: SpecM a a -> SpecM a b -> SpecM a b
$c*> :: forall a a b. SpecM a a -> SpecM a b -> SpecM a b
liftA2 :: (a -> b -> c) -> SpecM a a -> SpecM a b -> SpecM a c
$cliftA2 :: forall a a b c.
(a -> b -> c) -> SpecM a a -> SpecM a b -> SpecM a c
<*> :: SpecM a (a -> b) -> SpecM a a -> SpecM a b
$c<*> :: forall a a b. SpecM a (a -> b) -> SpecM a a -> SpecM a b
pure :: a -> SpecM a a
$cpure :: forall a a. a -> SpecM a a
$cp1Applicative :: forall a. Functor (SpecM a)
Applicative, Applicative (SpecM a)
a -> SpecM a a
Applicative (SpecM a)
-> (forall a b. SpecM a a -> (a -> SpecM a b) -> SpecM a b)
-> (forall a b. SpecM a a -> SpecM a b -> SpecM a b)
-> (forall a. a -> SpecM a a)
-> Monad (SpecM a)
SpecM a a -> (a -> SpecM a b) -> SpecM a b
SpecM a a -> SpecM a b -> SpecM a b
forall a. Applicative (SpecM a)
forall a. a -> SpecM a a
forall a a. a -> SpecM a a
forall a b. SpecM a a -> SpecM a b -> SpecM a b
forall a b. SpecM a a -> (a -> SpecM a b) -> SpecM a b
forall a a b. SpecM a a -> SpecM a b -> SpecM a b
forall a a b. SpecM a a -> (a -> SpecM a b) -> SpecM a b
forall (m :: * -> *).
Applicative m
-> (forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
return :: a -> SpecM a a
$creturn :: forall a a. a -> SpecM a a
>> :: SpecM a a -> SpecM a b -> SpecM a b
$c>> :: forall a a b. SpecM a a -> SpecM a b -> SpecM a b
>>= :: SpecM a a -> (a -> SpecM a b) -> SpecM a b
$c>>= :: forall a a b. SpecM a a -> (a -> SpecM a b) -> SpecM a b
$cp1Monad :: forall a. Applicative (SpecM a)
Monad)

-- | Convert a `Spec` to a forest of `SpecTree`s.
runSpecM :: SpecWith a -> IO (Endo Config, [SpecTree a])
runSpecM :: SpecWith a -> IO (Endo Config, [SpecTree a])
runSpecM = (ReaderT Env IO (Endo Config, [SpecTree a])
 -> Env -> IO (Endo Config, [SpecTree a]))
-> Env
-> ReaderT Env IO (Endo Config, [SpecTree a])
-> IO (Endo Config, [SpecTree a])
forall a b c. (a -> b -> c) -> b -> a -> c
flip ReaderT Env IO (Endo Config, [SpecTree a])
-> Env -> IO (Endo Config, [SpecTree a])
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT ([String] -> Env
Env []) (ReaderT Env IO (Endo Config, [SpecTree a])
 -> IO (Endo Config, [SpecTree a]))
-> (SpecWith a -> ReaderT Env IO (Endo Config, [SpecTree a]))
-> SpecWith a
-> IO (Endo Config, [SpecTree a])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) ()
-> ReaderT Env IO (Endo Config, [SpecTree a])
forall (m :: * -> *) w a. Monad m => WriterT w m a -> m w
execWriterT (WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) ()
 -> ReaderT Env IO (Endo Config, [SpecTree a]))
-> (SpecWith a
    -> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) ())
-> SpecWith a
-> ReaderT Env IO (Endo Config, [SpecTree a])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SpecWith a
-> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) ()
forall a r.
SpecM a r -> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
unSpecM

-- | Create a `Spec` from a forest of `SpecTree`s.
fromSpecForest :: (Endo Config, [SpecTree a]) -> SpecWith a
fromSpecForest :: (Endo Config, [SpecTree a]) -> SpecWith a
fromSpecForest = WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) ()
-> SpecWith a
forall a r.
WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r -> SpecM a r
SpecM (WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) ()
 -> SpecWith a)
-> ((Endo Config, [SpecTree a])
    -> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) ())
-> (Endo Config, [SpecTree a])
-> SpecWith a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Endo Config, [SpecTree a])
-> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) ()
forall (m :: * -> *) w. Monad m => w -> WriterT w m ()
tell

-- | Create a `Spec` from a forest of `SpecTree`s.
fromSpecList :: [SpecTree a] -> SpecWith a
fromSpecList :: [SpecTree a] -> SpecWith a
fromSpecList = (Endo Config, [SpecTree a]) -> SpecWith a
forall a. (Endo Config, [SpecTree a]) -> SpecWith a
fromSpecForest ((Endo Config, [SpecTree a]) -> SpecWith a)
-> ([SpecTree a] -> (Endo Config, [SpecTree a]))
-> [SpecTree a]
-> SpecWith a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (,) Endo Config
forall a. Monoid a => a
mempty

-- | Run an IO action while constructing the spec tree.
--
-- `SpecM` is a monad to construct a spec tree, without executing any spec
-- items.  @runIO@ allows you to run IO actions during this construction phase.
-- The IO action is always run when the spec tree is constructed (e.g. even
-- when @--dry-run@ is specified).
-- If you do not need the result of the IO action to construct the spec tree,
-- `Test.Hspec.Core.Hooks.beforeAll` may be more suitable for your use case.
runIO :: IO r -> SpecM a r
runIO :: IO r -> SpecM a r
runIO = WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r -> SpecM a r
forall a r.
WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r -> SpecM a r
SpecM (WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
 -> SpecM a r)
-> (IO r -> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r)
-> IO r
-> SpecM a r
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO r -> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO

mapSpecForest :: ([SpecTree a] -> [SpecTree b]) -> SpecM a r -> SpecM b r
mapSpecForest :: ([SpecTree a] -> [SpecTree b]) -> SpecM a r -> SpecM b r
mapSpecForest [SpecTree a] -> [SpecTree b]
f (SpecM WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
specs) = WriterT (Endo Config, [SpecTree b]) (ReaderT Env IO) r -> SpecM b r
forall a r.
WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r -> SpecM a r
SpecM ((ReaderT Env IO (r, (Endo Config, [SpecTree a]))
 -> ReaderT Env IO (r, (Endo Config, [SpecTree b])))
-> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
-> WriterT (Endo Config, [SpecTree b]) (ReaderT Env IO) r
forall (m :: * -> *) a w (n :: * -> *) b w'.
(m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n b
mapWriterT (((r, (Endo Config, [SpecTree a]))
 -> (r, (Endo Config, [SpecTree b])))
-> ReaderT Env IO (r, (Endo Config, [SpecTree a]))
-> ReaderT Env IO (r, (Endo Config, [SpecTree b]))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (((Endo Config, [SpecTree a]) -> (Endo Config, [SpecTree b]))
-> (r, (Endo Config, [SpecTree a]))
-> (r, (Endo Config, [SpecTree b]))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (([SpecTree a] -> [SpecTree b])
-> (Endo Config, [SpecTree a]) -> (Endo Config, [SpecTree b])
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second [SpecTree a] -> [SpecTree b]
f))) WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
specs)

mapSpecItem :: (ActionWith a -> ActionWith b) -> (Item a -> Item b) -> SpecWith a -> SpecWith b
mapSpecItem :: (ActionWith a -> ActionWith b)
-> (Item a -> Item b) -> SpecWith a -> SpecWith b
mapSpecItem ActionWith a -> ActionWith b
_ = (Item a -> Item b) -> SpecWith a -> SpecWith b
forall a b. (Item a -> Item b) -> SpecWith a -> SpecWith b
mapSpecItem_

mapSpecItem_ :: (Item a -> Item b) -> SpecWith a -> SpecWith b
mapSpecItem_ :: (Item a -> Item b) -> SpecWith a -> SpecWith b
mapSpecItem_ = ([SpecTree a] -> [SpecTree b]) -> SpecWith a -> SpecWith b
forall a b r.
([SpecTree a] -> [SpecTree b]) -> SpecM a r -> SpecM b r
mapSpecForest (([SpecTree a] -> [SpecTree b]) -> SpecWith a -> SpecWith b)
-> ((Item a -> Item b) -> [SpecTree a] -> [SpecTree b])
-> (Item a -> Item b)
-> SpecWith a
-> SpecWith b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (IO () -> IO ())
-> (Item a -> Item b) -> [SpecTree a] -> [SpecTree b]
forall a b c d. (a -> b) -> (c -> d) -> [Tree a c] -> [Tree b d]
bimapForest IO () -> IO ()
forall a. a -> a
id

modifyParams :: (Params -> Params) -> SpecWith a -> SpecWith a
modifyParams :: (Params -> Params) -> SpecWith a -> SpecWith a
modifyParams Params -> Params
f = (Item a -> Item a) -> SpecWith a -> SpecWith a
forall a b. (Item a -> Item b) -> SpecWith a -> SpecWith b
mapSpecItem_ ((Item a -> Item a) -> SpecWith a -> SpecWith a)
-> (Item a -> Item a) -> SpecWith a -> SpecWith a
forall a b. (a -> b) -> a -> b
$ \Item a
item -> Item a
item {itemExample :: Params -> (ActionWith a -> IO ()) -> ProgressCallback -> IO Result
itemExample = \Params
p -> (Item a
-> Params
-> (ActionWith a -> IO ())
-> ProgressCallback
-> IO Result
forall a.
Item a
-> Params
-> (ActionWith a -> IO ())
-> ProgressCallback
-> IO Result
itemExample Item a
item) (Params -> Params
f Params
p)}

newtype Env = Env {
  Env -> [String]
envSpecDescriptionPath :: [String]
}

withEnv :: (Env -> Env) -> SpecM a r -> SpecM a r
withEnv :: (Env -> Env) -> SpecM a r -> SpecM a r
withEnv Env -> Env
f = WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r -> SpecM a r
forall a r.
WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r -> SpecM a r
SpecM (WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
 -> SpecM a r)
-> (SpecM a r
    -> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r)
-> SpecM a r
-> SpecM a r
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ReaderT Env IO (r, (Endo Config, [SpecTree a]))
-> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
forall w (m :: * -> *) a. m (a, w) -> WriterT w m a
WriterT (ReaderT Env IO (r, (Endo Config, [SpecTree a]))
 -> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r)
-> (SpecM a r -> ReaderT Env IO (r, (Endo Config, [SpecTree a])))
-> SpecM a r
-> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Env -> Env)
-> ReaderT Env IO (r, (Endo Config, [SpecTree a]))
-> ReaderT Env IO (r, (Endo Config, [SpecTree a]))
forall r (m :: * -> *) a.
(r -> r) -> ReaderT r m a -> ReaderT r m a
local Env -> Env
f (ReaderT Env IO (r, (Endo Config, [SpecTree a]))
 -> ReaderT Env IO (r, (Endo Config, [SpecTree a])))
-> (SpecM a r -> ReaderT Env IO (r, (Endo Config, [SpecTree a])))
-> SpecM a r
-> ReaderT Env IO (r, (Endo Config, [SpecTree a]))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
-> ReaderT Env IO (r, (Endo Config, [SpecTree a]))
forall w (m :: * -> *) a. WriterT w m a -> m (a, w)
runWriterT (WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
 -> ReaderT Env IO (r, (Endo Config, [SpecTree a])))
-> (SpecM a r
    -> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r)
-> SpecM a r
-> ReaderT Env IO (r, (Endo Config, [SpecTree a]))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SpecM a r -> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
forall a r.
SpecM a r -> WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r
unSpecM