Files
sanderling/pkg/spec/test/verbs.test.ts
T
pj b5f64bf665 refactor(spec): one candidate producer over one target-eligibility rule
Both hosts routed verbs themselves and both policies enumerated their own
actions, and all four drifted. Web sent `swipes` to scrollable containers only,
so swipe-to-dismiss on a list row was reachable on native and unreachable on
web; the model policy folded gestures its own way and could not reach what the
seeded picker drew.

A host now reports facts about every element and never decides which verb may
act on it: targets.ts acceptsTarget owns that for both. pick.ts builtinCandidates
is the single enumeration, and the model policy reads it through
__sanderlingEnumerateBuiltin__ instead of reimplementing it in Go.

Gesture verbs change with it: scrolls stay vertical over scrollable containers,
swipes go free-form in all four directions from any element with real bounds.

Claude-Session: https://claude.ai/code/session_01Fj4wJUikdABuMQEETwW55J
2026-08-12 16:48:27 +05:30

73 lines
2.1 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { supports, warnUnsupportedOnce, resetWarnings } from "../src/verbs.ts";
import type { BuiltinVerb, Host } from "../src/action-tree.ts";
const ALL_VERBS: BuiltinVerb[] = [
"taps",
"doubleTaps",
"longPresses",
"scrolls",
"typing",
"swipes",
"waitOnce",
"pressKeys",
];
function countingHost(platform: "android" | "ios" | "web"): Host & { calls: BuiltinVerb[] } {
const calls: BuiltinVerb[] = [];
return {
calls,
platform: () => platform,
queryTargets: () => [],
reportUnsupported: (verb) => {
calls.push(verb);
},
seedHi: () => 0n,
seedLo: () => 0n,
};
}
test("every verb is supported on native platforms", () => {
for (const verb of ALL_VERBS) {
assert.equal(supports(verb, "android"), true, `android ${verb}`);
assert.equal(supports(verb, "ios"), true, `ios ${verb}`);
}
});
test("web supports the full builtin verb set (declared, not silently no-op)", () => {
for (const verb of ALL_VERBS) {
assert.equal(supports(verb, "web"), true, `web ${verb}`);
}
});
test("warn-once reports a verb exactly once per platform", () => {
resetWarnings();
const host = countingHost("web");
assert.equal(warnUnsupportedOnce(host, "swipes"), true);
assert.equal(warnUnsupportedOnce(host, "swipes"), false);
assert.equal(warnUnsupportedOnce(host, "swipes"), false);
assert.deepEqual(host.calls, ["swipes"]);
});
test("warn-once keys by verb@platform independently", () => {
resetWarnings();
const web = countingHost("web");
const android = countingHost("android");
warnUnsupportedOnce(web, "scrolls");
warnUnsupportedOnce(android, "scrolls");
warnUnsupportedOnce(web, "scrolls");
// Same verb on two platforms: one report each, suppressed on repeat.
assert.deepEqual(web.calls, ["scrolls"]);
assert.deepEqual(android.calls, ["scrolls"]);
});
test("resetWarnings re-arms reporting", () => {
resetWarnings();
const host = countingHost("web");
warnUnsupportedOnce(host, "longPresses");
resetWarnings();
warnUnsupportedOnce(host, "longPresses");
assert.deepEqual(host.calls, ["longPresses", "longPresses"]);
});