packages feed

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 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: &#34;False&#34;+ but got: &#34;True&#34;+</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>