widgettest

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 3, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package widgettest renders a registered widget's own live region, so a specification can assert on what a viewer receives rather than on how the markup was produced.

Why it renders rather than reads source

Two widgets that draw the same card share no file names, no function names and no formatting when one is generated from a document and the other is written by hand against the SDK. The one thing they can be held to identically is what comes out of Render, so everything here takes a widget and gives back markup: a card assertion written against this package holds of both, which is what makes it fair to compare them.

What it is not

It is not an HTML parser and does not want to become one. Rendered answers the questions a card assertion actually asks — does this string appear, how many times, in what order, on how many elements of one class, and is the landmark named — and every one of them is a substring or a class-token question. A specification that needs a document tree has outgrown this package and should say so rather than grow it a DOM.

The mount is a real one

Mount puts the widget in a registry of its own and takes the fragment the live path patches, rather than calling the widget's Render directly. The difference is not decorative: a region identity that does not match the fragment's, a mount that never returns state, and a dirty declaration that disagrees with the markup are all mistakes a direct call holds constant.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoRegion = errors.New("widgettest: the mounted widget has no fragment for its own region")

ErrNoRegion is a registry that produced no fragment for the widget's own region, which means the registration and the live configuration disagree about what this widget is called on the wire.

Functions

func Deliver

func Deliver(name string, fields map[string]string) live.Event

Deliver is one event as a stream delivers it: a wire name and the wire field names the widget's registration declared.

It is here rather than in each specification because the field map is the contract between the widget and whatever fills its events, and a literal retyped per specification is a contract with one more copy of itself.

Types

type Card

type Card struct {
	// contains filtered or unexported fields
}

Card is one widget mounted alone in a registry of its own: the fragment the live path patches, the reducer that routes to it, and the state a session would hold.

It is not generic, and the erasure is deliberate. Mount takes the type parameter — that is the boundary — and the registry is where a widget's state type is already forgotten exactly once, so a second generic shell here would be a second place the same erasure happens.

func Mount

func Mount[S any](ctx context.Context, instance widget.IWidget[S, live.AnonymousIdentity]) (*Card, error)

Mount registers one widget, mounts a session for it, and returns the card.

The security posture is the anonymous one, because a card that is rendered and never served has no socket to authenticate: nothing here opens a listener, and Mount is the wrong tool for asserting anything about authorization.

That is also why the identity type is fixed rather than a second parameter. A widget is generic in its host's identity type since 2026-09-03, and a card has no host: it instantiates on live.AnonymousIdentity, the concrete type live.Anonymous produces, which is the honest identity for a session that never opened.

func (*Card) Apply

func (card *Card) Apply(events ...live.Event) []live.Effect[live.AnonymousIdentity]

Apply drives the card through events in order, and returns whatever effects the transitions scheduled.

The effects are returned rather than dropped because "this card scheduled none" is a thing worth asserting: a widget that schedules an effect a host cannot execute is a change that never happens.

func (*Card) Region

func (card *Card) Region() string

Region is the live region this card patches, and the identity every event sent to it must name.

func (*Card) Render

func (card *Card) Render(ctx context.Context) (Rendered, error)

Render returns the card's live region as the markup a patch would carry.

type Landmark

type Landmark struct {
	// Element is the root element's tag name.
	Element string

	// LabelledBy is the root's aria-labelledby, or empty when it carries none.
	LabelledBy string

	// Named reports whether some element in the fragment carries the id
	// LabelledBy names. Both halves are required rather than decorative:
	// HTML-AAM maps a nameless <aside> inside sectioning content to `generic`,
	// so a landmark with no accessible name is not a landmark.
	Named bool
}

Landmark is what a screen-reader user finds when they jump to this widget.

type Rendered

type Rendered string

Rendered is one widget's live region as a viewer receives it.

Every method is a question a card assertion actually asks. None of them builds a document tree: see the package comment for why that is a boundary rather than an omission.

func (Rendered) Count

func (rendered Rendered) Count(fragment string) int

Count is how many times a fragment appears.

It is separate from Rendered.Has because "exactly once" is the assertion a region identity needs: a card that named its own region twice would patch correctly and be impossible to address.

func (Rendered) Elements

func (rendered Rendered) Elements(class string) int

Elements is how many elements carry a class, matched as a whole class token rather than as a substring.

The distinction is the whole reason this is not a Count: "widget-pulse" appears inside "widget-pulse-forward", so a substring count of a class name reports one element as two or three.

func (Rendered) Has

func (rendered Rendered) Has(fragment string) bool

Has reports whether the markup contains a fragment.

func (Rendered) InOrder

func (rendered Rendered) InOrder(fragments ...string) bool

InOrder reports whether every fragment appears, each after the one before it.

Declaration order is a promise the widget language makes — stats, legend entries and indicators all render in the order the document declares them — and a set of independent Has assertions cannot see it.

func (Rendered) Landmark

func (rendered Rendered) Landmark() (Landmark, bool)

Landmark reads the fragment's root element and its accessible name.

The second return is false for markup with no element at all, which is what a widget whose render produced nothing looks like — a case worth telling apart from a root that simply has no label.

func (Rendered) MotionOpen

func (rendered Rendered) MotionOpen() bool

MotionOpen reports whether the motion gate is open on this render.

func (Rendered) Pulses

func (rendered Rendered) Pulses() int

Pulses is how many pulse elements the scene carries.

A pulse is emitted whether or not the motion gate is open — the gate is the root's data-motion attribute and the stylesheet reads it — so this counts the scene's declared motion rather than what is currently moving. A card assertion that means "nothing is animating" reads Rendered.MotionOpen.

func (Rendered) String

func (rendered Rendered) String() string

String is the markup itself, for a failure message that has to show it.

Jump to

Keyboard shortcuts

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