State machine: counter #

This property models a counter as a state machine, checking that the counter only transitions by staying the same, incrementing by 1, or decrementing by 1 (no invalid jumps allowed).

import { extract, always, now, next } from "@antithesishq/bombadil";
export * from "@antithesishq/bombadil/browser/defaults";

const counterValue = extract((state) => {
    const element = state.document.body.querySelector("#counter");
    return parseInt(element?.textContent ?? "0", 10);
});

const unchanged = now(() => {
    const current = counterValue.current;
    return next(() => counterValue.current === current);
});

const increment = now(() => {
    const current = counterValue.current;
    return next(() => counterValue.current === current + 1);
});

const decrement = now(() => {
    const current = counterValue.current;
    return next(() => counterValue.current === current - 1);
});

export const counterStateMachine = 
    always(unchanged.or(increment).or(decrement));

If this specification exports the reload action, the unchanged property becomes relevant.1 Unless this application stored the state of the counter somehow, reloading the page would clear the counter, which this property would catch as a violation.


  1. A state transition that allows for nothing to change is a way of making a property “stutter-invariant”, as it’s called in the literature.↩︎