Mixins / foldkit-mixins
Testing and static output
SlotView.inertBuilder<Message>() is an HtmlBuilder with no runtime behind it. Rendering a
view with it builds the same tagged attributes and elements, but nothing mounts and handlers are
never dispatched. Use it in tests, demos and static output, not as a renderer.
Attributes reads a resolved bundle, such as the result of slots.x.attrs(base), by tag.
Attributes.find(bundle, tag) returns the first match typed as that variant, so value,
message or action need no cast; Attributes.filter returns every match in bundle order;
Attributes.tagOf returns an entry's tag. Opaque ChildAttribute entries have no readable tag
and are skipped.
import { Attributes, type SlotAttributes } from 'foldkit-mixins'
let input: SlotAttributes<Message> = []
const Probe = SlotView.forMessages<Message>()
.define(FieldSlots, (field: FieldInput, slots, h) => {
input = slots.input.attrs([h.Value(field.value)])
return h.input(input)
})
.pipe(Style.attach(FieldStyle), Behavior.attach(Validation))
Probe({ value: 'Ada', invalid: true }, SlotView.inertBuilder<Message>())
Attributes.find(input, 'Class')?.value // 'field-input'
Attributes.find(input, 'AriaInvalid')?.value // trueWhat a whole view draws is read with Inert from foldkit-mixins/testing: Inert.all,
children, byTag, byRole, byLabel (by aria-label, a <label for>, or the innermost
element with that text), text, value (an attribute or a property, whichever the builder
wrote, so title and value need no special case), classes, style and pressed (an
Option, none for a button that is not a toggle).
import { Inert } from 'foldkit-mixins/testing'
const root = Probe({ value: 'Ada', invalid: false }, SlotView.inertBuilder<Message>())
Inert.value(Inert.byTag(root, 'input')[0], 'value') // 'Ada'Inert.draw(view, input) draws a SlotView with every element a Slot draws marked with the
Slot's name, however deeply its views nest; a view drawn for real is never marked. It draws
under a Foldkit Scene's render frame, so a view that embeds a Submodel with h.submodel
(every @foldkit/ui component page) draws whole, childAttributes included; nothing is
dispatched, and no Command or Mount it declares runs or is checked. On that tree,
On the real runtime, Frames.track() from foldkit-mixins/testing tracks the
page's animation frames (create it after any stub of requestAnimationFrame,
and dispose() it after). await frames.settle() resolves once every frame
asked for has run and none followed, so a test reads the page drawn instead of
sleeping and hoping; pass Sync.mount's handle (frames.settle(mounted)) to
wait for its replica first. settle does not wait for I/O a Command started:
wait for that result (vi.waitFor), then settle. hold() keeps every frame
back until release(), so events meet the view as last drawn, as input
faster than a frame does; see
Messages carry intent.
Inert.bySlot(root, 'input') finds a Slot's elements, Inert.unslotted(root, { inside }) lists
every element no Slot drew (skipping what a Slot named in inside holds, such as a canvas drawing
the application's page), and Inert.fixedInline(root, { inside }) every inline declaration that
is not a custom property. A package view's test expects both lists to be empty.
Inert.css(nodes) is the compiled CSS behind the nodes' classes, and
Inert.missingTokens(root, stylesheet) lists the --fk-* tokens the drawn Styles read with no
fallback that nothing defines (not the sheet, not a drawn rule, not an inline style), each of
which makes its declaration invalid in a browser. The two checks every application view test
repeats are then:
const tree = Inert.draw(Counter, 0)
expect(Inert.unslotted(tree)).toEqual([])
expect(Inert.missingTokens(tree, stylesheet)).toEqual([])A tree whose Styles read no token passes the second vacuously; assert that
Inert.css(Inert.all(tree)) contains var(--fk- where that would be a mistake.