test(browser): drive an undefined reading through the whole web path

four layers carry it: the page's envelope, the driver's unwrap, the
runner's count check and the verifier's decode. each has a unit test and
only a run proves they compose. goes red both ways, decoding an absent
value as null and dropping the envelope.
This commit is contained in:
pj committed 2026-08-15 12:45:37 +05:30
1 parent 85338aeaf1
commit ce4f660098
3 files changed
+62

No files matched your search

+18
View File
@@ -0,0 +1,18 @@
<!doctype html>
<html>
<body>
<!-- One control and one counter. The spec's other two extractors look for
ids that are not here, which is the shape a route-scoped extractor has
on every step it is off its own screen. -->
<div id="app">
<button id="go" style="width:200px;height:80px">go</button>
<div id="count">0</div>
</div>
<script>
let taps = 0;
document.getElementById("go").addEventListener("click", function () {
document.getElementById("count").textContent = String(++taps);
});
</script>
</body>
</html>
+26
View File
@@ -0,0 +1,26 @@
import { always, extract, taps } from "@sanderling/spec";
// Two extractors that resolve nothing, the shape every route-scoped extractor
// has on the steps it is off its own screen (folio has nine of them). JSON has
// no undefined, so these are what the {value} envelope exists for.
const absent = extract((s) => s.ax.find({ id: "nothing-is-ever-here" })).named("absent");
const absentText = extract((s) => s.ax.find({ id: "missing" })?.text).named("absentText");
const bumps = extract((s) => {
const el = s.ax.find({ id: "count" });
return el ? parseInt(el.text, 10) || 0 : 0;
}).named("bumps");
// A reading the page could not take must arrive as undefined, not as null and
// not as goja's reading of the dump. This property is what a spec sees.
const undefinedStaysUndefined = always(
() => absent.current === undefined && absentText.current === undefined,
);
// The run has to actually be doing something, or the property above holds for
// the wrong reason.
const counterNeverMoves = always(() => bumps.current === 0);
export const properties = { undefinedStaysUndefined, counterNeverMoves };
export const actionsRoot = taps;