fix(spec): an editable or scrollable selector names what this host derives

Both facts are derived from the live element rather than written by the
markup, and matching them as attributes built [editable="true"], which
no page carries. Both resolve against the dump on the goja host, so a
spec naming a field or a scroll container that way found it there and no
element at all here, with no unknown-key error to say so.

Each reads the same function the fact is derived with, so a selector
cannot name an element this host calls something else: the handle, the
picker's target list and the editable selector all go through
isEditable, and scrollable reads the overflow test collectTargets reads.

scrollable false names nothing rather than every element that does not
scroll: both producers state the fact only where it holds, the way an
element that is no field at all answers to neither value of secure.
This commit is contained in:
pj committed 2026-08-19 10:12:12 +05:30
1 parent c4fbe79016
commit 43970686d7
2 files changed
+69 -10

No files matched your search

+44
View File
@@ -998,6 +998,50 @@ test("elementType names the elements class names", () => {
});
});
// editable and scrollable are derived from the live element the way the other
// boolean states are, and were reached the same wrong way: as markup
// attributes, which build [editable="true"] and match nothing on any page. Both
// resolve against the dump on the goja host, so a spec naming a field or a
// scroll container that way found it there and no element at all here.
test("editable selects what this host reports editable", () => {
const note = fakeElement({
tag: "input", x: 0, y: 0, width: 120, height: 20, id: "note", editable: true,
});
const remember = fakeElement({
tag: "input", x: 0, y: 20, width: 20, height: 20, id: "remember",
attrs: { type: "checkbox" }, editable: true,
});
const heading = fakeElement({ tag: "h1", x: 0, y: 40, width: 120, height: 20, id: "title" });
withFakeDocument([note, remember, heading], () => {
assert.deepEqual(statesMatched({ editable: true }, "editable"), [["note", true]]);
assert.deepEqual(statesMatched({ editable: false }, "editable"), [
["remember", false],
["title", false],
]);
});
});
// The selector reads the same overflow test the picker's target list is built
// with, so a container this host offers a scroll on is the container a spec can
// name. `false` names nothing: the producers state the fact only where it
// holds, so the elements that do not scroll answer to neither value, the way an
// element that is no field at all answers to neither value of secure.
test("scrollable selects the containers this host offers a scroll on", () => {
const feed = fakeElement({
tag: "div", x: 0, y: 0, width: 120, height: 40, id: "feed", overflows: true,
});
const row = fakeElement({ tag: "div", x: 0, y: 40, width: 120, height: 20, id: "row" });
withFakeDocument([feed, row], () => {
const scrolls = host
.queryTargets()
.filter((target) => target.scrollable)
.map((target) => target.selector);
assert.deepEqual(scrolls, ["id:feed"]);
assert.deepEqual(matchedIDs({ scrollable: true }), ["feed"]);
assert.deepEqual(matchedIDs({ scrollable: false }), []);
});
});
test("attrs carries every other attribute alongside tag and aria-label", () => {
const attrs = attrsOf(
domElement({ tag: "input", attributes: { id: "txn-note", placeholder: "What's this for?" } }),