Triggers

fs-trigger says when an assertion starts watching for its expected outcome. Every instrumented element needs exactly one trigger (unless it's an OOB assertion — see oob).

<button fs-assert="..." fs-trigger="click" fs-assert-...>…</button>

Choosing the right trigger

Three questions get you to the right trigger fast:

  1. Does the user directly do something? Use an interaction trigger: click, submit, change, input, blur, focus, hover, keydown, dblclick.
  2. Does the element appear, disappear, or load on its own? Use a lifecycle trigger: mount, unmount, load, error.
  3. Should this fire continuously without any user action? Use invariant for perpetual monitoring, or event:<name> to hook into a custom event your app dispatches.

Every trigger

User interaction

Trigger Fires when Typical element
click Element is clicked button, a, div, any clickable
dblclick Element is double-clicked any
change Input value changes and commits input, select, textarea, checkbox
input Input value changes while typing input, textarea
blur Element loses focus input, textarea
focus Element receives focus input, button, a
hover Mouse enters the element (alias for mouseenter) any
keydown Any key is pressed; keydown:<key> for a specific one any focusable
submit Form is submitted form

Lifecycle

Trigger Fires when Typical element
mount Element is added to the DOM any (useful for page-load checks)
unmount Element is removed from the DOM any
load Resource finishes loading img, video, iframe
error Resource fails to load img, video, iframe

Environment

Trigger Fires when
online Browser connectivity restored
offline Browser connectivity lost

Passive monitoring

Trigger Fires when
invariant Continuously — only reports violations and recoveries

Custom events

Trigger Fires when
event:<name> A named CustomEvent fires on document
event:<name>[detail-matches=key:value] Custom event fires and event.detail.<key> matches (shallow string equality)

Placement rules

  1. Attributes go on the trigger element — the element the user directly interacts with. Clicks on descendants (icon spans inside a button, text inside a label) resolve up to the nearest fs-trigger ancestor via closest(), so nested content works naturally.
  2. For forms: use fs-trigger="submit" on the <form> or fs-trigger="click" on the submit button — either works.
  3. For mount/unmount: place on the element being observed.
  4. For load/error: place on the media element itself.
  5. One trigger per element. fs-trigger accepts exactly one value. Need multiple? Split into multiple elements or use OOB assertions.
  6. Multiple assertion types on one element are fine. Each creates a separate assertion with the same trigger and key.

Common mistakes

  • Placing fs-trigger="click" on a container <div> that wraps multiple unrelated children. It will fire for ANY click inside it, producing noisy assertions. Put the trigger on the specific element the user is meant to interact with.
  • Using mount where invariant is correct. mount fires once when the element enters the DOM. invariant continuously monitors while the element exists. For "this should always be visible" use invariant.
  • Expecting keydown to fire on a container. Keyboard events fire on the focused element. Put fs-trigger="keydown:Escape" on the focusable element (input, button, or an element with tabindex).