miso-tests
Copyright(C) 2016-2026 David M. Johnson
LicenseBSD3-style (see the file LICENSE)
MaintainerDavid M. Johnson <code@dmj.io>
Stabilityexperimental
Portabilitynon-portable
Safe HaskellNone
LanguageHaskell2010

Miso.Test

Description

Overview

Miso.Test is a lightweight hspec-style testing framework for miso Component integration tests. It runs in the browser (or a JSDOM environment) via Playwright, giving each test full access to the live DOM.

Tests are written as StateT TestState IO, which means they can call any IO action — including miso DOM operations and FFI calls — directly. Each it block is timed and its result is printed with coloured output. After all tests run, runTests prints a summary and calls exitSuccess or exitFailure so the process exit code reflects the test outcome.

Quick start

import Miso.Test
import Miso.Runtime.Internal (components)
import Data.IORef (writeIORef)
import qualified Data.IntMap.Strict as IM

main :: IO ()
main = runTests $ do
  describe "Arithmetic" $ do
    it "2 + 2 = 4" $
      (2 + 2) `shouldBe` (4 :: Int)
    it "3 /= 4" $
      (3 :: Int) `shouldNotBe` 4

  describe "Component" $
    beforeEach (writeIORef components IM.empty) $ do
      it "mounts without error" $ do
        -- mount a component and assert on DOM state
        pure ()

Combinators

  • describe — group related tests under a label.
  • it — declare a single named test; times it and records pass/fail.
  • beforeEach — run an IO action before every it in the wrapped block.
  • afterEach — run an IO action after every it in the wrapped block.

Assertion primitives

  • x `shouldBe` y — assert x == y; pretty-prints a diff on failure.
  • x `shouldNotBe` y — assert x /= y.
  • x `shouldSatisfy` p — assert that the predicate p x holds.
  • expect f x y — assert f x y; the most general form.

Multiple assertions within a single it block are all evaluated; the block is marked as failed if any assertion fails.

Running tests

runTests :: Test a -> IO () is the entry point. It executes the test tree, prints per-test results and a final summary (pass count, fail count, total tests, total duration, and expect() call count), then exits:

When compiled with -DJSDOM, runTests additionally calls globalThis.initJSDOM() before running any tests, enabling headless DOM testing outside a real browser.

Utilities

  • choose min max — returns a uniformly random Int in [min, max) using the JS getRandomNumber global. Useful for property-style tests that need random inputs.

Types

  • Test a = StateT TestState IO a — the test monad.
  • TestState — internal state tracking per-test timing, pass/fail counts, before/after hooks, and error messages.

See also

Synopsis

Test Combinators

describe Source #

Arguments

:: MisoString

Description of test group

-> Test ()

Group of tests to run

-> Test () 

Used to group a bunch of expectations using it. Testing out will include the test description in its output.

it Source #

Arguments

:: MisoString

Name of test to execute

-> Test ()

Test holding multiple expectations

-> Test () 

Used to make multiple expectations using shouldBe / shouldNotBe.

expect Source #

Arguments

:: (Eq a, Show a) 
=> (a -> a -> Bool)

Comparison predicate; the test passes when this returns True

-> a

Actual value (shown in failure output as Received)

-> a

Expected value (shown in failure output as Expecting)

-> Test () 

Primitive for performing expectations in an it block.

beforeEach Source #

Arguments

:: IO ()

Action to run before each it block in the wrapped group

-> Test ()

Test group to wrap

-> Test () 

Execute a IO action before each it block.

This is useful for scenarios like clearing the global Component state.

afterEach Source #

Arguments

:: IO ()

Action to run after each it block in the wrapped group

-> Test ()

Test group to wrap

-> Test () 

Execute a IO after each it block.

This is useful for scenarios like clearing the global Component state.

shouldBe Source #

Arguments

:: (Show a, Eq a) 
=> a

Actual value

-> a

Expected value it must equal

-> Test () 

Performs an expectation in an it block.

shouldSatisfy Source #

Arguments

:: (Eq a, Show a) 
=> a

Actual value to test

-> (a -> Bool)

Predicate that must hold for the assertion to pass

-> Test () 

Primitive for performing expectations in an it block.

shouldNotBe Source #

Arguments

:: (Show a, Eq a) 
=> a

Actual value

-> a

Value it must NOT equal

-> Test () 

Perform an expectation in an it block.

The complement of shouldBe.

runTests Source #

Arguments

:: Test a

The test tree to execute (built from describe / it blocks)

-> IO () 

Executes a block of tests in describe blocks.

Utils

choose Source #

Arguments

:: Int

min

-> Int

max

-> IO Int 

Return a random integer between the first two provided [min, max)

The maximum is exclusive and the minimum is inclusive

Types

type Test a = StateT TestState IO a Source #

The monad that executes tests

data TestState Source #

Internal type for managing test state