Documentation
¶
Overview ¶
Package tethertest provides a test harness for tether Handle functions. It invokes the handler directly - no HTTP server, no JSON serialisation, no transport plumbing - so tests see the exact types the handler pushed.
func TestIncrement(t *testing.T) {
h := tethertest.New(tethertest.Config[State]{
Handle: handle,
})
h.Send("increment")
if h.State().Count != 1 {
t.Errorf("got %d, want 1", h.State().Count)
}
}
Error Injection ¶
Set Config.Session to provide a custom tether.CaptureSession for each event dispatch. This lets you test handler behaviour under error conditions that the default harness cannot simulate:
h := tethertest.New(tethertest.Config[State]{
Handle: handle,
Session: func() *tether.CaptureSession {
return &tether.CaptureSession{
SessionID: "test",
PushErr: errors.New("push unavailable"),
}
},
})
Index ¶
- type ComponentHarness
- func (h *ComponentHarness[C]) Announce() string
- func (h *ComponentHarness[C]) Component() C
- func (h *ComponentHarness[C]) Flash() map[string]string
- func (h *ComponentHarness[C]) HTML() string
- func (h *ComponentHarness[C]) HasAnnounce(text string) bool
- func (h *ComponentHarness[C]) HasFlash(selector, text string) bool
- func (h *ComponentHarness[C]) HasSignal(key string, value any) bool
- func (h *ComponentHarness[C]) HasToast(text string) bool
- func (h *ComponentHarness[C]) Mount()
- func (h *ComponentHarness[C]) Replaced() bool
- func (h *ComponentHarness[C]) Send(action string)
- func (h *ComponentHarness[C]) SendEvent(ev tether.Event)
- func (h *ComponentHarness[C]) SendInput(action, value string)
- func (h *ComponentHarness[C]) SendSubmit(action string, data map[string]string)
- func (h *ComponentHarness[C]) Signals() map[string]any
- func (h *ComponentHarness[C]) Title() string
- func (h *ComponentHarness[C]) Toast() string
- func (h *ComponentHarness[C]) URL() string
- type Config
- type Harness
- func (h *Harness[S]) Announce() string
- func (h *Harness[S]) Connect()
- func (h *Harness[S]) Disconnect()
- func (h *Harness[S]) Flash() map[string]string
- func (h *Harness[S]) HTML() string
- func (h *Harness[S]) HasAnnounce(text string) bool
- func (h *Harness[S]) HasFlash(selector, text string) bool
- func (h *Harness[S]) HasSignal(key string, value any) bool
- func (h *Harness[S]) HasToast(text string) bool
- func (h *Harness[S]) MorphKeys() []string
- func (h *Harness[S]) Navigate(path string)
- func (h *Harness[S]) Render() string
- func (h *Harness[S]) RenderNode() node.Node
- func (h *Harness[S]) Replaced() bool
- func (h *Harness[S]) Send(action string)
- func (h *Harness[S]) SendEvent(ev tether.Event)
- func (h *Harness[S]) SendInput(action, value string)
- func (h *Harness[S]) SendSubmit(action string, data map[string]string)
- func (h *Harness[S]) Signals() map[string]any
- func (h *Harness[S]) State() S
- func (h *Harness[S]) Title() string
- func (h *Harness[S]) Toast() string
- func (h *Harness[S]) URL() string
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ComponentHarness ¶
ComponentHarness drives a tether.Component in isolation for testing. Events are dispatched directly to the component's Handle method - no prefix stripping, no wrapper state, no transport plumbing.
h := tethertest.NewComponent(MyWidget{Count: 0})
h.Send("inc")
if h.Component().Count != 1 {
t.Errorf("got %d, want 1", h.Component().Count)
}
func NewComponent ¶
func NewComponent[C tether.Component](initial C) *ComponentHarness[C]
NewComponent creates a test harness for an isolated tether.Component. The component receives events directly - no prefix, no parent state, no getter/setter boilerplate.
func (*ComponentHarness[C]) Announce ¶
func (h *ComponentHarness[C]) Announce() string
Announce returns the accessibility announcement from the most recent event.
func (*ComponentHarness[C]) Component ¶
func (h *ComponentHarness[C]) Component() C
Component returns the current component value.
func (*ComponentHarness[C]) Flash ¶
func (h *ComponentHarness[C]) Flash() map[string]string
Flash returns the flash messages from the most recent event.
func (*ComponentHarness[C]) HTML ¶
func (h *ComponentHarness[C]) HTML() string
HTML returns the component's rendered HTML.
func (*ComponentHarness[C]) HasAnnounce ¶
func (h *ComponentHarness[C]) HasAnnounce(text string) bool
HasAnnounce reports whether the most recent event triggered a matching announcement.
func (*ComponentHarness[C]) HasFlash ¶
func (h *ComponentHarness[C]) HasFlash(selector, text string) bool
HasFlash reports whether the most recent event triggered a matching flash.
func (*ComponentHarness[C]) HasSignal ¶
func (h *ComponentHarness[C]) HasSignal(key string, value any) bool
HasSignal reports whether the most recent event pushed a matching signal.
func (*ComponentHarness[C]) HasToast ¶
func (h *ComponentHarness[C]) HasToast(text string) bool
HasToast reports whether the most recent event triggered a matching toast.
func (*ComponentHarness[C]) Mount ¶
func (h *ComponentHarness[C]) Mount()
Mount triggers the tether.Mounter lifecycle callback. Panics if the component does not implement tether.Mounter.
func (*ComponentHarness[C]) Replaced ¶
func (h *ComponentHarness[C]) Replaced() bool
Replaced reports whether the most recent URL change used ReplaceURL rather than Navigate.
func (*ComponentHarness[C]) Send ¶
func (h *ComponentHarness[C]) Send(action string)
Send fires a click event with the given action.
func (*ComponentHarness[C]) SendEvent ¶
func (h *ComponentHarness[C]) SendEvent(ev tether.Event)
SendEvent fires an arbitrary event and captures the response.
func (*ComponentHarness[C]) SendInput ¶
func (h *ComponentHarness[C]) SendInput(action, value string)
SendInput fires an input event with the given action and value.
func (*ComponentHarness[C]) SendSubmit ¶
func (h *ComponentHarness[C]) SendSubmit(action string, data map[string]string)
SendSubmit fires a submit event with the given action and form data.
func (*ComponentHarness[C]) Signals ¶
func (h *ComponentHarness[C]) Signals() map[string]any
Signals returns the signal values from the most recent event.
func (*ComponentHarness[C]) Title ¶
func (h *ComponentHarness[C]) Title() string
Title returns the title from the most recent event.
func (*ComponentHarness[C]) Toast ¶
func (h *ComponentHarness[C]) Toast() string
Toast returns the toast from the most recent event.
func (*ComponentHarness[C]) URL ¶
func (h *ComponentHarness[C]) URL() string
URL returns the navigation URL from the most recent event.
type Config ¶
type Config[S any] struct { // State is the initial state for each test interaction. State S // Render builds a node tree from the current state. Optional - // only required when calling [Harness.HTML], [Harness.Render], // or [Harness.RenderNode]. Render tether.RenderFunc[S] // Handle processes a client event and returns the new state. Handle func(session tether.Session, state S, event tether.Event) S // Middleware wraps the Handle function with cross-cutting // behaviour. Applied outermost-first: the first entry in the // slice is the outermost layer of the chain. Optional. Middleware []tether.Middleware[S] OnNavigate func(session tether.Session, state S, params tether.Params) S // OnConnect is called when [Harness.Connect] is called. Use this // to test session registration logic (e.g. joining a [tether.Group] // or starting background tasks). Optional. OnConnect func(session tether.Session) // OnDisconnect is called when [Harness.Disconnect] is called. Use // this to test cleanup logic (e.g. removing from a [tether.Group] // or decrementing counters). Optional. OnDisconnect func(session tether.Session) // Components declares component mounts for automatic event routing. // Events matching a mount's prefix are dispatched to the component // before Handle runs - mirroring [tether.StatefulConfig].Components. // Optional. Components []tether.ComponentMount[S] // Layout wraps the rendered content for [Harness.Render] and // [Harness.HTML], mirroring [tether.StatefulConfig].Layout. Optional - // when absent, only the content node is rendered. Layout func(S, node.Node) node.Node // Session provides a custom [tether.CaptureSession] for each // event dispatch. Use this to inject error conditions that the // default harness cannot simulate: // // - Set PushErr to test push notification error handling // - Set Ctx to a cancelled context to test context-aware handlers // // When nil, the harness creates a default CaptureSession with // ID "tethertest" and no error conditions. Session func() *tether.CaptureSession }
Config configures the test harness.
type Harness ¶
type Harness[S any] struct { // contains filtered or unexported fields }
Harness drives a tether handler for testing. Create one with New, send events with Harness.Send or Harness.SendEvent, and inspect the result with the accessor methods.
func New ¶
New creates a test harness. The harness invokes Handle directly - no HTTP server, no JSON round-trip, no goroutines.
Component routing, OnNavigate, and middleware are composed into Handle in the same order as tether.Stateful and tether.Stateless, so all events flow through the full middleware chain.
func (*Harness[S]) Announce ¶
Announce returns the accessibility announcement from the most recent Send call. Returns an empty string if none was triggered.
func (*Harness[S]) Connect ¶
func (h *Harness[S]) Connect()
Connect triggers the OnConnect callback, simulating a client connecting to the server. Panics if OnConnect was not configured.
func (*Harness[S]) Disconnect ¶
func (h *Harness[S]) Disconnect()
Disconnect triggers the OnDisconnect callback, simulating a client disconnecting from the server. Panics if OnDisconnect was not configured.
func (*Harness[S]) Flash ¶
Flash returns the flash messages from the most recent Send call. Returns nil if no flash messages were triggered.
func (*Harness[S]) HTML ¶
HTML returns the rendered HTML from the most recent Send call. Returns an empty string if Render was not configured.
func (*Harness[S]) HasAnnounce ¶
HasAnnounce reports whether the most recent Send call triggered an accessibility announcement matching the given text.
func (*Harness[S]) HasFlash ¶
HasFlash reports whether the most recent Send call triggered a flash message matching the given selector and text.
func (*Harness[S]) HasSignal ¶
HasSignal reports whether the most recent Send call pushed a signal matching the given key and value. Values are compared with == so the expected type must match exactly (e.g. int, not float64).
func (*Harness[S]) HasToast ¶
HasToast reports whether the most recent Send call triggered a toast matching the given text.
func (*Harness[S]) MorphKeys ¶ added in v0.2.2
MorphKeys returns the Dynamic keys declared via tether.Session.Morph in the most recent Send call. Returns nil if Morph was not called.
func (*Harness[S]) Navigate ¶
Navigate sends a navigate event with the given path. Query parameters in the path are parsed and delivered to OnNavigate.
func (*Harness[S]) Render ¶
Render returns the rendered HTML for the current state. When Layout is configured, the content is wrapped in the layout. Returns an empty string if Render was not configured.
func (*Harness[S]) RenderNode ¶
RenderNode returns the node tree for the current state. Useful for inspecting the tree structure directly. Panics if Render was not configured.
func (*Harness[S]) Replaced ¶
Replaced reports whether the most recent URL change used ReplaceURL rather than Navigate. Returns false if no URL was set.
func (*Harness[S]) Send ¶
Send fires a click event with the given action name. This is the most common case - use Harness.SendEvent for other event types.
func (*Harness[S]) SendEvent ¶
SendEvent fires an arbitrary event and captures the response. After this call, Harness.State, Harness.HTML, Harness.Toast, etc. reflect the result of handling this event.
func (*Harness[S]) SendSubmit ¶
SendSubmit fires a submit event with the given action and form data.
func (*Harness[S]) Signals ¶
Signals returns the signal values from the most recent Send call. Returns nil if no signals were pushed. Values retain their original Go types - no JSON round-tripping.
func (*Harness[S]) State ¶
func (h *Harness[S]) State() S
State returns the current accumulated state. Each Send call applies the Handle function, so State reflects all events sent so far.
func (*Harness[S]) Title ¶
Title returns the title from the most recent Send call. Returns an empty string if no title change was triggered.