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:catesworks/next-starters.git --skill skills/appium-testid-catalog -g
npm install @next-starters/skill-appium-testid-catalog
/plugin marketplace add catesworks/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)