Frames
When a page embeds an iframe — a payment widget, a chat, a map — Playwright can't interact with elements inside it directly through page locators. I use page.frameLocator() to get a locator scoped to the iframe's DOM, then use standard locators inside it. The most common real-world case: testing a Stripe or PayPal embedded payment form.
Why iframes need special handling
An iframe is a separate HTML document embedded inside the main page. It has its own DOM, its own JavaScript context, and its own origin (often a different domain). page.getByRole() and other locators only search the main frame's DOM — they don't cross iframe boundaries.
The solution: page.frameLocator(selector) returns a FrameLocator — a locator object that's scoped to the iframe's content. I chain standard locators on it just like on page.
frameLocator — the preferred approach
frameLocator returns a locator that scopes all subsequent locator calls to the iframe's DOM. I target the iframe by its CSS selector (usually a class, id, or title attribute), then use normal locators inside.
The most common real-world scenario — embedded payment form where the card fields are inside a Stripe iframe:
Frame objects — direct access
page.frame() gives access to the raw Frame object, which is useful when I need to run JavaScript inside the frame, evaluate expressions, or access frame metadata like the URL. I target frames by name attribute or URL pattern.
Nested iframes
When a frame contains another iframe, I chain frameLocator calls. Each level scopes the search to the next iframe: