Advanced-DOMadvanced 16 min
Shadow DOM Elements — Practice in Playwright, Selenium & Cypress
Shadow DOM automation practice for Playwright and Selenium — pierce open shadow roots, locate web components, and assert against encapsulated UI.
Live element
Locators cheat-sheet
| data-testid | Role | Accessible label | What it is |
|---|---|---|---|
| shadow-host | generic | Shadow host element | Custom element <pilot-toggle> with an OPEN shadow root. |
| shadow-btn | button | Toggle shadow state | Lives inside the open shadow root. Use shadow-piercing selectors. |
| shadow-state | status | Shadow toggle state | Text node inside the shadow root that flips between ON / OFF. |
Reference solutions
import { test, expect } from '@playwright/test';
test('toggles state inside the open shadow root', async ({ page }) => {
await page.goto('/practice/shadow-dom');
// Playwright auto-pierces OPEN shadow roots — locators "just work".
const widget = page.frameLocator('iframe[title="Shadow DOM live widget"]');
// Use getByTestId; no shadow-specific syntax needed.
await expect(widget.getByTestId('shadow-state')).toHaveText('OFF');
await widget.getByTestId('shadow-btn').click();
await expect(widget.getByTestId('shadow-state')).toHaveText('ON');
// Equivalent CSS shadow-piercing if you ever need explicit form:
// page.locator('pilot-toggle >> [data-testid="shadow-btn"]')
});