Skip to main content

appium-testid-catalog

Why It Exists

This skill exists to fix a mistake the plan itself made. .omc/plans/appium-rn-testing-skills.md (local research artifact, not tracked in this repo) records that the original draft of this whole appium-* family had the testID convention backwards, proposing ~-prefixed selectors for the catalog. Round-1 Architect review traced that against swarmdriver's real buildSelector() (utils.ts:124-139) and found a ~ prefix resolves to WDIO's accessibility id strategy, which buildSelector returns untranslated — silently dropping the Android <appPackage>:id/ resource-id prefix the bare-id path would otherwise add. That defect is what the plan calls "a live instance" of a deeper structural problem, not a one-off typo: RN/Appium's dominant organizing axis is platform (Android vs iOS), not the producer/consumer axis the sibling playwright-* family mirrors, and a straight 1:1 content mirror across four skills would have sharded that platform knowledge into duplication or platform-blind half-truths.

The plan's chosen fix, "Option A′," is why this specific skill carries more than a typed-object pattern: it promotes appium-testid-catalog to the single authoritative home for locator translation, owning the full nativeUtils.buildSelector() contract and the Android/iOS divergence table, rather than letting appium-testid-attributes or appium-page-objects re-derive platform-specific behavior inline. The plan names this risk explicitly and assigns a dedicated cross-reference verification pass (its Implementation Step 7) to catch platform knowledge leaking into the wrong skill — because nothing mechanically stops an author from re-deriving it inline instead of citing back here.

Two further hard-won facts came out of the same review cycle and now live in this skill's invariant 9: nativeUtils.findEles() computes a translated selector and then queries with the raw, untranslated one (utils.ts:293 vs :299), discarding its own translation; and buildSelector reads the bare appPackage capability key on Android while a nearby reference in the same module uses the W3C-namespaced appium:appPackage, so a strict W3C session can silently produce id=undefined:id/<testID> locators. Both are documented here as workarounds for real dependency defects, not conventions to canonize.

What It Does

appium-testid-catalog defines a shared registry helper that builds a nested, section-keyed catalog object of bare testID strings for a feature area, validates uniqueness at dev-time (throwing on a duplicate leaf and naming every conflicting path), and a WDIO-side mirror module that re-exports the identical object so RN components and WDIO specs read one in-memory object and can never drift on an id string.

It is also the authoritative home for the nativeUtils.buildSelector() locator-translation contract and the full Android/iOS platform-divergence table — knowledge no sibling skill duplicates. appium-testid-attributes consumes catalog ids in components but does not define the registry, mirror, or translation mechanics; appium-attribute-waits and appium-page-objects pass catalog ids through buildSelector at lookup time but likewise treat this skill as the source of truth for what that translation actually does.

How To Use It

Triggers on: "testID catalog", "typed test ids for RN", "testID registry", "duplicate testID", "migrate hardcoded testID strings", "WDIO mirror module", "buildSelector for Android vs iOS", "platform-divergent locator", or standing up the catalog layer for a new feature area.

skills add git@github.com:catesandrew/next-starters.git --skill skills/appium-testid-catalog -g
npm install @next-starters/skill-appium-testid-catalog
/plugin marketplace add catesandrew/next-starters
/plugin install appium-testid-catalog@next-starters

Gotchas & Invariants

  • Catalogs store bare testIDs only — never ~-prefixed, never id=-prefixed, never platform-qualified. A ~-prefixed value hits WDIO's accessibility id strategy and returns untranslated from buildSelector, losing the Android <appPackage>:id/ prefix; the reference registry helper rejects any leaf beginning with ~ or containing a strategy= prefix.
  • One typed catalog module per feature area, nested by view/section (list, detail, create, edit, dialogs), built via the shared createTestIdRegistry helper — never a hand-rolled plain object literal, which validates nothing.
  • nativeUtils.buildSelector() is the only locator-translation authority. Never hand-build `id=${pkg}:id/${id}` at a call site and never branch on driver.isAndroid in a spec — that duplicates the prefixing rule this skill already owns.
  • Never import swarmdriver's find-strategy internals. defineStrategy and findStrategy are real exports but are not part of @caps/core's public surface (absent from the native barrel and the package's exports map); import only nativeUtils.buildSelector.
  • Dynamic per-row ids use a `prefix-${id}` convention and must be looked up with findEle once per individually-built id — never findEles against a shared pattern. findEles computes the translated selector and then queries with the raw, untranslated one (utils.ts:293 vs :299), silently discarding the translation.
  • Dev-time uniqueness validation must throw and name both conflicting catalog paths (e.g. list.page and detail.page), not just the duplicated value — a message naming only the value forces a grep; naming both paths is the fix instruction.
  • The WDIO-side mirror module re-exports the exact same catalog object — zero string duplication. A renamed or removed id breaks the mirror's import at compile time instead of leaving a stale string alive in test code.
  • buildSelector reads the bare appPackage capability key on Android (utils.ts:132) while a nearby reference in the same module uses the W3C-namespaced appium:appPackage (utils.ts:168). Under a strict W3C session carrying only the namespaced key, every Android locator silently becomes id=undefined:id/<testID>. Assert driver.capabilities.appPackage is a non-empty string at session start and fail loudly rather than treating undefined:id/ as expected.
  • appium-testid-attributes — the producer side; binds this skill's catalog ids onto actual RN components via testID and typed accessibility props.
  • appium-attribute-waits — consumes bare catalog ids through buildSelector() at lookup time but does not redefine the translation contract this skill owns.
  • appium-page-objects — locator getters pull bare testIDs from this skill's typed registry rather than hand-writing or hard-coding id strings.

Sourced from: skills/appium-testid-catalog/metadata.json, skills/appium-testid-catalog/SKILL.md, .omc/plans/appium-rn-testing-skills.md (local research artifact, not tracked in this repo), git log --follow -- skills/appium-testid-catalog (commit 5a4e3d3)