Embed

Shadow DOM

Advanced

The button below lives inside an open shadow root. Playwright pierces these by default.

Live widget · interact freely

Manual test goal

Pierce a shadow root, click a button rendered inside, and validate the encapsulated state.

Expected result

Playwright pierces shadow boundaries with default selectors; clicking the inner button increments a count.

Stable selectors

  • Shadow host[data-testid="shadow-host"]
  • Inner button[data-testid="shadow-button"]
  • Inner counter[data-testid="shadow-counter"]

Reference Playwright spec

shadow-dom.spec.ts
ts
1
2
3
4
5
6
7
8
9
10
import { test, expect } from '@playwright/test';

test('click element inside shadow root', async ({ page }) => {
  await page.goto('https://lab.hakdogan.com/practice/shadow-dom');

  // Playwright pierces open shadow roots automatically
  await page.getByTestId('shadow-button').click();
  await page.getByTestId('shadow-button').click();
  await expect(page.getByTestId('shadow-counter')).toHaveText('count: 2');
});

Demo Simulator

Simulated Playwright run · no real browser is launched

Idleshadow-dom.spec.ts
https://lab.hakdogan.com/practice/shadow-dom
# console output will stream here