hspec-junit-formatter-1.1.2.0: tests/Test/Hspec/JUnit/FormatterSpec.hs
{-# LANGUAGE CPP #-}
module Test.Hspec.JUnit.FormatterSpec
( spec
) where
import Prelude
import Control.Monad (void)
import Data.Char (isSpace)
import qualified Data.Map.Strict as Map
import qualified Data.Text as T
import qualified Example
import System.FilePath ((<.>), (</>))
import System.IO.Temp (withSystemTempDirectory)
import Test.Hspec
import Test.Hspec.Golden
import Test.Hspec.JUnit.Config
import qualified Test.Hspec.JUnit.Formatter as Formatter
import Test.Hspec.Runner
import qualified Text.XML as XML
spec :: Spec
spec = do
it "matches golden file" $
junitGolden "default" id
it "matches golden file with prefixing" $
junitGolden "prefixed" $
setJUnitConfigSourcePathPrefix "lol/monorepo"
-- | Run @Example.spec@ and compare XML to a golden file
junitGolden
:: String
-- ^ Unique name
-> (JUnitConfig -> JUnitConfig)
-- ^ Any modification to make to the 'JUnitConfig' before running
-> IO (Golden XML.Document)
junitGolden name modifyConfig = do
actual <- withSystemTempDirectory "" $ \tmp -> do
let junitConfig =
modifyConfig $
setJUnitConfigOutputDirectory tmp $
setJUnitConfigOutputName "test.xml" $
defaultJUnitConfig "hspec-junit-format"
runSpec' $ Formatter.use junitConfig Example.spec
readNormalizedXML $ tmp </> "test.xml"
pure
Golden
{ output = actual
, encodePretty = show
, writeToFile = XML.writeFile XML.def
, readFromFile = readNormalizedXML
, goldenFile =
"tests" </> "golden" </> name <> "-" <> ghcSuffix <.> "xml"
, actualFile = Nothing
, failFirstTime = False
}
runSpec' :: Spec -> IO ()
runSpec' x = do
(config, forest) <- evalSpec defaultConfig x
void $ runSpecForest forest config
readNormalizedXML :: FilePath -> IO XML.Document
readNormalizedXML = fmap normalizeDoc . XML.readFile XML.def
normalizeDoc :: XML.Document -> XML.Document
normalizeDoc = removeWhitespaceNodes . removeTimeAttributes
removeWhitespaceNodes :: XML.Document -> XML.Document
removeWhitespaceNodes doc =
doc
{ XML.documentRoot = go $ XML.documentRoot doc
}
where
go el =
el {XML.elementNodes = concatMap filterWhitespace $ XML.elementNodes el}
filterWhitespace :: XML.Node -> [XML.Node]
filterWhitespace = \case
XML.NodeElement el -> [XML.NodeElement $ go el]
XML.NodeContent c | T.all isSpace c -> []
n -> [n]
-- | Remove volatile attributes so they don't invalidate comparison
removeTimeAttributes :: XML.Document -> XML.Document
removeTimeAttributes =
removeAttributesByName "time" . removeAttributesByName "timestamp"
removeAttributesByName :: XML.Name -> XML.Document -> XML.Document
removeAttributesByName name doc =
doc
{ XML.documentRoot = go $ XML.documentRoot doc
}
where
go el =
el
{ XML.elementAttributes = Map.delete name $ XML.elementAttributes el
, XML.elementNodes = map (onNodeElement go) $ XML.elementNodes el
}
onNodeElement f = \case
XML.NodeElement el -> XML.NodeElement $ f el
n -> n
-- GHC can change certain aspects, mainly about source-location, so we can
-- incorpate that by tracking separate Golden files as necessary
ghcSuffix :: String
#if __GLASGOW_HASKELL__ >= 900
ghcSuffix = "ghc-9"
#elif __GLASGOW_HASKELL__ >= 800
ghcSuffix = "ghc-8"
#else
-- Fail to compile on other GHCs
#endif