hspec-junit-formatter 1.0.2.2 → 1.0.3.0
raw patch · 10 files changed
+219/−33 lines, 10 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Test.Hspec.Core.Runner.Ext: configAddAvailableFormatter :: String -> (FormatConfig -> IO Format) -> Config -> Config
+ Test.Hspec.JUnit: configWithJUnitAvailable :: JUnitConfig -> Config -> Config
+ Test.Hspec.JUnit: hspecJUnit :: Spec -> IO ()
+ Test.Hspec.JUnit: hspecJUnitWith :: Config -> Spec -> IO ()
+ Test.Hspec.JUnit.Config: setJUnitConfigSuiteName :: Text -> JUnitConfig -> JUnitConfig
+ Test.Hspec.JUnit.Config.Env: envJUnitConfig :: IO JUnitConfig
+ Test.Hspec.JUnit.Config.Env: envJUnitEnabled :: IO Bool
Files
- CHANGELOG.md +6/−1
- README.lhs +6/−5
- README.md +6/−5
- hspec-junit-formatter.cabal +4/−1
- library/Test/Hspec/Core/Runner/Ext.hs +27/−0
- library/Test/Hspec/JUnit.hs +54/−5
- library/Test/Hspec/JUnit/Config.hs +4/−0
- library/Test/Hspec/JUnit/Config/Env.hs +48/−0
- tests/Main.hs +3/−16
- tests/golden-prefixed.xml +61/−0
CHANGELOG.md view
@@ -1,6 +1,11 @@-## [_Unreleased_](https://github.com/freckle/hspec-junit-formatter/compare/v1.0.2.2...main)+## [_Unreleased_](https://github.com/freckle/hspec-junit-formatter/compare/v1.0.3.0...main) None++## [v1.0.3.0](https://github.com/freckle/hspec-junit-formatter/compare/v1.0.2.2...v1.0.3.0)++- Add `hspecJUnit` and environment-variable configuration+- Fix `file` attribute not respecting source-path-prefix ## [v1.0.2.2](https://github.com/freckle/hspec-junit-formatter/compare/v1.0.2.1...v1.0.2.2)
README.lhs view
@@ -14,16 +14,17 @@ ```haskell import Test.Hspec-import Test.Hspec.Core.Runner (defaultConfig, hspecWith) import Test.Hspec.JUnit+import System.Environment (setEnv) main :: IO () main = do- let- junitConfig = setJUnitConfigOutputDirectory "/tmp" $ defaultJUnitConfig "my-tests"- hspecConfig = configWithJUnit junitConfig defaultConfig+ -- Most likely done in your CI setup+ setEnv "JUNIT_ENABLED" "1"+ setEnv "JUNIT_OUTPUT_DIRECTORY" "/tmp"+ setEnv "JUNIT_SUITE_NAME" "my-tests" - hspecWith hspecConfig spec+ hspecJUnit spec spec :: Spec spec = describe "Addition" $ do
README.md view
@@ -14,16 +14,17 @@ ```haskell import Test.Hspec-import Test.Hspec.Core.Runner (defaultConfig, hspecWith) import Test.Hspec.JUnit+import System.Environment (setEnv) main :: IO () main = do- let- junitConfig = setJUnitConfigOutputDirectory "/tmp" $ defaultJUnitConfig "my-tests"- hspecConfig = configWithJUnit junitConfig defaultConfig+ -- Most likely done in your CI setup+ setEnv "JUNIT_ENABLED" "1"+ setEnv "JUNIT_OUTPUT_DIRECTORY" "/tmp"+ setEnv "JUNIT_SUITE_NAME" "my-tests" - hspecWith hspecConfig spec+ hspecJUnit spec spec :: Spec spec = describe "Addition" $ do
hspec-junit-formatter.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.12 name: hspec-junit-formatter-version: 1.0.2.2+version: 1.0.3.0 license: MIT license-file: LICENSE copyright: 2021 Renaissance Learning Inc@@ -18,6 +18,7 @@ README.md CHANGELOG.md tests/golden.xml+ tests/golden-prefixed.xml source-repository head type: git@@ -25,9 +26,11 @@ library exposed-modules:+ Test.Hspec.Core.Runner.Ext Test.HSpec.JUnit Test.Hspec.JUnit Test.Hspec.JUnit.Config+ Test.Hspec.JUnit.Config.Env Test.HSpec.JUnit.Render Test.Hspec.JUnit.Render Test.HSpec.JUnit.Schema
+ library/Test/Hspec/Core/Runner/Ext.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE CPP #-}++-- | Compatibility module for 'configAvailableFormatters'+--+-- This feature is only available in hspec >= 2.9. Note that this module doesn't+-- do anything to backport the feature; it just supplies a function that will+-- use it when possible and safely no-op when not.+--+module Test.Hspec.Core.Runner.Ext+ ( configAddAvailableFormatter+ ) where++import Prelude++import Test.Hspec.Core.Format (Format, FormatConfig)+import Test.Hspec.Core.Runner (Config(..))++configAddAvailableFormatter+ :: String -> (FormatConfig -> IO Format) -> Config -> Config+#if MIN_VERSION_hspec_core(2,9,0)+configAddAvailableFormatter name format config = config+ { configAvailableFormatters =+ configAvailableFormatters config <> [(name, format)]+ }+#else+configAddAvailableFormatter _ _ = id+#endif
library/Test/Hspec/JUnit.hs view
@@ -1,5 +1,14 @@ module Test.Hspec.JUnit- ( configWithJUnit+ (+ -- * Runners+ hspecJUnit+ , hspecJUnitWith++ -- * Directly modifying 'Config'+ , configWithJUnit+ , configWithJUnitAvailable++ -- * Actual format function , junitFormat -- * Configuration@@ -22,16 +31,55 @@ import System.FilePath (splitFileName) import Test.Hspec.Core.Format import Test.Hspec.Core.Runner+import Test.Hspec.Core.Runner.Ext+import Test.Hspec.Core.Spec (Spec) import Test.Hspec.JUnit.Config+import Test.Hspec.JUnit.Config.Env import Test.Hspec.JUnit.Render (renderJUnit) import qualified Test.Hspec.JUnit.Schema as Schema import Text.XML.Stream.Render (def, renderBytes) +-- | Like 'hspec' but adds JUNit functionality+--+-- To actually /use/ the JUnit format, you must set @JUNIT_ENABLED=1@ in the+-- environment; by default, this function just behaves like 'hspec'.+--+-- (If using hspec >= 2.9, running tests with @--test-arguments="-f junit"@ also+-- works.)+--+-- All configuration of the JUnit report occurs through environment variables.+--+-- See "Test.Hspec.JUnit.Config" and "Test.Hspec.JUnit.Config.Env".+--+hspecJUnit :: Spec -> IO ()+hspecJUnit = hspecJUnitWith defaultConfig++-- | 'hspecJUnit' but built on a non-default 'Config'+hspecJUnitWith :: Config -> Spec -> IO ()+hspecJUnitWith config spec = do+ junitEnabled <- envJUnitEnabled+ junitConfig <- envJUnitConfig++ let+ modify = if junitEnabled then configWithJUnit junitConfig else id+ base = configWithJUnitAvailable junitConfig config++ hspecWith (modify base) spec+ -- | Modify an Hspec 'Config' to use 'junitFormat' configWithJUnit :: JUnitConfig -> Config -> Config configWithJUnit junitConfig config = config { configFormat = Just $ junitFormat junitConfig } +-- | Modify an Hspec 'Config' to have the 'junitFormat' /available/+--+-- Adds @junit@ to the list of available options for @-f, --format@.+--+-- __NOTE__: This only works with hspec >= 2.9, otherwise it is a no-op.+--+configWithJUnitAvailable :: JUnitConfig -> Config -> Config+configWithJUnitAvailable = configAddAvailableFormatter "junit" . junitFormat+ -- | Hspec 'configFormat' that generates a JUnit report junitFormat :: JUnitConfig -> FormatConfig -> IO Format junitFormat junitConfig _config = pure $ \case@@ -81,7 +129,8 @@ :: (FilePath -> FilePath) -> Text -> Text -> Item -> Schema.TestCase itemToTestCase applyPrefix group name item = Schema.TestCase { testCaseLocation =- toSchemaLocation <$> (itemResultLocation item <|> itemLocation item)+ toSchemaLocation applyPrefix+ <$> (itemResultLocation item <|> itemLocation item) , testCaseClassName = group , testCaseName = name , testCaseDuration = unSeconds $ itemDuration item@@ -133,9 +182,9 @@ Pending mLocation _ -> mLocation Failure mLocation _ -> mLocation -toSchemaLocation :: Location -> Schema.Location-toSchemaLocation Location {..} = Schema.Location- { Schema.locationFile = locationFile+toSchemaLocation :: (FilePath -> FilePath) -> Location -> Schema.Location+toSchemaLocation applyPrefix Location {..} = Schema.Location+ { Schema.locationFile = applyPrefix locationFile , Schema.locationLine = fromIntegral $ max 0 locationLine }
library/Test/Hspec/JUnit/Config.hs view
@@ -6,6 +6,7 @@ , setJUnitConfigOutputDirectory , setJUnitConfigOutputName , setJUnitConfigOutputFile+ , setJUnitConfigSuiteName , setJUnitConfigSourcePathPrefix -- * Use@@ -62,6 +63,9 @@ -- setJUnitConfigOutputFile :: FilePath -> JUnitConfig -> JUnitConfig setJUnitConfigOutputFile x config = config { junitConfigOutputFile = Just x }++setJUnitConfigSuiteName :: Text -> JUnitConfig -> JUnitConfig+setJUnitConfigSuiteName x config = config { junitConfigSuiteName = x } -- | Set a prefix to apply to source paths in the report --
+ library/Test/Hspec/JUnit/Config/Env.hs view
@@ -0,0 +1,48 @@+-- | Load a 'JUnitConfig' using environment variables+module Test.Hspec.JUnit.Config.Env+ ( envJUnitEnabled+ , envJUnitConfig+ ) where++import Prelude++import Data.Semigroup (Endo(..))+import Data.Text (pack)+import System.Directory (getCurrentDirectory)+import System.Environment (lookupEnv)+import System.FilePath (takeBaseName)+import Test.Hspec.JUnit.Config++-- | Is @JUNIT_ENABLED=1@ set in the environment?+envJUnitEnabled :: IO Bool+envJUnitEnabled = (== Just "1") <$> lookupEnv (envPrefix <> "ENABLED")++-- | Produce a 'JUnitConfig' by reading environment variables+--+-- Variable names align with setter functions from "Test.Hspec.JUnit.Config":+--+-- * @JUNIT_OUTPUT_DIRECTORY@ 'setJUnitConfigOutputDirectory'+-- * @JUNIT_OUTPUT_NAME@ 'setJUnitConfigOutputName+-- * and so on+--+envJUnitConfig :: IO JUnitConfig+envJUnitConfig = do+ modify <- appEndo . foldMap Endo <$> sequence+ [ lookupEnvOverride "OUTPUT_DIRECTORY" setJUnitConfigOutputDirectory+ , lookupEnvOverride "OUTPUT_NAME" setJUnitConfigOutputName+ , lookupEnvOverride "OUTPUT_FILE" setJUnitConfigOutputFile+ , lookupEnvOverride "SUITE_NAME" $ setJUnitConfigSuiteName . pack+ , lookupEnvOverride "SOURCE_PATH_PREFIX" setJUnitConfigSourcePathPrefix+ ]++ modify . defaultJUnitConfig . pack . takeBaseName <$> getCurrentDirectory++lookupEnvOverride+ :: String+ -> (String -> JUnitConfig -> JUnitConfig)+ -> IO (JUnitConfig -> JUnitConfig)+lookupEnvOverride name setter =+ maybe id setter <$> lookupEnv (envPrefix <> name)++envPrefix :: String+envPrefix = "JUNIT_"
tests/Main.hs view
@@ -8,7 +8,6 @@ import Control.Monad (void) import Data.Char (isSpace) import qualified Data.Map.Strict as Map-import Data.Text (Text) import qualified Data.Text as T import qualified ExampleSpec import System.FilePath ((</>))@@ -26,15 +25,12 @@ golden <- XML.readFile XML.def "tests/golden.xml" normalizeDoc doc `shouldBe` normalizeDoc golden - it "can prefix source paths" $ do+ it "matches golden file with prefixing" $ do let modify = setJUnitConfigSourcePathPrefix "lol/monorepo" withJUnitReportConfig modify ExampleSpec.spec $ \doc -> do- let- root = XML.documentRoot doc- hasPrefix = ("lol/monorepo/tests/ExampleSpec.hs" `T.isPrefixOf`)-- elementInnerTexts root `shouldSatisfy` all hasPrefix+ golden <- XML.readFile XML.def "tests/golden-prefixed.xml"+ normalizeDoc doc `shouldBe` normalizeDoc golden withJUnitReport :: Spec -> (XML.Document -> IO ()) -> IO () withJUnitReport = withJUnitReportConfig id@@ -89,12 +85,3 @@ onNodeElement f = \case XML.NodeElement el -> XML.NodeElement $ f el n -> n--elementInnerTexts :: XML.Element -> [Text]-elementInnerTexts = concatMap go . XML.elementNodes- where- go :: XML.Node -> [Text]- go = \case- XML.NodeElement el -> elementInnerTexts el- XML.NodeContent x -> [x]- _ -> []
+ tests/golden-prefixed.xml view
@@ -0,0 +1,61 @@+<testsuites name="hspec-junit-format">+ <testsuite+ name="Some section"+ package="Some section"+ id="0"+ hostname="localhost"+ time="0.000032355"+ timestamp="2021-05-17T15:36:10.205608409Z"+ tests="2" failures="1" errors="0" skipped="0">+ <properties/>+ <testcase+ file="lol/monorepo/tests/ExampleSpec.hs"+ line="18"+ name="has a failure"+ classname="Some section"+ time="0.000015537">+ <failure type="error">lol/monorepo/tests/ExampleSpec.hs:18:7++expected: "False"+ but got: "True"+</failure>+ </testcase>+ <testcase+ file="lol/monorepo/tests/ExampleSpec.hs"+ line="15"+ name="has an expectation"+ classname="Some section"+ time="0.000016818"/>+ </testsuite>+ <testsuite+ name="Some section/A grouped context"+ package="Some section/A grouped context"+ id="1"+ time="0.000021009"+ timestamp="2021-05-17T15:36:10.205608409Z"+ hostname="localhost"+ tests="3" failures="0" errors="0" skipped="1">+ <properties/>+ <testcase+ file="lol/monorepo/tests/ExampleSpec.hs"+ line="26"+ name="gets skipped"+ classname="Some section/A grouped context"+ time="0.000006765">+ <skipped>lol/monorepo/tests/ExampleSpec.hs:26:9+some reason</skipped>+ </testcase>+ <testcase+ file="lol/monorepo/tests/ExampleSpec.hs"+ line="23"+ name="also happens in a group"+ classname="Some section/A grouped context"+ time="0.000007421"/>+ <testcase+ file="lol/monorepo/tests/ExampleSpec.hs"+ line="21"+ name="happens in a group"+ classname="Some section/A grouped context"+ time="0.000006823"/>+ </testsuite>+</testsuites>