Component Lab C · Selector resilience

Dynamic DOM Lab

A clearly marked practice fixture for learning why implementation-coupled selectors fail when IDs, classes, order, visibility, and nodes change.

PRACTICE FIXTURE — intentionally built for automation practice

Render 1 uses seed 4803.

IDs change on every fixture render

The control keeps its role and accessible name while its generated ID changes.

Intentional anti-pattern

Why fragile selectors fail here: a selector copied from the generated ID stops matching after the next render.

Generated class names

The stable card title survives while hashed-looking class tokens rotate.

Intentional anti-pattern

Regression summary

24 stable checks

Why fragile selectors fail here: build-style class tokens are implementation details and change with the render sequence.

Seeded reordering

The same seed produces the same order; a different seed produces another documented order.

Intentional anti-pattern

    Why fragile selectors fail here: nth-child points at a position, not the learner the test intends to select.

    Identical text in different contexts

    Two named regions intentionally expose the same button label.

    Intentional anti-pattern

    Profile workflow

    Security workflow

    Why fragile selectors fail here: global text matches both controls; scope to the intended named region.

    Hidden and visible duplicates

    The DOM includes the same text twice, but only one copy is user-visible.

    Intentional anti-pattern

    Release candidate approved

    Why fragile selectors fail here: raw text engines may count hidden content; use visibility-aware locators and assertions.

    DOM replacement after action

    The original article is removed and a new node takes its place.

    Intentional anti-pattern

    Original card

    Why fragile selectors fail here: an element handle becomes stale; a Playwright locator resolves the replacement on each action.

    Elements rendered after an API call

    The button requests synthetic employees and renders accessible cards only after the response.

    Intentional anti-pattern

    Why fragile selectors fail here: querying synchronously before the response returns captures an empty container.

    Passing examples

    Start with observable behavior

    These snippets match executable tests stored inexamples/playwright-ts/.

    Scope by named region
    const billing = page.getByRole('region', { name: 'Billing workflow' });
    await billing.getByRole('button', { name: 'Approve request' }).click();
    await expect(page.getByTestId('dynamic-log')).toContainText('Billing request approved');
    Assert DOM replacement
    const original = page.getByRole('button', { name: 'Replace card' });
    await original.click();
    await expect(original).not.toBeAttached();
    await expect(page.getByRole('article', { name: 'Replacement card' })).toBeVisible();