Frames and shadow DOM
Find and check elements inside browser frames and open shadow roots.
If a control sits inside an iframe or a web component, your locator may need a different scope. Name a frame explicitly; ordinary locators already search through open shadow roots.
Find elements in a frame Link to Find elements in a frame
An iframe has its own document, which page locators do not enter. Pass a locator for its iframe or frame element to page.frame(). This example assumes your task form is embedded in an iframe titled "Tasks".
import { expect, test } from '@rehearsal-labs/retest'test('saves a task inside a frame', async ({ page }) => { await page.goto('/') const tasks = page.frame(page.locator('iframe[title="Tasks"]')) await tasks.getByTestId('task-title').fill('Release checklist') await tasks.getByTestId('save-task').click() await expect(tasks.getByTestId('saved-task')).toHaveText('Release checklist')})The returned frame has the same finders as a page, scoped to its document. Use tasks.getByTestId('task-title') for the embedded field and page for elements around the frame.
frame() creates a scope without reading the document, so it needs no await. The frame element is found again on every action and check, including after the app replaces it.
Nested and cross-origin frames Link to Nested and cross-origin frames
To enter a nested frame, make its element locator from the frame that contains it. If the task editor embeds a Preview frame, you can check the saved title inside that second document.
const preview = tasks.frame(tasks.locator('iframe[title="Preview"]'))await expect(preview.getByTestId('saved-task')).toHaveText('Release checklist')The iframe locator is made from tasks, so it names an element inside the parent frame. Passing a locator made on page to tasks.frame() would fail with usage, because it belongs to a different document scope.
Frames from another origin work through the same API, including ones the browser runs in another process. If you fill a secret there, the frame's origin must be among the secret's allowed destinations.
A frame has no goto() of its own, and URL and title checks belong to the page. If the frame navigates after a form submission, its next locator searches the new document.
Frame failures and limits Link to Frame failures and limits
A missing frame is waited for within the action budget, then fails with not_found and names the frame locator. Several matching frame elements fail with ambiguous; an element that is not a frame fails with usage.
Retest checks both the element inside the frame and the frames around it before sending input. A hidden frame or an overlay covering it can make the action fail with not_actionable, even if its own document shows the button.
Frame actions and checks work on Chromium-family browsers. Firefox, WebKit and Electron refuse frame-scoped locators with unsupported, while native apps have no browser frames.
Operations inside a frame
setInputFiles() and element screenshot comparisons inside a frame are unsupported. Catch its file picker through page.fileChooser() instead. Layout checks cannot compare elements in different frames and fail with usage if you try.
Frame steps name their own document in the report, but failure screenshots show the page viewport. A frame below that viewport may be outside the picture because checks do not scroll it into view.
Find elements in shadow DOM Link to Find elements in shadow DOM
Shadow DOM holds a web component's internal markup. If you put the task form inside an open shadow root, its test can keep the same locators. Test ids, text, placeholders and CSS search through nested open roots too.
import { expect, test } from '@rehearsal-labs/retest'test('saves a task rendered by a component', async ({ page }) => { await page.goto('/') await page.getByTestId('task-title').fill('Release checklist') await page.getByTestId('save-task').click() await expect(page.getByTestId('saved-task')).toHaveText('Release checklist')})The example searches the page and its open shadow roots with no special shadow selector. You can also scope a finder inside a component locator when several components show the same control.
CSS also crosses open root boundaries: page.locator('task-editor button.primary') can find a primary button inside that component. Use ordinary CSS without a >>> separator; Locators explains how to narrow a query with several matches.
Shadow DOM support Link to Shadow DOM support
On Chromium-family browsers and Electron, role and label locators also reach open roots through the browser's accessibility tree. A closed shadow root is out of reach on every engine, so Retest finds no elements inside it.
Firefox and WebKit support test id, text, placeholder and CSS queries through open roots. A role or label query fails with unsupported when an open root in its search could hold a match; the failure names the component host.
If you hit that refusal, use a test id or another supported finder for the element inside the root. Shadow DOM is a web feature; native app elements use their platform's locators.