@@ -8,7 +8,7 @@ import Effect (Effect)
88import Effect.Aff (Aff , launchAff_ )
99import Effect.Class (liftEffect )
1010import Node.Process (exit' )
11- import Test.Spec (Spec )
11+ import Test.Spec (SpecT , Spec )
1212import Test.Spec.Result (Result )
1313import Test.Spec.Runner (Reporter )
1414import 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'`.
2225runSpecAndExitProcess :: Array Reporter -> Spec Unit -> Effect Unit
2326runSpecAndExitProcess =
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
3956runSpecAndExitProcess' 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 ))
4978runSpecAndGetResults 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
0 commit comments