Skip to main content

playwright-testid-attributes

Why It Exists

This skill was one of four introduced together in commit 306857c ("skills: add playwright testid-catalog/attributes/waits/page-objects family"), whose message says the split "generalize[s] a Playwright instrumentation convention found undocumented-but-pervasive in a client engagement's codebase." Rather than write one large skill, the pattern was divided along a producer/catalog/consumer/structure axis, with docs/PLAYWRIGHT-TESTING-RUNBOOK.md added as a thin index tying the four together — that runbook spells out the real dependency order: the typed catalog (playwright-testid-catalog) comes first, this skill's reactive state-marker attributes come second (pulling data-testid from the catalog and adding data-state/data-load-state/aria-busy/epoch attributes on top), the wait helpers (playwright-attribute-waits) come third, and Page Objects (playwright-page-objects) come last.

The skill wasn't static after that initial commit. A follow-up fix, 94613cb ("fix(skills): quote-safe colon in playwright-testid-attributes description"), corrected a mid-line colon-space in this exact skill's YAML frontmatter description that broke frontmatter parsing — skill loaders were silently dropping this skill from discovery entirely until that fix landed. That failure mode is a concrete reminder of why the frontmatter description field carries the trigger phrases verbatim: if it fails to parse, the skill doesn't just render badly, it never activates at all.

This skill's authority extends beyond its own directory. .omc/plans/quality-loop-skills.md (local research artifact, not tracked in this repo) explicitly defers to it rather than re-deriving equivalent guidance: its Lens I item 4, "DOM State Anchor: data-load-state/data-state," is noted as "already this family's own playwright-testid-attributes skill, invariant-for-invariant." The same plan also maps a Playwright-rules lens directly onto this skill (among its three siblings) instead of writing a parallel checklist. A separate plan, .omc/plans/appium-rn-testing-skills.md (also local-only), used this family's producer/catalog/consumer/structure split as the explicit template for a parallel React Native/Appium skill family, naming this skill's sibling-disclaimer style and invariant-verifiability bar as the precedent to match.

What It Does

playwright-testid-attributes is the producer half of a three-skill family: it teaches how to instrument a component's markup with data-*/aria-* attributes that describe visual, loading, and interaction state, derived from the exact same variable that drives the component's render — never a parallel computation that can drift from what's actually on screen. It covers the data-state/data-load-state/aria-busy baseline pair for async-gated containers, data-<action>-epoch counters for repeatable actions like delete/refresh/retry, the four-stage debounced-search attribute pattern, JSON.stringify-ing identity attributes when a test needs to know which items are selected (not just how many), hidden sentinel elements for content that unmounts on close, and preferring standard ARIA attributes over bespoke data-* equivalents when an ARIA one already exists.

It does not define the typed data-testid catalog mechanics — that's the sibling skill playwright-testid-catalog — or how test code reads and waits on these attributes — that's playwright-attribute-waits. This skill only covers the producer side: instrumenting the component itself.

How To Use It

Triggers on: "add data-testid to this component", "make this Sheet Playwright-testable", "async loading state attributes", "epoch counter for retries", "debounced search test attributes", "data-load-state", "aria-busy", "hidden sentinel for closed Dialog", or reviewing whether a component's state attributes will race Playwright.

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

Gotchas & Invariants

  • Derive data-state/data-load-state from the exact same variable that decides what JSX renders — never a second, parallel computation that can disagree after a fast update or a refactor that only touches one of them.
  • Expose data-state ('open' | 'closed') and data-load-state ('loading' | 'ready' | 'error' | 'closed') as a baseline pair on any async-gated container — collapsing them into one attribute forces a test to guess whether "closed" means "not mounted" or "mounted but errored."
  • Set aria-busy from the exact same boolean that flips data-load-state === 'loading', so test code has a generic, enum-agnostic "is this settled yet" gate.
  • Repeatable async actions (delete, refresh, save, retry) need a monotonically-incrementing data-<action>-epoch counter that increments exactly once per completed attempt, success or failure, never while pending — a terminal state string alone can repeat across two separate attempts and hide a stale result.
  • A debounced search input tracks three pipeline stages (raw typed value, debounce-settled value, applied-filter value) as a four-state idle | pending | loading | ready machine, never a single boolean.
  • JSON.stringify the full value (e.g. data-selected-ids) when a test needs to know which items are involved, not just a count.
  • Components that unmount their content on close (Radix/shadcn-style Dialog/Sheet) need a permanently mounted, visually hidden sentinel carrying the same data-state/data-load-state pair, so a test can assert across a close-then-reopen transition without racing the unmount.
  • Never hand-write a data-testid string literal — every value comes from the typed catalog (playwright-testid-catalog owns that mechanism).
  • Prefer the standard ARIA attribute (aria-expanded, aria-pressed, aria-selected) over a bespoke data-* equivalent when one already exists — reinventing it can disagree with the ARIA value it duplicates.
  • Static, non-reactive labels (analytics/RUM action names, onboarding-tour anchors) are a separate concern from the reactive state markers above — don't apply render-derivation reasoning to a fixed string set once at author time.
  • playwright-testid-catalog — owns the typed data-testid registry every id this skill's components bind comes from.
  • playwright-attribute-waits — the consumer-side sibling that reads and waits on the exact attributes this skill produces.
  • playwright-page-objects — Page Object classes wrap components instrumented by this skill, using the catalog and wait helpers one layer up.

Sourced from: skills/playwright-testid-attributes/metadata.json, skills/playwright-testid-attributes/SKILL.md, docs/PLAYWRIGHT-TESTING-RUNBOOK.md, git log --follow -- skills/playwright-testid-attributes (commits 306857c, 94613cb, ddeb5f5), .omc/plans/quality-loop-skills.md (local research artifact, not tracked in this repo), .omc/plans/appium-rn-testing-skills.md (local research artifact, not tracked in this repo)