{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
module Test.Hspec.Golden
( Golden(..)
, defaultGolden
)
where
import Data.IORef
import System.Directory (createDirectoryIfMissing, doesFileExist)
import Test.Hspec.Core.Spec (Example (..), FailureReason (..),
Result (..), ResultStatus (..))
data Golden str =
Golden {
output :: str,
encodePretty :: str -> String,
writeToFile :: FilePath -> str -> IO (),
readFromFile :: FilePath -> IO str,
testName :: String,
directory :: FilePath
}
instance Eq str => Example (Golden str) where
type Arg (Golden str) = ()
evaluateExample e = evaluateExample (\() -> e)
instance Eq str => Example (arg -> Golden str) where
type Arg (arg -> Golden str) = arg
evaluateExample golden _ action _ = do
ref <- newIORef (Result "" Success)
action $ \arg -> do
r <- runGolden (golden arg)
writeIORef ref (fromGoldenResult r)
readIORef ref
fromGoldenResult :: GoldenResult -> Result
fromGoldenResult FirstExecution = Result "First time execution. Golden file created." Success
fromGoldenResult SameOutput = Result "Golden and Actual output hasn't changed" Success
fromGoldenResult (MissmatchOutput expected actual) =
Result "Files golden and actual not match"
(Failure Nothing (ExpectedButGot Nothing expected actual))
defaultGolden :: String -> String -> Golden String
defaultGolden name output_ =
Golden {
output = output_,
encodePretty = show,
testName = name,
writeToFile = writeFile,
readFromFile = readFile,
directory = ".golden"
}
data GoldenResult =
MissmatchOutput String String
| SameOutput
| FirstExecution
runGolden :: Eq str => Golden str -> IO GoldenResult
runGolden Golden{..} =
let goldenTestDir = directory ++ "/" ++ testName
goldenFilePath = goldenTestDir ++ "/" ++ "golden"
actualFilePath = goldenTestDir ++ "/" ++ "actual"
in do
createDirectoryIfMissing True goldenTestDir
goldenFileExist <- doesFileExist goldenFilePath
writeToFile actualFilePath output
if not goldenFileExist
then writeToFile goldenFilePath output
>> return FirstExecution
else do
contentGolden <- readFromFile goldenFilePath
if contentGolden == output
then return SameOutput
else return $ MissmatchOutput (encodePretty contentGolden) (encodePretty output)