appium-attribute-waits
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 belonging to the Playwright family, Chrome DevTools
tooling, or next-frontend-quality-loop, with zero hits in either
vercel-react-native-skills or mobile-expo-app. .omc/plans/appium-rn-testing-skills.md
(local research artifact, not tracked in this repo) authored four skills —
appium-testid-attributes, appium-testid-catalog, this one, and appium-page-objects
— to fill that gap, mirroring the playwright-* family's producer/catalog/consumer/
structure split. The plan's own ADR is candid that this skill is "honestly thinner
than its Playwright namesake," since swarmdriver's waits are inherited NativeBase
methods rather than an authored helper module like Playwright's readiness.ts.
The plan's original draft also had a concrete factual error corrected during review:
it described isShown() as a driver.waitUntil() wrapper. Round-1 Architect review
traced the real implementation (base.ts:71-96) and found isShown() is a plain
try/catch around findEleAndSel() + el.isDisplayed() that swallows any thrown
error — a stale element, a dropped driver session, anything — into a bare false,
indistinguishable from a genuine "not shown yet." That correction became invariant 2.
Before asserting either an epoch-fencing port or an intentional non-port, Implementation
Step 2 required investigating .omc/artifacts/appium-rn-attribute-budget.md rather than
assuming Playwright's data-*-based epoch counter pattern simply didn't apply. Both
real candidates it evaluated — accessibilityValue.now and nativeID — were rejected:
the former is screen-reader-announced and semantically tied to a min/max range (fails
the "never widen an a11y prop for test convenience" invariant outright), and the latter
has unverified iOS surfacing and collides with its existing accessibilityLabelledBy
cross-reference role.
The skill's sharpest invariants, though, came from a real-device incident after initial
ship. Commit d640258 records that during a poker-helper next-appium run, the shipped
accessibilityState.busy guess turned out to be wrong: WDA exposes no compound
accessibilityState.* attribute at all, so polling it silently no-ops instead of
erroring. The fix repointed the busy-clearing poll at the native enabled attribute
instead. The same run also reproduced a screen's own root container reporting
isDisplayed()/visible as false while genuinely on-screen — which hangs
waitForIsShown()'s isDisplayed()-based check for the full timeout — and added
waitForScreenShown(), an existence-based check, for root-level readiness specifically.
What It Does
appium-attribute-waits documents how to wait correctly on async native UI state in
swarmdriver-based Appium/WDIO tests for React Native screens: a spinner clearing, a
busy accessibilityState settling, a screen transition finishing, a list finishing a
refresh. It covers NativeBase.waitForIsShown()/waitForIsNotShown() semantics, the
isShown() trap underneath them, and composing driver.waitUntil()/
nativeUtils.waitForCondition() for anything those inherited methods don't cover.
It does not define how components emit the accessibilityState/accessibilityValue
props being waited on — that's the sibling skill appium-testid-attributes — and it
does not define page-object class structure — that's the sibling skill
appium-page-objects. This skill only covers waiting on native UI state correctly
once a page object and its props already exist.
How To Use It
Triggers on: "wait for element visible appium", "flaky Appium test", "waitForIsShown timeout", "isShown() swallowing errors", "driver.waitUntil composition", "wait for accessibilityState.busy to clear", or writing/reviewing an Appium wait against instrumented native UI state.
skills add git@github.com:catesandrew/next-starters.git --skill skills/appium-attribute-waits -g
npm install @next-starters/skill-appium-attribute-waits
/plugin marketplace add catesandrew/next-starters
/plugin install appium-attribute-waits@next-starters
Gotchas & Invariants
waitForIsShown()/waitForIsNotShown()(base.ts:39-66) already wrapdriver.waitUntil()— never wrap a seconddriver.waitUntil()around an already-polling inherited method.isShown()(base.ts:71-96) is not awaitUntilwrapper — it's a try/catch that swallows every thrown error into a plainfalse. Always pair a wait built on it with a specific, selector-includingtimeoutMsg, never a generic one, or a timeout is indistinguishable from a genuine driver error.- Compose
driver.waitUntil()/nativeUtils.waitForCondition()directly for anything the two inherited methods don't cover — never a hand-rolledsetTimeout/sleep polling loop. - Epoch-fencing is intentionally not ported from the Playwright family.
accessibilityValue.nowandnativeIDwere both investigated and rejected as counter substitutes (perappium-rn-attribute-budget.md); do not invent a third candidate or repurpose either prop as a test-only counter. - The narrowly-scoped fallback for "did an async action provably progress": poll an
existing, already-real in-budget state transition — e.g. a control's busy state
clearing — via
driver.waitUntil()/nativeUtils.waitForCondition(), instead of a dedicated counter prop. - v1.1.0, verified against a real device: WDA exposes no compound
accessibilityState.*attribute at all, so the busy-clearing poll reads the nativeenabledattribute instead — safe only whendisabledderives from the same in-flight variable asbusy. - v1.1.0, also verified against a real device: a screen's own root container can
report
isDisplayed()/visibleasfalsewhile genuinely on-screen, so root-level readiness checks use the existence-basedwaitForScreenShown()instead of the inherited,isDisplayed()-basedwaitForIsShown(). Leaf elements are unaffected and keep usingwaitForIsShown(). - Treating an UNVERIFIED page-source attribute mapping from
appium-rn-attribute-budget.mdas confirmed fact in a real spec, without a first-run validation step, is a red flag — mark it unverified until checked against a real session.
Related Skills
- appium-page-objects — its action methods call this skill's inherited wait helpers around interactions but never redefine them.
- appium-testid-attributes — the producer
side; emits the
accessibilityState/accessibilityValueprops this skill's waits poll. - appium-testid-catalog — owns locator translation and platform-divergence knowledge that this skill assumes but doesn't itself document.
Sourced from: skills/appium-attribute-waits/metadata.json, skills/appium-attribute-waits/SKILL.md, .omc/plans/appium-rn-testing-skills.md (local research artifact, not tracked in this repo), git log --follow -- skills/appium-attribute-waits (commits 5a4e3d3, d640258)