Skip to main content

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 accessibilityLabel or accessibilityValue is 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 no data-*-style arbitrary-attribute escape hatch; every prop reached for must already exist for a declared purpose.
  • accessibilityValue.min/.max/.now is 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 testID string literal on a component — pull every value from the typed catalog (appium-testid-catalog owns 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>-epoch polling 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.
  • appium-testid-catalog — owns the typed, bare-testID registry every testID this 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 NativeBase waits, 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)