appium-testid-attributes
Why It Exists
Before this skill family, no RN/Appium testing skill existed at all: a grep across
~/.claude/skills and ~/.agents/skills for testid|appium|webdriver|wdio turned
up 17 matched files, every one of them belonging to the Playwright family, Chrome
DevTools tooling, or next-frontend-quality-loop — zero hits in either
vercel-react-native-skills or mobile-expo-app, the two existing RN-adjacent
skills. .omc/plans/appium-rn-testing-skills.md (local research artifact, not
tracked in this repo) authored four skills — this one, appium-testid-catalog,
appium-attribute-waits, and appium-page-objects — to fill that gap, mirroring
the existing playwright-* family's producer/catalog/consumer/structure split.
The differentiated content in this specific skill traces back to a corrected
mistake, not an assumption ported wholesale from Playwright. The plan's original
draft proposed an arbitrary data-*-style vocabulary for surfacing test state on
RN components — but React Native's native views expose no such channel to the
accessibility tree. Round-1 Architect review rejected that invention and replaced
it with RN's actual finite, typed accessibility-prop surface (accessibilityState,
accessibilityLabel, accessibilityHint, accessibilityValue, and similar),
paired with a hard invariant: never widen one of those props' payload to carry
test-only state, because a11y props are spoken aloud by VoiceOver/TalkBack to real
users, not an inert instrumentation side channel.
That invariant was then stress-tested for real. Implementation Step 2 of the plan
(appium-rn-attribute-budget-investigation) evaluated the Playwright family's
data-<action>-epoch pattern for a direct RN port, and considered the two real
in-budget candidates for carrying an epoch counter: accessibilityValue.now and
nativeID. Both failed. accessibilityValue.now is disqualified outright — it's
screen-reader-announced and carries a declared "current value of a range/progress
indicator" contract, so repurposing it as an opaque test counter on a non-range
component manufactures a fake announcement for real users. nativeID clears that
letter (it isn't announced) but has unverified — and plausibly absent — iOS
surfacing, plus a real collision with its existing role as the target of Android's
accessibilityLabelledBy. The grounding investigation at
.omc/artifacts/appium-rn-attribute-budget.md (referenced directly from both
metadata.json and SKILL.md, and explicitly labeled UNVERIFIED pending a live
Appium session) concluded from this that epoch-fencing is an intentional non-port
for this skill family, not an oversight — a poll on an already-in-budget state
transition (accessibilityState.busy flipping true → false) stands in for it
instead. The introducing commit (5a4e3d3) followed a RALPLAN-DR consensus process
(Architect x2, Critic/codex x5) before merge.
What It Does
appium-testid-attributes is the producer half of the three-skill family: it
teaches how to instrument a React Native component's accessibility props —
accessibilityState.busy/.disabled, accessibilityLabel, accessibilityHint,
and similar — so Appium can assert on real state transitions instead of racing
timeouts or scraping visible text. The core discipline is deriving every one of
those props from the exact same variable that already drives the component's
visual render, never a parallel computation that can silently drift from what's
actually on screen.
It does not define the typed testID catalog mechanics (that's
appium-testid-catalog) or how test code reads and waits on these props (that's
appium-attribute-waits) — this skill only covers the producer side, instrumenting
the component itself.
How To Use It
Triggers on: "add accessibilityState to this RN component", "make this screen Appium-testable", "accessibilityState.busy for loading", "accessibilityState.disabled from the same variable", "RN component not exposing state to Appium", or reviewing whether a component's accessibility props will race Appium.
skills add git@github.com:catesandrew/next-starters.git --skill skills/appium-testid-attributes -g
npm install @next-starters/skill-appium-testid-attributes
/plugin marketplace add catesandrew/next-starters
/plugin install appium-testid-attributes@next-starters
Gotchas & Invariants
- Derive
accessibilityState.busy/.disabled(and every other in-budget prop) from the exact same variable driving the component's visual render — never a second, parallel computation that can disagree after a fast update or a refactor that only touches one of them. - Never widen an accessibility prop's payload to carry test-only state. A
counter, sentinel, or opaque ID stuffed into
accessibilityLabeloraccessibilityValueis a real accessibility regression, spoken aloud by VoiceOver/TalkBack — not a neutral engineering tradeoff. - Stay within RN's finite, typed accessibility-prop surface —
accessibilityState(busy,disabled,selected,checked,expanded),accessibilityLabel,accessibilityHint,accessibilityValue(scoped below),accessibilityRole,accessible. There is nodata-*-style arbitrary-attribute escape hatch; every prop reached for must already exist for a declared purpose. accessibilityValue.min/.max/.nowis reserved for genuine range/progress widgets — never repurposed as a general-purpose numeric slot on a button, list, or screen root.- Treat every claim about which concrete Appium/page-source attribute a prop
surfaces as (
content-desc,resource-id,name,label) as UNVERIFIED until validated against a real device/session — cite the budget doc's table, don't assert it as settled fact. - Never hand-write a
testIDstring literal on a component — pull every value from the typed catalog (appium-testid-catalogowns that mechanism). - A fixed, author-time
accessibilityLabel(e.g."Settings"on a static icon button) is a separate concern from the reactive state props above — don't apply render-derivation reasoning to a label that never recomputes. - Epoch-fencing (the Playwright family's
data-<action>-epochpolling pattern) is intentionally not ported here — RN has no unannounced, semantically-free, cross-platform-confirmed prop to carry it. Poll an already-in-budget state transition instead.
Related Skills
- appium-testid-catalog — owns the typed,
bare-testID registry every
testIDthis skill's components bind comes from. - appium-attribute-waits — the consumer side, reading and waiting on the exact accessibility props this skill produces.
- appium-page-objects — page objects assert
on these instrumented props via inherited
NativeBasewaits, one layer up.
Sourced from: skills/appium-testid-attributes/metadata.json, skills/appium-testid-attributes/SKILL.md, .omc/plans/appium-rn-testing-skills.md (local research artifact, not tracked in this repo), git log --follow -- skills/appium-testid-attributes (commit 5a4e3d3)