test(spec): pin the unknown-key diagnostic to one text

The two runtimes each claimed to raise the other's message and nothing
checked it. Both now render the committed text for the committed key.
This commit is contained in:
pj committed 2026-08-13 00:39:29 +05:30
1 parent 269574706d
commit b8bb44cf61
4 files changed
+58 -17

No files matched your search

+36 -12
View File
@@ -8,26 +8,33 @@ import (
"testing" "testing"
) )
// selectorKeysGolden is the cross-runtime contract for object selectors: the
// keys both runtimes accept, and the diagnostic both raise for a key neither
// can match.
type selectorKeysGolden struct {
Keys []string `json:"keys"`
UnknownKeyExample []string `json:"unknownKeyExample"`
UnknownKeyMessage string `json:"unknownKeyMessage"`
}
// Both runtimes reject an object-selector key they do not know, so the two key // Both runtimes reject an object-selector key they do not know, so the two key
// lists have to be one list. Were they to drift, a spec would be accepted by // lists have to be one list. Were they to drift, a spec would be accepted by
// the runtime that lists the key and fail the run on the one that does not, and // the runtime that lists the key and fail the run on the one that does not, and
// the difference would only show on the platform nobody ran first. // the difference would only show on the platform nobody ran first.
// pkg/spec/test/selector-keys.test.ts asserts the SAME file from the web side. // pkg/spec/test/selector-keys.test.ts asserts the SAME file from the web side.
func TestSelectorKeysMatchTheCrossRuntimeList(t *testing.T) { func TestSelectorKeysMatchTheCrossRuntimeList(t *testing.T) {
path, err := filepath.Abs("../../pkg/spec/test/fixtures/selector-keys.json") golden := loadSelectorKeysGolden(t)
if err != nil { if got := SelectorKeys(); !slices.Equal(got, golden.Keys) {
t.Fatal(err) t.Errorf("native key list\n got=%v\nwant=%v", got, golden.Keys)
} }
body, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read selector keys: %v", err)
} }
var want []string
if err := json.Unmarshal(body, &want); err != nil { // An author who hits this on Android and again on web must read one sentence,
t.Fatalf("decode selector keys: %v", err) // not two dialects of it.
} func TestUnknownSelectorKeyMessageMatchesTheCrossRuntimeText(t *testing.T) {
if got := SelectorKeys(); !slices.Equal(got, want) { golden := loadSelectorKeysGolden(t)
t.Errorf("native key list\n got=%v\nwant=%v", got, want) if got := UnknownSelectorKeyMessage(golden.UnknownKeyExample); got != golden.UnknownKeyMessage {
t.Errorf("native message\n got=%q\nwant=%q", got, golden.UnknownKeyMessage)
} }
} }
@@ -37,3 +44,20 @@ func TestSelectorKeysAreSorted(t *testing.T) {
t.Errorf("keys must stay sorted so the two lists compare readably: %v", keys) t.Errorf("keys must stay sorted so the two lists compare readably: %v", keys)
} }
} }
func loadSelectorKeysGolden(t *testing.T) selectorKeysGolden {
t.Helper()
path, err := filepath.Abs("../../pkg/spec/test/fixtures/selector-keys.json")
if err != nil {
t.Fatal(err)
}
body, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read selector keys: %v", err)
}
var golden selectorKeysGolden
if err := json.Unmarshal(body, &golden); err != nil {
t.Fatalf("decode selector keys: %v", err)
}
return golden
}
+1
View File
@@ -694,6 +694,7 @@ export const __testing__ = {
selectorFromString, selectorFromString,
selectorFromObject, selectorFromObject,
SELECTOR_KEYS, SELECTOR_KEYS,
unknownSelectorKeyMessage,
xpathStringLiteral, xpathStringLiteral,
}; };
+8 -2
View File
@@ -1,4 +1,5 @@
[ {
"keys": [
"accessibilityIdentifier", "accessibilityIdentifier",
"accessibilityLabel", "accessibilityLabel",
"accessibilityText", "accessibilityText",
@@ -35,4 +36,9 @@
"text", "text",
"title", "title",
"value" "value"
] ],
"unknownKeyExample": [
"descripton"
],
"unknownKeyMessage": "selector key \"descripton\" cannot match: no element carries that attribute, and it is not one of the accepted keys: accessibilityIdentifier, accessibilityLabel, accessibilityText, aria-label, ariaLabel, bounds, checked, class, className, clickable, content-desc, contentDescription, data-testid, desc, descPrefix, editable, elementType, enabled, focused, hintText, id, idPrefix, identifier, label, package, placeholder, placeholderValue, resource-id, scrollable, selected, tag, testID, testTag, text, title, value"
}
+13 -3
View File
@@ -5,10 +5,14 @@ import { test } from "node:test";
import { __testing__ } from "../src/web-runtime.ts"; import { __testing__ } from "../src/web-runtime.ts";
const { SELECTOR_KEYS } = __testing__; const { SELECTOR_KEYS, unknownSelectorKeyMessage } = __testing__;
const goldenPath = fileURLToPath(new URL("./fixtures/selector-keys.json", import.meta.url)); const goldenPath = fileURLToPath(new URL("./fixtures/selector-keys.json", import.meta.url));
const golden: string[] = JSON.parse(readFileSync(goldenPath, "utf8")); const golden: {
keys: string[];
unknownKeyExample: string[];
unknownKeyMessage: string;
} = JSON.parse(readFileSync(goldenPath, "utf8"));
// Both runtimes reject an object-selector key they do not know, so the two key // Both runtimes reject an object-selector key they do not know, so the two key
// lists have to be one list. Were they to drift, a spec would be accepted by // lists have to be one list. Were they to drift, a spec would be accepted by
@@ -17,5 +21,11 @@ const golden: string[] = JSON.parse(readFileSync(goldenPath, "utf8"));
// internal/hierarchy/selector_keys_test.go asserts the SAME file from the // internal/hierarchy/selector_keys_test.go asserts the SAME file from the
// native side. // native side.
test("the web key list is the cross-runtime list", () => { test("the web key list is the cross-runtime list", () => {
assert.deepEqual([...SELECTOR_KEYS], golden); assert.deepEqual([...SELECTOR_KEYS], golden.keys);
});
// An author who hits this on Android and again on web must read one sentence,
// not two dialects of it.
test("the web diagnostic is the cross-runtime text", () => {
assert.equal(unknownSelectorKeyMessage(golden.unknownKeyExample), golden.unknownKeyMessage);
}); });