Issie logo Issie

DevHarness Module

 Driving Issie from outside it, for development and for automated checking.

 Published as `window.issieDev` in a debug build, and reached from a terminal through
 `scripts/drive.js`. It exists because the alternative - synthesising DOM events and reading
 rendered text back - is both slow and wrong often enough to mislead: a click has to find the
 right element, a wait has to guess how long a render takes, and text scraped from the DOM can
 be a frame out of date while looking authoritative.

 Four things, which is what that experience says are needed:

   onNextRender   when the update has been applied AND the view has run, so a caller can wait
                  for the app rather than for a stopwatch
   send           a named command, dispatched as the message the UI would have sent
   state          what the app currently is, as data rather than as rendered text
   simRefs        what is holding a simulation, which is the question the DOM cannot answer

 `send` takes a NAME from a fixed table rather than a serialised Msg. A Msg is an F# union
 carrying models, canvases and functions; nothing useful survives a round trip through JSON, and
 a general dispatch-anything surface in a debug build is a hazard for the sake of messages
 nobody wanted to send. Add a row to `commands` when a new one is needed - that is the point.

Functions and values

Function or value Description

forceException kind dispatch

Full Usage: forceException kind dispatch

Parameters:
    kind : string
    dispatch : Msg -> unit

Returns: string

Fire the renderer's exception boundary on purpose, so that what it records - and what Info -> Bug Reports then shows - can be checked rather than assumed. One kind per way an exception can get out of Issie's own code; see the boundary in Renderer.fs. Reached from the Development menu and from `drive.js send forceException `. "view" does not come back: from the render this causes onwards the application is the crash page, which is what a view that throws really does.

kind : string
dispatch : Msg -> unit
Returns: string

forceViewException

Full Usage: forceViewException

Returns: bool

Make the view throw, so that the renderer's exception boundary can be fired on purpose - read by `Renderer.view'` and set by the Development menu and the `forceException` command below. Never cleared. A view is a pure function of the model, so a view that throws once throws on every render after it; a flag that cleared itself would be testing something that cannot happen, and would not leave the crash page up long enough to read. Restarting is the way out, as it is for the real thing. Not model state: it exists to break the model's rendering (docs/mutableState.md).

Returns: bool

publish dispatch

Full Usage: publish dispatch

Parameters:
    dispatch : Msg -> unit

Publish the harness. Debug builds only, by the same test the Development menu uses: this can start simulations and open sheets, which is not something a shipped build should offer a page.

dispatch : Msg -> unit

recordModel model

Full Usage: recordModel model

Parameters:

Called from the view wrapper, before the view runs.

model : Model

renderDone ()

Full Usage: renderDone ()

Parameters:
    () : unit

Called from the view wrapper, after the view has produced its elements. The callbacks run on the next animation frame rather than immediately: the view returning means React has been given the new elements, not that the DOM holds them, and a caller waiting for a render wants the state it can then read to be the state it was waiting for.

() : unit

runOnSidecarWithProgress cycles arraySize topSheet model dispatch

Full Usage: runOnSidecarWithProgress cycles arraySize topSheet model dispatch

Parameters:
    cycles : int
    arraySize : int
    topSheet : string option
    model : Model
    dispatch : Msg -> unit

A long simulation run ON THE SIDECAR, reported through the same progress bar as a local run. The loop is chunked SimRun requests with a time budget each; after every reply it updates the bar and re-checks ownership on the model, so Cancel takes effect within one chunk. The wave-cursor nudge in CancelWaveSimulation touches only the LOCAL simulation and is a no-op when none exists.

cycles : int
arraySize : int
topSheet : string option
model : Model
dispatch : Msg -> unit

sendDesignToSidecar model _dispatch

Full Usage: sendDesignToSidecar model _dispatch

Parameters:
model : Model
_dispatch : Msg -> unit

setSimulateInRenderer inRenderer dispatch

Full Usage: setSimulateInRenderer inRenderer dispatch

Parameters:
    inRenderer : bool
    dispatch : Msg -> unit

Choose which simulator runs, throwing away anything currently simulating. The two backends share nothing - not a step array, not a built simulation - so a switch cannot leave one running and hand it to the other. Everything that holds a simulation is therefore ended first: the step simulator, the waveform simulator and the truth table, which between them are what `ModelHelpers.simulationIsOpen` reports. The flag is then set, and the next simulation the user starts is built by whichever backend is now chosen. Here rather than in the menu because both the Development menu and the `simulateIn` harness command need it, and a switch that stopped simulations in one path and not the other would be a good way to end up with a waveform viewer reading a simulation nobody is running.

inRenderer : bool
dispatch : Msg -> unit

Type something to start searching.