Skip to content

Authoring Patterns

When to Use

Use these patterns when writing VR tests that need to hold up over time. The assertion line should be the most visible part of any test.

Decision

Pattern Pros Cons
Real-page fixtures (/node/123) Tests what users see Drags in editorial content, navigation chrome, sidebar blocks; every content change ripples
Component fixtures (a dedicated /vrt-fixtures/<component> route) Isolated, stable, fast to update Requires a custom controller; doesn't catch integration regressions

For shared SDC/atoms: use dedicated fixture routes — the Drupal equivalent of Storybook stories.

Pattern

waitForStableLayout helper:

export async function waitForStableLayout(page: Page) {
  await page.waitForLoadState('domcontentloaded');
  await page.waitForLoadState('networkidle');
  await page.evaluate(() => document.fonts.ready);
  await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
  await page.evaluate(() => window.scrollTo(0, 0));
  await page.waitForTimeout(200); // settle
}

data-vrt-mask in Twig — editorial code marks volatile regions:

<span data-vrt-mask class="timestamp">{{ node.created.value|format_date }}</span>

Tests pick it up automatically:

await expect(page).toHaveScreenshot({
  mask: [page.locator('[data-vrt-mask]')],
});

Navigate + stabilize + capture macro:

export async function vrSnapshot(page: Page, url: string, name: string) {
  await page.goto(url);
  await waitForStableLayout(page);
  await expect(page).toHaveScreenshot(`${name}.png`, {
    fullPage: true,
    mask: [page.locator('[data-vrt-mask]')],
  });
}

Most VR tests become 1–3 lines:

test('homepage desktop', async ({ page }) => {
  await vrSnapshot(page, '/', 'homepage');
});

Common Mistakes

  • Wrong: long copy-pasted setup in every test → Right: extract to a helper; the assertion line should be the most visible part
  • Wrong: tests that depend on editorial content (real nodes with timestamps) → Right: moves the goalposts every content change
  • Wrong: single tests asserting multiple unrelated screenshots → Right: split into separate tests per state

See Also