hspec-formatter-github (empty) → 0.1.0.0
raw patch · 8 files changed
+371/−0 lines, 8 filesdep +basedep +hspecdep +hspec-apisetup-changed
Dependencies added: base, hspec, hspec-api, hspec-core, hspec-formatter-github
Files
- CHANGELOG.md +11/−0
- LICENSE +30/−0
- README.md +4/−0
- Setup.hs +2/−0
- hspec-formatter-github.cabal +131/−0
- src/Test/Hspec/Formatters/GithubAction.hs +52/−0
- src/Test/Hspec/Formatters/GithubAction/Internal.hs +126/−0
- test/Main.hs +15/−0
+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# Changelog for `hspec-formatter-github`++All notable changes to this project will be documented in this file.++The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),+and this project adheres to the+[Haskell Package Versioning Policy](https://pvp.haskell.org/).++## Unreleased++## 0.1.0.0 - 2023-12-07
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Author name here (c) 2023++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Author name here nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,4 @@+# `hspec-formatter-github`++This package provides an `hspec` `Formatter` that emits Github Actions Annotations.+This highlights test failures directly in the PR view.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ hspec-formatter-github.cabal view
@@ -0,0 +1,131 @@+cabal-version: 2.2++-- This file has been generated from package.yaml by hpack version 0.35.2.+--+-- see: https://github.com/sol/hpack++name: hspec-formatter-github+version: 0.1.0.0+synopsis: A Formatter for hspec that provides Github Actions Annotations+description: Please see the README on GitHub at <https://github.com/MercuryTechnologies/hspec-formatter-github#readme>+category: Testing+homepage: https://github.com/MercuryTechnologies/hspec-formatter-github#readme+bug-reports: https://github.com/MercuryTechnologies/hspec-formatter-github/issues+author: Alex Vieth, Matt von Hagen+maintainer: mattp@mercury.com+copyright: 2023 Mercury Technologies+license: BSD-3-Clause+license-file: LICENSE+build-type: Simple+extra-source-files:+ README.md+ CHANGELOG.md++source-repository head+ type: git+ location: https://github.com/MercuryTechnologies/hspec-formatter-github++library+ exposed-modules:+ Test.Hspec.Formatters.GithubAction+ Test.Hspec.Formatters.GithubAction.Internal+ other-modules:+ Paths_hspec_formatter_github+ autogen-modules:+ Paths_hspec_formatter_github+ hs-source-dirs:+ src+ default-extensions:+ BlockArguments+ DataKinds+ DefaultSignatures+ DeriveFunctor+ DeriveGeneric+ DeriveLift+ DerivingStrategies+ DerivingVia+ FlexibleContexts+ FlexibleInstances+ GADTs+ GeneralizedNewtypeDeriving+ ImportQualifiedPost+ InstanceSigs+ LambdaCase+ MultiParamTypeClasses+ MultiWayIf+ NamedFieldPuns+ NegativeLiterals+ NumericUnderscores+ OverloadedLabels+ OverloadedStrings+ PartialTypeSignatures+ PatternSynonyms+ RankNTypes+ RecordWildCards+ ScopedTypeVariables+ StandaloneDeriving+ TypeApplications+ TypeFamilies+ UndecidableInstances+ ViewPatterns+ TypeOperators+ NoForeignFunctionInterface+ ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints+ build-depends:+ base >=4.14 && <5+ , hspec-api+ , hspec-core+ default-language: Haskell2010++test-suite test+ type: exitcode-stdio-1.0+ main-is: Main.hs+ other-modules:+ Paths_hspec_formatter_github+ autogen-modules:+ Paths_hspec_formatter_github+ hs-source-dirs:+ test+ default-extensions:+ BlockArguments+ DataKinds+ DefaultSignatures+ DeriveFunctor+ DeriveGeneric+ DeriveLift+ DerivingStrategies+ DerivingVia+ FlexibleContexts+ FlexibleInstances+ GADTs+ GeneralizedNewtypeDeriving+ ImportQualifiedPost+ InstanceSigs+ LambdaCase+ MultiParamTypeClasses+ MultiWayIf+ NamedFieldPuns+ NegativeLiterals+ NumericUnderscores+ OverloadedLabels+ OverloadedStrings+ PartialTypeSignatures+ PatternSynonyms+ RankNTypes+ RecordWildCards+ ScopedTypeVariables+ StandaloneDeriving+ TypeApplications+ TypeFamilies+ UndecidableInstances+ ViewPatterns+ TypeOperators+ NoForeignFunctionInterface+ ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N -O0+ build-depends:+ base >=4.14 && <5+ , hspec+ , hspec-api+ , hspec-core+ , hspec-formatter-github+ default-language: Haskell2010
+ src/Test/Hspec/Formatters/GithubAction.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE StrictData #-}+{-# LANGUAGE OverloadedStrings #-}++-- | This module provides a utility to allow you to have annotations on+-- test failures in GitHub Actions.+--+-- To use this, you'll call 'registerGithubActionsAnnotations' with+-- a 'Formatter' of your choice.+--+-- @+-- main :: IO ()+-- main = do+-- let hspecConfig =+-- 'registerGithubActionsAnnotations' 'checks' 'defaultConfig'+-- 'hspecWith' hspecConfig spec+-- @+--+-- This will make the @github@ formatter available.+--+-- Then, to actually use the formatter, you can provide the argument+-- @--format=github@ on the command line or in a @.hspec@ file, or the+-- @HSPEC_FORMAT=github@ environment variable.+--+-- To do this locally with @cabal@, you must use @cabal run@, as @cabal+-- test@ evidently does not allow you to pass @--test-arguments@ anymore?+--+-- > $ cabal run $(test-target-name) -- --format=github+--+-- The environment variable is easier to set in CI, probably.+--+-- > $ HSPEC_FORMAT=github cabal test+--+-- And you can use a file, if you really want to, for some reason.+--+-- > $ echo "--format=github" >> .hspec+-- > $ cabal test+module Test.Hspec.Formatters.GithubAction+ ( registerGithubActionsAnnotations+ , withGithubActionFormatter+ -- * Re-exported 'Formatter'+ , silent+ , checks+ ) where++import Test.Hspec.Formatters.GithubAction.Internal+import Test.Hspec.Api.Formatters.V2++-- | This function adds the GitHub Annotations to the underlying+-- 'Formatter'+registerGithubActionsAnnotations :: Formatter -> Config -> Config+registerGithubActionsAnnotations format =+ registerFormatter ("github", withGithubActionFormatter format)
+ src/Test/Hspec/Formatters/GithubAction/Internal.hs view
@@ -0,0 +1,126 @@+{-# LANGUAGE StrictData #-}+{-# LANGUAGE OverloadedStrings #-}++-- | This module is internal! Use at your own risk. Breaking changes to+-- this module will not necessarily be reflected in PVP versions.+module Test.Hspec.Formatters.GithubAction.Internal where++import Prelude+import Control.Applicative+import Test.Hspec.Core.Util (joinPath)+import Test.Hspec.Api.Formatters.V2++-- | This option enhances a given 'Formatter' with annotations that will+-- show up on the correct test item failure. This allows you to use the+-- test output you want, and additionally get Github Action annotations.+withGithubActionFormatter :: Formatter -> Formatter+withGithubActionFormatter fmtr = fmtr+ { formatterItemDone = \path item -> do+ formatterItemDone fmtr path item+ emitGithubActionAnnotation path item+ }++-- | A representation of the Github Actions error format.+data ErrorCommand = ErrorCommand+ { title :: Maybe String+ , file :: Maybe String+ , line :: Maybe Int+ , col :: Maybe Int+ , message :: String+ }++-- | Make a suitable error annotation from an hspec failure.+--+-- Not clear what to do with the Maybe Location here: do we use the one from+-- the Item, or this one? What if both or neither are available?+-- Also not clear whether to use itemInfo.+errorCommandFor :: Path -> Item -> Maybe Location -> FailureReason -> ErrorCommand+errorCommandFor specPath@(_nesting, requirement) item mFailureLocation reason = ErrorCommand+ { -- requirement is used because it should always be non-empty and meaningful+ title = Just requirement+ , file = locationFile <$> mloc+ , line = locationLine <$> mloc+ , col = locationColumn <$> mloc+ , message = unlines (messageHeaderLines ++ messageBodyLines)+ }+ where+ -- prefer the location of the actual failure. if that is not present,+ -- fall back to the test definition location+ mloc = mFailureLocation <|> itemLocation item+ -- Use the path to give a message header, so that the message is never empty.+ -- Empty messages seem to cause github actions to ignore the annotation.+ messageHeaderLines :: [String]+ messageHeaderLines = joinPath specPath : if itemInfo item == "" then [] else [itemInfo item]++ messageBodyLines :: [String]+ messageBodyLines = case reason of+ NoReason -> []+ Reason str -> [str]+ ExpectedButGot preface expected actual ->+ let bodyLines =+ [ mconcat ["expected: ", expected]+ , mconcat [" got: ", actual ]+ ]+ headerLines = maybe [] (\x -> [x]) preface+ in headerLines ++ bodyLines+ Error preface err ->+ let bodyLines = [show err]+ headerLines = maybe [] (\x -> [x]) preface+ in headerLines ++ bodyLines++-- | The github actions command format.+formatErrorCommand :: ErrorCommand -> String+formatErrorCommand ec = mconcat+ [ "\n::error "+ , "title=", maybe "" escapeProperty (title ec), ","+ , "file=", maybe "" escapeProperty (file ec), ","+ , "line=", maybe "" show (line ec), ","+ , "col=", maybe "" show (col ec)+ , "::"+ , escapeData (message ec)+ -- FIXME should check if on windows and use \r\n, as this is what the+ -- github library does.+ , "\n"+ ]++{-+function escapeData(s: any): string {+ return toCommandValue(s)+ .replace(/%/g, '%25')+ .replace(/\r/g, '%0D')+ .replace(/\n/g, '%0A')+}+function escapeProperty(s: any): string {+ return toCommandValue(s)+ .replace(/%/g, '%25')+ .replace(/\r/g, '%0D')+ .replace(/\n/g, '%0A')+ .replace(/:/g, '%3A')+ .replace(/,/g, '%2C')+}+-}++escapeData :: String -> String+escapeData = (>>= replace)+ where+ replace '%' = "%25"+ replace '\r' = "%0D"+ replace '\n' = "%0A"+ replace c = [c]++escapeProperty :: String -> String+escapeProperty = (>>= replace)+ where+ replace '%' = "%25"+ replace '\r' = "%0D"+ replace '\n' = "%0A"+ replace ':' = "%3A"+ replace ',' = "%2C"+ replace c = [c]++-- | If you want to extend+emitGithubActionAnnotation :: Path -> Item -> FormatM ()+emitGithubActionAnnotation path item = case itemResult item of+ Success -> pure ()+ Pending _ _ -> pure ()+ Failure mLoc reason -> write . formatErrorCommand $ errorCommandFor path item mLoc reason
+ test/Main.hs view
@@ -0,0 +1,15 @@+module Main where++import Test.Hspec+import Test.Hspec.Core.Runner+import Test.Hspec.Formatters.GithubAction++main :: IO ()+main = do+ hspecWith (registerGithubActionsAnnotations checks defaultConfig) $ do+ describe "it works" $ do+ it "unmark pending to test" $ do+ pendingWith "test is pending by default"+ expectationFailure "oh no"++