Skip to content

Commit 4a855c6

Browse files
authored
Support for non-Identity generator monad (#4)
1 parent 76e1bc6 commit 4a855c6

File tree

11 files changed

+3378
-1461
lines changed

11 files changed

+3378
-1461
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## 0.0.3
9+
10+
## Changed
11+
12+
- `runSpecAndExitProcess'` and `runSpecAndGetResults` now work for any test tree
13+
generator monad, via the new `TestTreeGenerator` class.
14+
815
## 0.0.2
916

1017
### Changed

src/Node.purs

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Effect (Effect)
88
import Effect.Aff (Aff, launchAff_)
99
import Effect.Class (liftEffect)
1010
import Node.Process (exit')
11-
import Test.Spec (Spec)
11+
import Test.Spec (SpecT, Spec)
1212
import Test.Spec.Result (Result)
1313
import Test.Spec.Runner (Reporter)
1414
import Test.Spec.Runner as Spec
@@ -19,6 +19,9 @@ import Test.Spec.Tree (Tree)
1919

2020
-- | Runs the given spec, using configuration derived from CLI options (if any),
2121
-- | and exits the process with an exit indicating success or failure.
22+
-- |
23+
-- | For more control over the configuration or test tree generating monad, use
24+
-- | `runSpecAndExitProcess'`.
2225
runSpecAndExitProcess :: Array Reporter -> Spec Unit -> Effect Unit
2326
runSpecAndExitProcess =
2427
runSpecAndExitProcess' { defaultConfig: Cfg.defaultConfig, parseCLIOptions: true }
@@ -29,12 +32,26 @@ runSpecAndExitProcess =
2932
-- | The `parseCLIOptions` parameter determines whether the `defaultConfig`
3033
-- | should be used as is or CLI options (if any provided) should be applied on
3134
-- | top of it.
32-
runSpecAndExitProcess' :: c.
33-
{ defaultConfig :: Cfg.TestRunConfig' c
34-
, parseCLIOptions :: Boolean
35-
}
35+
-- |
36+
-- | Note that, because this function works for any test tree generator monad
37+
-- | `m`, you will need to specify it somehow. You can either give the spec
38+
-- | parameter an explicit type:
39+
-- |
40+
-- | spec :: SpecT Aff Unit Aff Unit
41+
-- | spec = do
42+
-- | ...
43+
-- |
44+
-- | Or specify the monad via visible type application:
45+
-- |
46+
-- | runSpecAndExitProcess' @Aff ...
47+
-- |
48+
runSpecAndExitProcess' :: @m c.
49+
TestTreeGenerator m
50+
=> { defaultConfig :: Cfg.TestRunConfig' c
51+
, parseCLIOptions :: Boolean
52+
}
3653
-> Array Reporter
37-
-> Spec Unit
54+
-> SpecT Aff Unit m Unit
3855
-> Effect Unit
3956
runSpecAndExitProcess' args reporters spec = launchAff_ do
4057
config <-
@@ -45,9 +62,61 @@ runSpecAndExitProcess' args reporters spec = launchAff_ do
4562
res <- runSpecAndGetResults config reporters spec
4663
liftEffect $ exit' $ if successful res then 0 else 1
4764

48-
runSpecAndGetResults :: c. Cfg.TestRunConfig' c -> Array Reporter -> Spec Unit -> Aff (Array (Tree String Void Result))
65+
-- | The core logic of a persistent test run:
66+
-- |
67+
-- | * Runs the spec tree generation in the given monad `m` (which is usually
68+
-- | just `Identity`, but can be different in most complex scenarios)
69+
-- | * Persists results to disk.
70+
-- | * Returns the tree of results.
71+
-- |
72+
runSpecAndGetResults :: c m
73+
. TestTreeGenerator m
74+
=> Cfg.TestRunConfig' c
75+
-> Array Reporter
76+
-> SpecT Aff Unit m Unit
77+
-> Aff (Array (Tree String Void Result))
4978
runSpecAndGetResults config reporters spec = do
5079
specCfg <- Cfg.toSpecConfig config <#> _ { exit = false }
51-
results <- un Identity $ Spec.evalSpecT specCfg reporters spec
80+
results <- generateTestTree $ Spec.evalSpecT specCfg reporters spec
5281
Persist.persistResults results
5382
pure results
83+
84+
-- | A monad in which test tree generation happens. This is different from the
85+
-- | monad in which the tests themselves run.
86+
-- |
87+
-- | In most cases the test tree would be generated in `Identity`, making for
88+
-- | deterministic, pure test trees:
89+
-- |
90+
-- | spec :: SpecT Aff Unit Identity Unit
91+
-- | spec = do
92+
-- | it "is a pure test" do
93+
-- | (2 + 2) `shouldEqual` 4
94+
-- |
95+
-- | But in more complicated scenarios, you might want to generate test trees in
96+
-- | a more powerful monad. For example, the following test tree is generated in
97+
-- | the `Effect` monad, utilizing the effectful function `randomInt` to
98+
-- | determine the number of tests to generate:
99+
-- |
100+
-- | spec :: SpecT Aff Unit Effect Unit
101+
-- | spec = do
102+
-- | numTests <- randomInt 1 10
103+
-- | for_ numTests \i -> do
104+
-- | it ("is test number " <> show i) do
105+
-- | (i + i - i) `shouldEqual` i
106+
-- |
107+
-- | This class assumes that the monad can be evaluated without any additional
108+
-- | parameters. This allows for most normal use cases with ergonomic API. For
109+
-- | more complicated cases, where the generator monad requires something extra
110+
-- | (such as `StateT` or `ReaderT`), you can always use the `mapSpecTree`
111+
-- | function to transform the generated test tree before running it.
112+
class Monad m <= TestTreeGenerator m where
113+
-- | Evaluates the test tree generator monad, returning the generated test
114+
-- | tree. See comments on the `TestTreeGenerator` class for more information.
115+
generateTestTree :: a. m (Aff a) -> Aff a
116+
117+
instance TestTreeGenerator Identity where
118+
generateTestTree = un Identity
119+
instance TestTreeGenerator Aff where
120+
generateTestTree = join
121+
instance TestTreeGenerator Effect where
122+
generateTestTree = liftEffect >>> join
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Summary
2+
0/0 tests passed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
✓︎ test number 1
2+
✓︎ test number 2
3+
✓︎ test number 3
4+
5+
Summary
6+
3/3 tests passed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
✓︎ test number 1
2+
✓︎ test number 2
3+
✓︎ test number 3
4+
✓︎ test number 4
5+
✓︎ test number 5
6+
7+
Summary
8+
5/5 tests passed

0 commit comments

Comments
 (0)