{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
module Test.Hspec.Wai.QuickCheck (
  property
, (==>)

-- * Re-exports
, Arbitrary (..)
, module Test.QuickCheck.Gen

-- * Internals
, Testable (..)
, WaiProperty (..)
) where

import           Test.QuickCheck hiding (Testable, property, (==>))
import qualified Test.QuickCheck as QuickCheck
import           Test.QuickCheck.Gen
import           Network.Wai (Application)

import           Test.Hspec.Wai.Internal

property :: Testable a => a -> (State a, Application) -> Property
property :: a -> (State a, Application) -> Property
property = WaiProperty (State a) -> (State a, Application) -> Property
forall st. WaiProperty st -> (st, Application) -> Property
unWaiProperty (WaiProperty (State a) -> (State a, Application) -> Property)
-> (a -> WaiProperty (State a))
-> a
-> (State a, Application)
-> Property
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> WaiProperty (State a)
forall a. Testable a => a -> WaiProperty (State a)
toProperty

data WaiProperty st = WaiProperty {WaiProperty st -> (st, Application) -> Property
unWaiProperty :: (st, Application) -> Property}

class Testable a where
  type State a
  toProperty :: a -> WaiProperty (State a)

instance Testable (WaiProperty st) where
  type State (WaiProperty st) = st
  toProperty :: WaiProperty st -> WaiProperty (State (WaiProperty st))
toProperty = WaiProperty st -> WaiProperty (State (WaiProperty st))
forall a. a -> a
id

instance Testable (WaiExpectation st) where
  type State (WaiExpectation st) = st
  toProperty :: WaiExpectation st -> WaiProperty (State (WaiExpectation st))
toProperty WaiExpectation st
action = ((st, Application) -> Property) -> WaiProperty st
forall st. ((st, Application) -> Property) -> WaiProperty st
WaiProperty (IO () -> Property
forall prop. Testable prop => prop -> Property
QuickCheck.property (IO () -> Property)
-> ((st, Application) -> IO ()) -> (st, Application) -> Property
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WaiExpectation st -> (st, Application) -> IO ()
forall st a. WaiSession st a -> (st, Application) -> IO a
runWithState WaiExpectation st
action)

instance (Arbitrary a, Show a, Testable prop) => Testable (a -> prop) where
  type State (a -> prop) = State prop
  toProperty :: (a -> prop) -> WaiProperty (State (a -> prop))
toProperty a -> prop
prop = ((State prop, Application) -> Property) -> WaiProperty (State prop)
forall st. ((st, Application) -> Property) -> WaiProperty st
WaiProperty (((State prop, Application) -> Property)
 -> WaiProperty (State prop))
-> ((State prop, Application) -> Property)
-> WaiProperty (State prop)
forall a b. (a -> b) -> a -> b
$ (a -> Property) -> Property
forall prop. Testable prop => prop -> Property
QuickCheck.property ((a -> Property) -> Property)
-> ((State prop, Application) -> a -> Property)
-> (State prop, Application)
-> Property
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((a -> (State prop, Application) -> Property)
-> (State prop, Application) -> a -> Property
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((a -> (State prop, Application) -> Property)
 -> (State prop, Application) -> a -> Property)
-> (a -> (State prop, Application) -> Property)
-> (State prop, Application)
-> a
-> Property
forall a b. (a -> b) -> a -> b
$ WaiProperty (State prop) -> (State prop, Application) -> Property
forall st. WaiProperty st -> (st, Application) -> Property
unWaiProperty (WaiProperty (State prop) -> (State prop, Application) -> Property)
-> (a -> WaiProperty (State prop))
-> a
-> (State prop, Application)
-> Property
forall b c a. (b -> c) -> (a -> b) -> a -> c
. prop -> WaiProperty (State prop)
forall a. Testable a => a -> WaiProperty (State a)
toProperty (prop -> WaiProperty (State prop))
-> (a -> prop) -> a -> WaiProperty (State prop)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> prop
prop)

infixr 0 ==>
(==>) :: Testable prop => Bool -> prop -> WaiProperty (State prop)
==> :: Bool -> prop -> WaiProperty (State prop)
(==>) = (Bool -> Property -> Property)
-> Bool -> prop -> WaiProperty (State prop)
forall prop a.
Testable prop =>
(a -> Property -> Property)
-> a -> prop -> WaiProperty (State prop)
lift Bool -> Property -> Property
forall prop. Testable prop => Bool -> prop -> Property
(QuickCheck.==>)

lift :: Testable prop => (a -> Property -> Property) -> a -> prop -> WaiProperty (State prop)
lift :: (a -> Property -> Property)
-> a -> prop -> WaiProperty (State prop)
lift a -> Property -> Property
f a
a prop
prop = ((State prop, Application) -> Property) -> WaiProperty (State prop)
forall st. ((st, Application) -> Property) -> WaiProperty st
WaiProperty (((State prop, Application) -> Property)
 -> WaiProperty (State prop))
-> ((State prop, Application) -> Property)
-> WaiProperty (State prop)
forall a b. (a -> b) -> a -> b
$ \(State prop, Application)
app -> a -> Property -> Property
f a
a (WaiProperty (State prop) -> (State prop, Application) -> Property
forall st. WaiProperty st -> (st, Application) -> Property
unWaiProperty (prop -> WaiProperty (State prop)
forall a. Testable a => a -> WaiProperty (State a)
toProperty prop
prop) (State prop, Application)
app)