feat(spec): add keyedBy(element, tags) identity helper

Joins element.find({testTag: tag})?.text per tag with U+001F as the
delimiter so user-visible text can never collide with the separator.
Returns empty string for an undefined element.
This commit is contained in:
pj committed 2026-04-27 00:03:45 +07:00
1 parent 4f8cb43bff
commit 6b50f2dbeb
3 files changed
+56 -2

No files matched your search

+13 -1
View File
@@ -1,5 +1,17 @@
import type { Extracted, State } from "./types.ts";
import type { AccessibilityElement, Extracted, State } from "./types.ts";
export function extract<T>(getter: (state: State) => T): Extracted<T> {
return globalThis.__sanderling__.extract(getter);
}
const KEY_DELIMITER = "\x1f";
export function keyedBy(
element: AccessibilityElement | undefined,
tags: readonly string[],
): string {
if (!element) return "";
return tags
.map(tag => element.find({ testTag: tag })?.text ?? "")
.join(KEY_DELIMITER);
}
+1 -1
View File
@@ -23,7 +23,7 @@ export type {
WeightedEntry,
} from "./types.ts";
export { extract } from "./extract.ts";
export { extract, keyedBy } from "./extract.ts";
export { always, eventually, next, now } from "./ltl.ts";
export {
InputText,
+42
View File
@@ -12,6 +12,7 @@ import {
eventually,
extract,
from,
keyedBy,
next,
now,
pressKey,
@@ -281,6 +282,47 @@ test("from forwards items to the runtime", () => {
assert.equal(sampler.generate(), "a");
});
function elementWithChildren(
cells: Record<string, string>,
): AccessibilityElement {
return {
find: selector => {
if (typeof selector === "string" || Array.isArray(selector)) return undefined;
const tag = (selector as Record<string, string>).testTag;
if (!tag) return undefined;
const text = cells[tag];
if (text === undefined) return undefined;
return { text, find: () => undefined, findAll: () => [] };
},
findAll: () => [],
};
}
test("keyedBy joins testTag-resolved texts with a stable delimiter", () => {
installFakeRuntime();
const row = elementWithChildren({
TxnDate: "2026-04-26",
TxnNote: "Coffee",
TxnAmount: "$5.00",
});
const key = keyedBy(row, ["TxnDate", "TxnNote", "TxnAmount"]);
assert.equal(key, "2026-04-26\x1fCoffee\x1f$5.00");
});
test("keyedBy returns empty string for an undefined element", () => {
installFakeRuntime();
assert.equal(keyedBy(undefined, ["TxnDate"]), "");
});
test("keyedBy substitutes empty strings for missing children", () => {
installFakeRuntime();
const row = elementWithChildren({ TxnDate: "2026-04-26" });
assert.equal(
keyedBy(row, ["TxnDate", "TxnNote", "TxnAmount"]),
"2026-04-26\x1f\x1f",
);
});
test("default generators proxy through to the runtime", () => {
installFakeRuntime();
assert.equal(taps.__sanderlingActionGenerator, true);