tethertest

package
v0.3.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2026 License: MIT Imports: 5 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ComponentHarness

type ComponentHarness[C tether.Component] struct {
	// contains filtered or unexported fields
}

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 processes URL parameters. Optional.
	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

func New[S any](cfg Config[S]) *Harness[S]

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

func (h *Harness[S]) Announce() string

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

func (h *Harness[S]) Flash() map[string]string

Flash returns the flash messages from the most recent Send call. Returns nil if no flash messages were triggered.

func (*Harness[S]) HTML

func (h *Harness[S]) HTML() string

HTML returns the rendered HTML from the most recent Send call. Returns an empty string if Render was not configured.

func (*Harness[S]) HasAnnounce

func (h *Harness[S]) HasAnnounce(text string) bool

HasAnnounce reports whether the most recent Send call triggered an accessibility announcement matching the given text.

func (*Harness[S]) HasFlash

func (h *Harness[S]) HasFlash(selector, text string) bool

HasFlash reports whether the most recent Send call triggered a flash message matching the given selector and text.

func (*Harness[S]) HasSignal

func (h *Harness[S]) HasSignal(key string, value any) bool

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

func (h *Harness[S]) HasToast(text string) bool

HasToast reports whether the most recent Send call triggered a toast matching the given text.

func (*Harness[S]) MorphKeys added in v0.2.2

func (h *Harness[S]) MorphKeys() []string

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

func (h *Harness[S]) Navigate(path string)

Navigate sends a navigate event with the given path. Query parameters in the path are parsed and delivered to OnNavigate.

func (*Harness[S]) Render

func (h *Harness[S]) Render() string

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

func (h *Harness[S]) RenderNode() node.Node

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

func (h *Harness[S]) Replaced() bool

Replaced reports whether the most recent URL change used ReplaceURL rather than Navigate. Returns false if no URL was set.

func (*Harness[S]) Send

func (h *Harness[S]) Send(action string)

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

func (h *Harness[S]) SendEvent(ev tether.Event)

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]) SendInput

func (h *Harness[S]) SendInput(action, value string)

SendInput fires an input event with the given action and value.

func (*Harness[S]) SendSubmit

func (h *Harness[S]) SendSubmit(action string, data map[string]string)

SendSubmit fires a submit event with the given action and form data.

func (*Harness[S]) Signals

func (h *Harness[S]) Signals() map[string]any

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

func (h *Harness[S]) Title() string

Title returns the title from the most recent Send call. Returns an empty string if no title change was triggered.

func (*Harness[S]) Toast

func (h *Harness[S]) Toast() string

Toast returns the toast message from the most recent Send call. Returns an empty string if no toast was triggered.

func (*Harness[S]) URL

func (h *Harness[S]) URL() string

URL returns the navigation URL from the most recent Send call. Returns an empty string if no navigation was triggered.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL