packages feed

hspec-tidy-formatter-0.2.0.0: README.md

# `hspec-tidy-formatter`

_A custom [`hspec`] formatter for terminal output._

[![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
![Hackage Version](https://img.shields.io/hackage/v/hspec-tidy-formatter)
![CI](https://img.shields.io/github/actions/workflow/status/carlwr/hspec-tidy-formatter/ci.yml?label=CI)

<br>

<p align="center">
  <ins>left</ins>: this formatter, <ins>right</ins>: hspec default<br>
</p>

![Screenshot](https://raw.githubusercontent.com/carlwr/hspec-tidy-formatter/refs/heads/main/assets/montage.png)

<p align="center">
  <sub>(color choices: &copy;<code>hspec</code>&nbsp;&nbsp;<del>me</del>&nbsp;&nbsp;<del>terminal theme</del>)</sub>
</p>
<br>

<!-- plaintext of montage.png:
---

$  # stack test --test-arguments=--format=tidy
$  # cabal test --test-options=--format=tidy
$  hspec --format=tidy


Data.MyLogic.Core.Instances
  [✔] Functor
  [✔] Monoid
  is Applicative-lawful per
    [✔] Identity
    [✔] Homomorphism
    [✔] Interchange
    [✔] Composition

Data.MyLogic.Evaluator
  [✔] is consistent
  [‐] proves this test to fail
      # PENDING: seems to run forever
                 ->TODO trouble-shoot

Data.MyLogic.TestUtils.Generator
  [✔] Hedgehog generator
      passed 100 tests.
        n:   0       1% ▏···················
        n:   1- 4    5% █···················
        n:   5-19   16% ███▏················
        n:  20-79   54% ██████████▊·········
        n:  80-     24% ████▊···············

Finished in 0.0322 seconds
9 examples, 0 failures, 1 pending

$  █


---

$  # stack test
$  # cabal test
$  hspec


Data.MyLogic.Core.Instances
  Functor [✔]
  Monoid [✔]
  is Applicative-lawful per
    Identity [✔]
      passed 100 tests.
    Homomorphism [✔]
      passed 100 tests.
    Interchange [✔]
      passed 100 tests.
    Composition [✔]
      passed 100 tests.
Data.MyLogic.Evaluator
  is consistent [✔]
    passed 100 tests.
  proves this test to fail [‐]
    # PENDING: seems to run forever
    ->TODO trouble-shoot
Data.MyLogic.TestUtils.Generator
  Hedgehog generator [✔]
    passed 100 tests.
      n:   0       1% ▏···················
      n:   1- 4    5% █···················
      n:   5-19   16% ███▏················
      n:  20-79   54% ██████████▊·········
      n:  80-     24% ████▊···············

Finished in 0.0294 seconds
9 examples, 0 failures, 1 pending

$  █

-->

---

The formatter should work with with any test runner backend. It may be particularly useful with [Hedgehog] tests through [`hspec-hedgehog`]: it omits the _"passed 100 tests."_ otherwise printed after each spec item, yet includes manually added test output (`Hedgehog.collect`, `Hedgehog.label` etc.).


<!--
```haskell
{-# OPTIONS_GHC -Wno-unused-top-binds #-}

-- module Main (main) where
-- import Prelude

```
-->


## How to enable

### With `hspec-discover`

Place a file `SpecHook.hs` in the source directory of the test component:

```haskell
-- --- test/SpecHook.hs ---

import Test.Hspec
import qualified Test.Hspec.TidyFormatter as TidyFormatter

hook :: Spec -> Spec

-- use by default:
hook = TidyFormatter.use

-- to instead use only if requested (`hspec --format=tidy`):
-- hook = TidyFormatter.register
```

`hspec --help` can be used to inspect which formatters `hspec` is aware of:

```diff
 $ cabal test hspec --test-options=--help | grep -A3 FORMATTER

 FORMATTER OPTIONS
   -f NAME  --format=NAME     use a custom formatter; can be one of
-                             checks, specdoc, progress,
+                             checks, specdoc, progress, tidy
                              failed-examples or silent
```

### By modifying `Spec`-s directly

```haskell
main :: IO ()
main = hspec . TidyFormatter.use $ spec

spec :: Spec
spec = it "adds" $ 1+1 `shouldBe` (2::Int)
```


## Functionality, options

* supports transient output/progress

* handles multiline spec tree strings gracefully
  * transient/progress output only prints the first line in case of multiline strings
  * the eventual non-transient output prints all lines, properly indented

* honors most `hspec` options, including: `--times`, `--no-unicode`, `--no-color`, `--print-cpu-time`, `--print-slow-items=[=N]`

### Printing of additional text from test runners

`hspec` allows test runners to pass it additional text together with the outcome of each test. This formatter, by default, prints such text only if it spans more than one line. To instead print all such text unconditionally, use `--times` (default: `--no-times`). This will additionally do what this option is originally supposed to do: print the execution time for spec items (if `> 0` after rounding to milliseconds).

E.g. with [`hspec-hedgehog`], which passes the number of tests run for each item as a single line of text:

```diff
- $  hspec --format=tidy
+ $  hspec --format=tidy --times

  [...]
    is Applicative-lawful per
-     [✔] Identity
+     [✔] Identity  (21ms) (passed 100 tests.)
```

To instead _suppress_ the printing of any additional text from the test runner, use `--expert` (default: `--no-expert`).

### Verbosity switches `--[no-]expert`, `--[no-]times` combinations

_For two spec items whose test runner returns a single line, and a few lines, respectively, of additional text:_

```
===========       =========================================
<ARGS>            `hspec <ARGS>` example output
===========       =========================================


                                             (most verbose)


-----------       -----------------------------------------
--times           [✔] Identity  (21ms) (passed 100 tests.)
--no-expert       [✔] Hedgehog generator  (49ms)
                    passed 100 tests.
                      n:   0       1% ▏···················
                      n:   1- 4    5% █···················
                      n:   5-19   16% ███▏················


-----------       -----------------------------------------
--no-times        [✔] Identity
--no-expert       [✔] Hedgehog generator
(DEFAULT)           passed 100 tests.
                      n:   0       1% ▏···················
                      n:   1- 4    5% █···················
                      n:   5-19   16% ███▏················


-----------       -----------------------------------------
--times           [✔] Identity  (21ms)
--expert          [✔] Hedgehog generator  (49ms)


-----------       -----------------------------------------
--no-times        [✔] Identity
--expert          [✔] Hedgehog generator


                                            (least verbose)

```
<!-- zsh command to produce material for the table:
foreach e (--no-expert --expert) {
foreach t (--times --no-times) {
  echo $t
  echo $e
  command cabal -v0 test test:readme-example --test-options="--no-color --format=tidy $t $e" \
  | awk "NR==6 || (NR>=18 && NR<=22)" \
  | grep -P '^ '
  echo
} }
-->


## Written by a human

During the development of this package, AI models were used extensively for discussions and feedback. All code and documentation however is authored by me (Carl), a human developer: no text (code; natural language) within this package/repo is direct output from an AI model.

Since I am not a native English speaker, any natural language is likely to feature language quirks. AI models were not asked to identify or rectify such.

The above should not be understood as any opinion or even preference of mine - I both use and value development with higher degrees of AI autonomy than what was used in this project.


<!-- links -->

[Hedgehog]: https://hackage.haskell.org/package/hedgehog
[`hspec`]: http://hspec.github.io
[`hspec-hedgehog`]: https://hackage.haskell.org/package/hspec-hedgehog