Skip to content

Test Cases

🕙 Start time⌛ Duration
5/11/2026, 3:40:30 PM22.32 s
✅ Passed❌ Failed⏩ Skipped🚧 Todo⚪ Total
Test Suites151010152
Tests476060482

2 passed, 0 failed, 0 skipped, 0 todo, done in 5.355596000000105 s

✅ action hash collision — different actions targeting the same initial state › collisionAutomata
   ✅ dismiss from Prompt transitions to Closed
   ✅ init from Prompt also transitions to Closed (global reset via [*] action)
✅ action hash collision — different actions targeting the same initial state

15 passed, 0 failed, 0 skipped, 0 todo, done in 50.731009000000086 s

✅ automata internal functions tests › currentState functions test
   ✅ returns the initial state ID & name
   ✅ returns the correct state ID & name after a transition
✅ automata internal functions tests › currentAction functions test
   ✅ has no initial action
   ✅ returns the correct action ID & name after a transition
✅ automata internal functions tests › currentCycle functions test
   ✅ initial cycle is 1
   ✅ cycle is incremented after action dispatch
✅ automata internal functions tests › currentEpoch functions test
   ✅ initial epoch is 1
   ✅ epoch is incremented after action dispatch of some automata
✅ automata internal functions tests › all internals captured via diagram reducers
   ✅ captures correct initial stateId and stateName
   ✅ actionId and actionName are null initially
   ✅ cycle is 1 initially
   ✅ captures actionId, actionName after dispatch; stateId/cycle reflect pre-transition values
   ✅ _currentTimestamp returns a positive number
   ✅ _currentTime returns an ISO-8601 string
✅ automata internal functions tests
   ✅ automata is not null

126 passed, 0 failed, 0 skipped, 0 todo, done in 138.48423000000003 s

✅ builtin functions codegen › diff
   ✅ diff(5, 3) = b-a = -2
   ✅ diff(3, 5) = b-a = 2
   ✅ diff(null, 3) throws TypeError
✅ builtin functions codegen › mod
   ✅ mod(10, 3) = 1
   ✅ mod(null, 3) throws TypeError
✅ builtin functions codegen › trunc
   ✅ trunc(1.9) = 1
   ✅ trunc(-1.9) = -1
✅ builtin functions codegen › ceil
   ✅ ceil(1.1) = 2
   ✅ ceil(-1.1) = -1
✅ builtin functions codegen › dec
   ✅ dec(5) = 4
✅ builtin functions codegen › neg
   ✅ neg(5) = -5
   ✅ neg(-3) = 3
✅ builtin functions codegen › inv
   ✅ inv(4) = 0.25
   ✅ inv(2) = 0.5
✅ builtin functions codegen › pow
   ✅ pow(2, 10) = 1024
   ✅ pow(4, 0.5) = 2
✅ builtin functions codegen › isGreater
   ✅ isGreater(5, 3) = true
   ✅ isGreater(3, 5) = false
   ✅ isGreater(3, 3) = false (boundary)
   ✅ isGreater(null, 3) throws
✅ builtin functions codegen › isLess
   ✅ isLess(3, 5) = true
   ✅ isLess(5, 3) = false
   ✅ isLess(3, 3) = false (boundary)
✅ builtin functions codegen › isGreaterOrEqual
   ✅ isGreaterOrEqual(3, 3) = true
   ✅ isGreaterOrEqual(5, 3) = true
   ✅ isGreaterOrEqual(2, 3) = false
✅ builtin functions codegen › isLessOrEqual
   ✅ isLessOrEqual(3, 3) = true
   ✅ isLessOrEqual(2, 3) = true
   ✅ isLessOrEqual(5, 3) = false
✅ builtin functions codegen › isEven / isOdd
   ✅ isEven(4) = true
   ✅ isEven(3) = false
   ✅ isOdd(3) = true
   ✅ isOdd(4) = false
   ✅ isEven(null) throws
✅ builtin functions codegen › isInteger
   ✅ isInteger(3) = true
   ✅ isInteger(3.1) = false
✅ builtin functions codegen › isPositive / isNegative
   ✅ isPositive(1) = true
   ✅ isPositive(-1) = false
   ✅ isPositive(0) = false
   ✅ isNegative(-1) = true
   ✅ isNegative(1) = false
✅ builtin functions codegen › and
   ✅ and(true, true) = true
   ✅ and(true, false) = false
   ✅ and(false, false) = false
✅ builtin functions codegen › or
   ✅ or(false, true) = true
   ✅ or(false, false) = false
✅ builtin functions codegen › none
   ✅ none(false, false) = true
   ✅ none(true, false) = false
✅ builtin functions codegen › if
   ✅ if(true, y, n) = y
   ✅ if(false, y, n) = n
✅ builtin functions codegen › case
   ✅ case: first branch matches
   ✅ case: second branch matches
   ✅ case: fallback when no branch matches
✅ builtin functions codegen › choose
   ✅ choose(0, a, b, c) = a
   ✅ choose(2, a, b, c) = c
✅ builtin functions codegen › repeat
   ✅ repeat(ab, 3) = ababab
✅ builtin functions codegen › padLeft / padRight
   ✅ padLeft(x, 4, 0) = 000x
   ✅ padRight(x, 4, 0) = x000
✅ builtin functions codegen › substr
   ✅ substr(hello, 1, 3) = el
✅ builtin functions codegen › len (string)
   ✅ len(hello) = 5
   ✅ len("") = 0
✅ builtin functions codegen › sin / cos
   ✅ sin(0) = 0
   ✅ cos(0) = 1
✅ builtin functions codegen › ln / lg
   ✅ ln(1) = 0
   ✅ lg(10) = 1
✅ builtin functions codegen › null-safe functions via NullSafe state
   ✅ isNull(null) = true
   ✅ isNull(0) = false
   ✅ isNull("") = false
   ✅ has(null, k) = false
   ✅ has({a:1}, a) = true
   ✅ has({a:1}, b) = false
   ✅ and(null, null) = false (null is falsy)
   ✅ or(null, true) = true
   ✅ none(null, null) = true
   ✅ if(null, y, n) = n (null is falsy)
   ✅ coalesce(null, null, fallback) = fallback
   ✅ coalesce(null, found, other) = found
   ✅ len(null) = 0
✅ builtin functions codegen › collection functions › stats
   ✅ min([3,1,4,1,5]) = 1
   ✅ min([]) = null
   ✅ med([1,2,3,4,5]) = 3
   ✅ med([]) = null
   ✅ sumsq([1,2,3]) = 14
   ✅ sumsq([3,4]) = 25
✅ builtin functions codegen › collection functions › isEqual (deep)
   ✅ isEqual({x:1},{x:1}) = true
   ✅ isEqual({x:1},{x:2}) = false
   ✅ isEqual([1,2],[1,2]) = true
✅ builtin functions codegen › collection functions › setAttr
   ✅ setAttr({a:1}, b, 2) = {a:1, b:2}
   ✅ setAttr({a:1}, a, 99) overwrites
   ✅ setAttr(null, k, 1) = null (lodash _.set returns null for null input)
✅ builtin functions codegen › collection functions › omit
   ✅ omit({a:1, b:2}, b) = {a:1}
   ✅ omit(null, k) = {}
✅ builtin functions codegen › collection functions › pick
   ✅ pick({a:1,b:2,c:3}, a) = {a:1}
   ✅ pick(null, k) = {}
✅ builtin functions codegen › collection functions › merge
   ✅ merge({a:1}, {b:2}) = {a:1, b:2}
   ✅ merge({a:1,b:1}, {b:2}) patch wins
✅ builtin functions codegen › collection functions › pluck
   ✅ pluck([{n:1},{n:2}], n) = [1,2]
   ✅ pluck(null, n) = [] (not a collection)
   ✅ pluck([], n) = []
✅ builtin functions codegen › collection functions › filterBy
   ✅ filterBy([{n:1},{n:2},{n:1}], n, 1) = [{n:1},{n:1}]
   ✅ filterBy(items, n, 99) = [] (no match)
   ✅ filterBy(null, n, 1) = []
✅ builtin functions codegen › collection functions › find
   ✅ find([{n:1},{n:2}], n, 2) = {n:2}
   ✅ find(items, n, 99) = null (no match)
   ✅ find(null, n, 1) = null
✅ builtin functions codegen › collection functions › sample
   ✅ sample([1,2,3,4,5], 2) returns 2 items from source
   ✅ sample([], 2) = []
✅ builtin functions codegen › collection functions › sort
   ✅ sort([3,1,4,1,5]) = [1,1,3,4,5]
   ✅ sort([]) = []
   ✅ sort(null) = []
✅ builtin functions codegen › collection functions › left / right
   ✅ left([1,2,3,4,5], 2) = [1,2]
   ✅ left([], 2) = []
   ✅ left(null, 2) = []
   ✅ right([1,2,3,4,5], 2) = [4,5]
✅ builtin functions codegen › collection functions › indexOf
   ✅ indexOf([10,20,30], 20) = 1
   ✅ indexOf([10,20,30], 99) = -1 (not found)
   ✅ indexOf(null, 20) = -1
✅ builtin functions codegen › collection functions › len (array)
   ✅ len([1,2,3,4,5]) = 5
   ✅ len([]) = 0
   ✅ len(null) = 0
✅ builtin functions codegen › collection functions › reverse
   ✅ reverse([1,2,3]) = [3,2,1]
   ✅ reverse([]) = []
✅ builtin functions codegen › collection functions › keys / values
   ✅ keys({a:1,b:2}) = [a,b]
   ✅ keys(null) = []
   ✅ values({a:1,b:2}) = [1,2]
   ✅ values(null) = []
✅ builtin functions codegen › collection functions
✅ builtin functions codegen

13 passed, 0 failed, 0 skipped, 0 todo, done in 21.00817200000006 s

✅ codegen output › gamePhaseAutomata
   ✅ initial state
   ✅ the context and state do not change with the wrong action.
   ✅ automata must have id
   ✅ automata must have actions
   ✅ automata must have states
   ✅ automata.getState must return numeric state
   ✅ [{"action":77866287,"payload":{}}] -- > 2252048
   ✅ [{"action":77866287,"payload":{}},{"action":81515,"payload":{}}] -- > 69824076
   ✅ [{"action":77866287,"payload":{}},{"action":81515,"payload":{}},{"action":407301981,"payload":{}}] -- > 1730055131
   ✅ [{"action":77866287,"payload":{}},{"action":81515,"payload":{}},{"action":407301981,"payload":{}},{"action":1973300761,"payload":{}}] -- > 1929949911
   ✅ [{"action":77866287,"payload":{}},{"action":81515,"payload":{}},{"action":407301981,"payload":{}},{"action":1973300761,"payload":{}},{"action":1058895409,"payload":{}},{"action":1626434024,"payload":{}}] -- > 1608719668
   ✅ [{"action":77866287,"payload":{}},{"action":81515,"payload":{}},{"action":407301981,"payload":{}},{"action":1973300761,"payload":{}},{"action":1058895409,"payload":{}},{"action":1626434024,"payload":{}},{"action":1757631242,"payload":{}}] -- > 1985829159
   ✅ [{"action":77866287,"payload":{}},{"action":81515,"payload":{}},{"action":407301981,"payload":{}},{"action":1973300761,"payload":{}},{"action":1058895409,"payload":{}},{"action":1626434024,"payload":{}},{"action":1757631242,"payload":{}}] -- > 1985829159
✅ codegen output

38 passed, 0 failed, 0 skipped, 0 todo, done in 2129.0909759999995 s

✅ byPass state
   ✅ should throw an error if the ByPass state has more than 1 transition
   ✅ should throw an error if the ByPass state has no transition
   ✅ should throw an error if the ByPass state has no action [-]
   ✅ payload is not propagated through ByPass
   ✅ the finite automaton must go to the next state after the byPasses state
✅ default assign
   ✅ left assign #{a=string | constant | number | list  | emptyPayload = {} | emptypreviousContext = {} }
   ✅ left assign with previous context and payload #{ a=$payload, $b=#prevContext } prevContext - all expressions
   ✅ right assign with #a,#b = (list, constant, integer, string, payload = {}, prevContext = {})
   ✅ right assign payload $a,$b = (list, constant, integer, string, payload = {}, prevContext = {}
   ✅ in the function call #{a} <= add($a=10, $b=5)
✅ reducers
   ✅ #{a,b} from previous to shortcut, prev:{a:3,b:"str"}
   ✅ #{a,b} shortcut, with missing previus context, "a" and "b" should be null
   ✅ #{a,b} <= #a, #b, with previous context
   ✅ #{a,b} <= #a, #b, previous context is missing
   ✅ #{a,b,c,d} <= payload(integer), payload(string)...
   ✅ #{} <= $payload, without passed payload
   ✅ #{a} <= expressions(string,number,arr)
   ✅ #{a} <= add($a,$b)
✅ constants reference
   ✅ constant reference should be replaced by value in dictionary
   ✅ constant value should be number or string
   ✅ codegen should throw an error if there is no key found in the dictionary.
   ✅ codgen should throw an error if the dictionary is missing
✅ initial
   ✅ the +INIT state must be the initial state for the automata
   ✅ by default start state should be first state in note
✅ default context
   ✅ reducer from [*] shared between states
   ✅ by default, the context should be empty object
✅ functions
   ✅ nested call, mult(avg($list),$count)
   ✅ function calls with mixed argument types (payload, constant, literal)
   ✅ chained function calls
   ✅ function calls with array operations
✅ user defined functions
   ✅ simple function returning a constant value, define/fn () => 3
   ✅ codegen should catch cyclic dependencies and throw an error
			define/lol1 () => kek1()
			define/kek1 () => kek2()
			define/kek2 () => kek3()
			define/kek3 () => kek4()
			define/kek4 () => kek5()
			define/kek5 () => kek6()
			define/kek6 () => lol1()
		
   ✅ the order in which the function is defined has no effect
			define/kek2 () => add(1,2)
			define/lol2 () => kek2()

   ✅ without arguments in a defined function, define/lol3 () => add(1,2)
   ✅ with arguments in a defined function, define/lol4 (a,b) => add(a,b)
   ✅ with arguments and  value,  define/lol5 (a) => add(a,3) 
   ✅ complex function with constants, define/complexFunc (x, y) => add(mult(%%BASE, x), mult(%%MULTIPLIER, y), %%OFFSET)
   ✅ function with passed previous context and payload

9 passed, 0 failed, 0 skipped, 0 todo, done in 58.58941300000015 s

✅ coreLoop with generated WeatherReportAutomata
   ✅ runs Event -> Action -> State and emits Events from State (Pending -> FETCH_WEATHER)
   ✅ resolves to Success on WEATHER_RESOLVED, preserving city/coords and setting result
   ✅ resolves to Error on WEATHER_REJECTED, preserving city/coords and setting error
   ✅ registers destination and unsubscribes correctly
   ✅ registers source; start/stop lifecycle works
   ✅ multiple sources and destinations; stop() stops sources and unbinds destinations
   ✅ idempotent start/stop
   ✅ throws on duplicate ids (automata/source/destination)
   ✅ unregister methods are safe to call twice

13 passed, 0 failed, 0 skipped, 0 todo, done in 34.000552999999854 s

✅ define directive codegen › defineExtended
   ✅ define/constVal () => 42 returns 42
   ✅ define/constVal is stable across dispatches
   ✅ 3-level chain step3(3) = 7
   ✅ 3-level chain step3(0) = 1
   ✅ 3-level chain step3(5) = 11
   ✅ step3(null) throws TypeError (mult rejects null)
✅ define directive codegen › defineForkPredicate
   ✅ amount > 0 -> Valid state
   ✅ amount < 0 -> Invalid state
   ✅ amount = 0 -> Invalid state (boundary: 0 is not > 0)
✅ define directive codegen › defineMultiState
   ✅ state A: double($input=3) -> val=6
   ✅ state B: double(#val) where #val=6 from A -> val=12
   ✅ double($input=10) in A -> val=20, B doubles again -> val=40
   ✅ double(null) in A throws TypeError (mult rejects null)
✅ define directive codegen

11 passed, 0 failed, 0 skipped, 0 todo, done in 485.0477599999999 s

✅ automata Events › event Adapter
   ✅ automata has an Event Adapter
   ✅ event Adapter is pre-loaded with events from diagram
   ✅ can add new Event Emitters to Adapter
   ✅ can add new Event Listeners to Adapter
   ✅ can set new Event Adapter
✅ automata Events › event interactions
   ✅ automata can listen to events dispatched by event bus
   ✅ subscribe/emit in 1 automata
   ✅ state of automata#1 changes after emit from automata#2
   ✅ state of automata#1 does not change on emitting wrong events from automata#2
   ✅ action payload can be filled from incoming event meta information
   ✅ event meta information can be filled from context of the automata
✅ automata Events

16 passed, 0 failed, 0 skipped, 0 todo, done in 42.50260299999991 s

✅ forks › default
   ✅ init
   ✅ automata is in working state
   ✅ transition into END state if condition is TRUE
   ✅ transition back into WORKING state if condition is FALSE
   ✅ state reducer is invoked when transitioning to the same state after the fork
✅ forks › fork with no default path
   ✅ init
   ✅ automata is in working state
   ✅ transition into State1 state if isGreater is TRUE
   ✅ transition into State2 state if isZero is TRUE
   ✅ staying in WORKING state if no condition is TRUE
   ✅ state reducer is NOT invoked when staying on the same state
✅ forks › fork that leads into another fork
   ✅ init
   ✅ automata is in working state
   ✅ transition into State1 state if isGreater is TRUE
   ✅ transition into State2 state if isGreater is FALSE, and isPositive is TRUE
   ✅ transition back into WORKING state as a default path for the second fork, if isGreater and isPositive are FALSE
✅ forks › fork with duplicate default paths is not generated
✅ forks

10 passed, 0 failed, 0 skipped, 0 todo, done in 95.45054399999981 s

✅ jS/TS Function Dictionary › can get functions from a dictionary
   ✅ built-in functions
   ✅ custom functions
✅ jS/TS Function Dictionary › cannot register a function under an already existing key
   ✅ built-in keys
   ✅ custom key
✅ jS/TS Function Dictionary
   ✅ dictionary is not empty
   ✅ cannot get a function under a non-existing key
   ✅ can register custom functions inside of a dictionary
   ✅ custom functions return the same result before and after being added to a dictionary
   ✅ cannot register a function with incorrect key length
   ✅ cannot register a function with incorrect key format

14 passed, 0 failed, 0 skipped, 0 todo, done in 53.8030289999997 s

✅ inject advanced codegen › injectWithDefine
   ✅ sumRange(1, 5) = sum([1,2,3,4]) = 10
   ✅ sumRange(0, 4) = sum([0,1,2,3]) = 6
   ✅ sumRange(5, 5) = sum([]) = 0
   ✅ functionDictionary has range registered
   ✅ functionDictionary has sumRange registered
   ✅ functionDictionary has sumArr registered
✅ inject advanced codegen › injectComplexReturn
   ✅ range(0, 4) stored as [0,1,2,3]
   ✅ makePoint(3, 7) stored as {x:3, y:7}
   ✅ funcA() = A, funcB() = B
   ✅ makePoint(null, null) = {x:null, y:null}
   ✅ range result reused in next dispatch: len([0,1,2,3]) via context is still 4
✅ inject advanced codegen › injectForkPredicate
   ✅ val=5 -> Positive state
   ✅ val=0 -> NonPositive state (boundary: 0 is not > 0)
   ✅ val=-3 -> NonPositive state
✅ inject advanced codegen

8 passed, 0 failed, 0 skipped, 0 todo, done in 452.9971780000001 s

✅ inject functions codegen
   ✅ should successfully inject functions into function dictionary
   ✅ should successfully inject functions from export default dictionary
   ✅ should correctly execute injected functions during state machine transitions
   ✅ should successfully inject default exported function
   ✅ should validate exported function types and reject invalid exports
   ✅ should reject function names that conflict with built-in identifiers
   ✅ should throw error when inject directive is used without function path
   ✅ should throw error when referenced inject function does not exist

3 passed, 0 failed, 0 skipped, 0 todo, done in 230.10282200000006 s

✅ inject functions codegen (TypeScript)
   ✅ emits namespace import for userFunctions when functionFilePath is set
   ✅ registers injected function via userFunctionsDict resolved from namespace import
   ✅ omits namespace import when functionFilePath is not provided

12 passed, 0 failed, 0 skipped, 0 todo, done in 33.976252999999815 s

✅ automata internals via EventBus › timestamp monotonicity
   ✅ _currentTimestamp is non-decreasing across sequential dispatches
   ✅ _currentTimestamp is a positive number on every dispatch
✅ automata internals via EventBus › eventBus-driven epoch and cycle
   ✅ first bus dispatch captures same epoch as construction (pre-increment semantics)
   ✅ second bus dispatch captures epoch incremented by one vs first
   ✅ cycle increments by one on each EventBus dispatch
✅ automata internals via EventBus › two instances subscribed to same event
   ✅ both instances process when the same event is dispatched
   ✅ first-registered instance captures lower epoch than second (insertion order)
   ✅ cycle counters are independent per instance
✅ automata internals via EventBus › chain reaction: A emits -> B reacts
   ✅ chain completes: B dispatches after A emits CHAIN
   ✅ b.epoch === A.epoch + 1 (A dispatches first, B second)
   ✅ each instance dispatches exactly once: A.cycle === B.cycle
   ✅ second chain: epoch increments by 2 per chain and ordering is preserved
✅ automata internals via EventBus

58 passed, 0 failed, 0 skipped, 0 todo, done in 61.80705900000066 s

✅ pure-javascript codegen - state transitions and context › pause / resume
   ✅ paused machine queues dispatches
   ✅ resume flushes queued dispatches
✅ pure-javascript codegen - state transitions and context › disable / enable
   ✅ disabled machine ignores dispatches
   ✅ enabled machine resumes accepting dispatches
✅ pure-javascript codegen - state transitions and context
   ✅ factory returns a non-null instance
   ✅ initial state is Off
   ✅ initial lastAction is null
   ✅ initial currentCycle is 0
   ✅ initial context has counter = null
   ✅ dispatch transitions state from Off to Red
   ✅ dispatch updates lastAction
   ✅ dispatch increments currentCycle
   ✅ counter increments on each dispatch
   ✅ getContext returns state and context snapshot
   ✅ multiple dispatches cycle through states
   ✅ unknown action does not change state
   ✅ destroy clears queue without throwing
✅ pure-javascript codegen - module-level exports
   ✅ exports statesDictionary
   ✅ exports actionsDictionary
   ✅ exports eventDictionary
   ✅ exports actionsMap
   ✅ exports statesMap
   ✅ exports functionDictionary
   ✅ exports getState helper
   ✅ exports getAction helper
   ✅ exports createAction helper
   ✅ exports hasState helper
   ✅ exports createEventBus function
   ✅ factory function is default export
✅ pure-javascript codegen - built-in functions
   ✅ mult built-in: doubled = val * 2
   ✅ if+isGreater built-in: clamps value at 100
   ✅ if+isGreater: passes value through when <= 100
✅ pure-javascript codegen - define directive
   ✅ user-defined function is applied
✅ pure-javascript codegen - inject functions
   ✅ functionDictionary has injected function
   ✅ injected function is callable
✅ pure-javascript codegen - eventAdapter on instance
   ✅ factory instance exposes eventAdapter
   ✅ eventAdapter has addEventEmitter
   ✅ eventAdapter has addEventListener
   ✅ createEventBus is exported
✅ pure-javascript codegen - forks
   ✅ score > 50 routes to High
   ✅ score <= 50 routes to Low
   ✅ score exactly 50 routes to Low
✅ pure-javascript codegen - subscribe via EventBus
   ✅ createEventBus returns [EventBus, automatas, cleanup] tuple
   ✅ subscribe transitions FSM on EventBus.dispatch
   ✅ getEventStack records dispatched events
✅ pure-javascript codegen - emit via EventBus
   ✅ emitter transitions to EMIT_TRIGGER on subscribed event
   ✅ emit dispatches specialEvent back to EventBus
✅ pure-javascript codegen - epoch tracking
   ✅ exports getEpoch function
   ✅ epoch increments on each successful dispatch
   ✅ epoch is shared across instances from same module
   ✅ unknown action does not increment epoch
✅ pure-javascript codegen - internals in context
   ✅ context.cycle reflects pre-dispatch cycle (currentCycle - 1)
   ✅ context.cycle increments on each successful dispatch
   ✅ context.cycle does not change on impossible action
✅ pure-javascript codegen - builtin tree-shaking
   ✅ includes used builtin 'add'
   ✅ excludes unused builtin 'mult'
   ✅ excludes unused builtin 'diff'
   ✅ excludes unused builtin 'isOdd'

49 passed, 0 failed, 0 skipped, 0 todo, done in 60.72674300000017 s

✅ pure-typescript codegen - state transitions and context › pause / resume
   ✅ paused machine queues dispatches
   ✅ resume flushes queued dispatches
✅ pure-typescript codegen - state transitions and context › disable / enable
   ✅ disabled machine ignores dispatches
   ✅ enabled machine resumes accepting dispatches
✅ pure-typescript codegen - state transitions and context
   ✅ factory returns a non-null instance
   ✅ initial state is Off
   ✅ initial lastAction is null
   ✅ initial currentCycle is 0
   ✅ initial context has counter = null
   ✅ dispatch transitions state from Off to Red
   ✅ dispatch updates lastAction
   ✅ dispatch increments currentCycle
   ✅ counter increments on each dispatch
   ✅ getContext returns state and context snapshot
   ✅ multiple dispatches cycle through states
   ✅ unknown action does not change state
   ✅ destroy clears queue without throwing
✅ pure-typescript codegen - .d.ts declarations
   ✅ generates a .d.ts file
   ✅ .d.ts exports statesDictionary
   ✅ .d.ts exports actionsDictionary
   ✅ .d.ts exports factory function
   ✅ .d.ts exports instance type
   ✅ .d.ts has default export
   ✅ .d.ts exports helper functions
   ✅ .d.ts exports createEventBus
✅ pure-typescript codegen - module-level exports
   ✅ exports statesDictionary
   ✅ exports actionsDictionary
   ✅ exports eventDictionary
   ✅ exports getState helper
   ✅ exports getAction helper
   ✅ exports createAction helper
   ✅ exports hasState helper
   ✅ exports createEventBus function
   ✅ factory function is default export
✅ pure-typescript codegen - forks
   ✅ score > 50 routes to High
   ✅ score <= 50 routes to Low
   ✅ score exactly 50 routes to Low
✅ pure-typescript codegen - subscribe via EventBus
   ✅ createEventBus returns [EventBus, automatas, cleanup] tuple
   ✅ subscribe transitions FSM on EventBus.dispatch
   ✅ getEventStack records dispatched events
✅ pure-typescript codegen - emit via EventBus
   ✅ emitter transitions to EMIT_TRIGGER on subscribed event
   ✅ emit dispatches specialEvent back to EventBus
✅ pure-typescript codegen - epoch tracking
   ✅ exports getEpoch function
   ✅ epoch increments on each successful dispatch
   ✅ epoch is shared across instances from same module
   ✅ unknown action does not increment epoch
✅ pure-typescript codegen - internals in context
   ✅ context.cycle reflects pre-dispatch cycle (currentCycle - 1)
   ✅ context.cycle increments on each successful dispatch
   ✅ context.cycle does not change on impossible action

63 passed, 0 failed, 0 skipped, 0 todo, done in 2585.5041440000005 s

✅ python codegen - structural validation
   ✅ generates states_dictionary
   ✅ generates actions_dictionary
   ✅ generates action_to_state_from_state_dict
   ✅ generates Off state hash
   ✅ generates Red state hash
   ✅ generates reducer functions
   ✅ generates _get_default_context
   ✅ generates factory function
   ✅ factory returns dict with state key
   ✅ generates helper functions
   ✅ generates function_dictionary
   ✅ context uses coalesce in default context
   ✅ context uses inc builtin
✅ python codegen - no pydash, selective functions
   ✅ no pydash import in output
   ✅ mult is present in function_dictionary
   ✅ unused functions not emitted (sort not used)
   ✅ event_dictionary is emitted
   ✅ _EventBus class is emitted
   ✅ _EventAdapter class is emitted
   ✅ create_event_bus function is emitted
   ✅ factory returns event_adapter key
   ✅ no pydash in traffic input either
✅ python codegen - builtin functions structural
   ✅ generates mult call in reducer
   ✅ generates is_greater call in reducer
   ✅ generates _if call in reducer
✅ python codegen - define directive structural
   ✅ registers user-defined double function
   ✅ double calls mult
✅ python codegen - inject functions
   ✅ includes injected function assignment
   ✅ injected function body references mult
✅ python codegen - event adapter structural
   ✅ emits event_adapter = _EventAdapter()
   ✅ wires add_event_listener for subscribe
   ✅ wires add_event_emitter for emit
   ✅ event_adapter in factory return dict
   ✅ create_event_bus function is emitted
   ✅ _EventBus class is emitted
   ✅ _EventAdapter class is emitted
   ✅ busReady event in event_dictionary
   ✅ fsmDone event in event_dictionary
✅ python codegen - inject functions execution
   ✅ generated inject file exists
   ✅ injected triple function triples a value
✅ python codegen - execution
   ✅ generated file exists
   ✅ factory creates instance with correct initial state
   ✅ dispatch transitions state from Off to Red
   ✅ dispatch increments current_cycle
   ✅ counter increments on each dispatch via coalesce/inc
   ✅ unknown action does not change state
   ✅ paused machine queues dispatches
   ✅ resume flushes queued dispatches
   ✅ disabled machine ignores dispatches
✅ python codegen - builtin functions execution
   ✅ mult builtin computes correctly
   ✅ isGreater + if builtins clamp correctly
✅ python codegen - define directive execution
   ✅ user-defined double function computes correctly
✅ python codegen - helper functions execution
   ✅ get_state returns correct state value
   ✅ get_action returns correct action value
   ✅ create_action returns correct action packet
   ✅ has_state returns True for current state
   ✅ has_state returns False for non-current state
✅ python codegen - event bus execution: subscribe triggers transition
   ✅ create_event_bus wires two automatas
   ✅ dispatching FsmSignal event triggers Receiver Trigger action
   ✅ cleanup unsubscribes all handlers - dispatch after cleanup has no effect
✅ python codegen - event bus execution: emit propagates to second FSM
   ✅ dispatching Send action on Sender emits FsmSignal which transitions Receiver
✅ python codegen - event dispatch sequence
   ✅ cascading: Sender dispatch -> emit -> Receiver transition in one bus.dispatch call
   ✅ no infinite loop: single dispatch causes exactly one transition, no cascade

16 passed, 0 failed, 6 skipped, 0 todo, done in 38.016267999999855 s

✅ shared behavior - JavaScript
   ✅ starts in initial state
   ✅ after 1 dispatch(STEP): state is B
   ✅ after 2 dispatch(STEP): state is C
   ✅ after 3 dispatch(STEP): state is A
✅ shared behavior - TypeScript
   ✅ starts in initial state
   ✅ after 1 dispatch(STEP): state is B
   ✅ after 2 dispatch(STEP): state is C
   ✅ after 3 dispatch(STEP): state is A
✅ shared behavior - PureJavaScript
   ✅ starts in initial state
   ✅ after 1 dispatch(STEP): state is B
   ✅ after 2 dispatch(STEP): state is C
   ✅ after 3 dispatch(STEP): state is A
✅ shared behavior - PureTypeScript
   ✅ starts in initial state
   ✅ after 1 dispatch(STEP): state is B
   ✅ after 2 dispatch(STEP): state is C
   ✅ after 3 dispatch(STEP): state is A
⏩ shared behavior - Python
   ⏩ starts in initial state A after dispatching GO
   ⏩ after GO + STEP: state is B
   ⏩ after GO + STEP + STEP: state is C
   ⏩ after GO + STEP x3: state is back to A
   ⏩ get_epoch increments with each dispatch
   ⏩ has_state accepts string key