Frames
Introduction
A page has one main frame. Page-level interactions (such as click, type, locator) run in the main frame by default.
A page can also have additional frames created with the <iframe> element. Use await browser.FrameAsync(iframeSelector) to get an ICSTestingFrame (C:\CSTesting-DotNet). Call async methods on that frame; use the main browser again for the top document.
To go back to the main page, use the original browser reference for main-page actions. You do not need to "switch back"; you keep two references: one for the main page and one for the frame.
Locating elements inside a frame
Call await browser.FrameAsync(iframeSelector) to get an ICSTestingFrame. Then use Locator, ClickAsync, TypeAsync, etc.
await browser.GotoAsync("https://example.com/page-with-iframe");
await browser.WaitForSelectorAsync("iframe", 10_000);
var frame = await browser.FrameAsync("iframe");
await frame.TypeAsync("#username-input", "John");
await frame.ClickAsync("button[type=submit]");
await browser.Locator("body").ClickAsync();
Selector formats inside the frame are the same as on the main page: CSS, XPath (//input[@name='user']), id=, name=, or any attr=value.
Frame access
Pass a selector for the <iframe> element in the parent document. Nested frames: await outer.FrameAsync("iframe#inner").
| Approach | CSTesting .NET |
|---|---|
| By tag | await browser.FrameAsync("iframe") |
| By id | await browser.FrameAsync("iframe#my-frame") |
| By name attribute | await browser.FrameAsync("iframe[name='frame-login']") |
| By URL (src) | await browser.FrameAsync("iframe[src*='domain']") |
By name / URL:
var frame = await browser.FrameAsync("iframe[name='frame-login']");
await frame.TypeAsync("#username-input", "John");
var frame2 = await browser.FrameAsync("iframe[src*='domain']");
await frame2.TypeAsync("#username-input", "John");
Interacting with the frame
ICSTestingFrame provides ClickAsync, TypeAsync, Locator, EvaluateAsync, ContentAsync, WaitForSelectorAsync, nested FrameAsync, etc. (see ICSTestingFrame in the repo). Assertions use NUnit with values from await frame.GetTextContentAsync(selector) or locators.
Example:
var frame = await browser.FrameAsync("iframe#login");
await frame.TypeAsync("name=user", "John");
await frame.TypeAsync("name=pass", "secret");
await frame.ClickAsync("button[type=submit]");
Nested frames
var outer = await browser.FrameAsync("iframe#outer");
var inner = await outer.FrameAsync("iframe#inner");
await inner.ClickAsync("button");
await inner.TypeAsync("#input", "hello");
var h1 = await outer.GetTextContentAsync("h1");
await browser.Locator("body").ClickAsync();
Selector for the inner iframe is evaluated in the context of the outer frame's document. So "iframe#inner" means an iframe with id="inner" inside the outer frame.
Summary
| Topic | CSTesting |
|---|---|
| Main frame | Default. Use the browser reference. |
| Single iframe | var frame = await browser.FrameAsync("iframeSelector"); then await frame.ClickAsync(...), frame.Locator(...), etc. |
| By name | await browser.FrameAsync("iframe[name='frame-login']") |
| By URL | await browser.FrameAsync("iframe[src*='domain']") |
| Nested frame | await outer.FrameAsync("iframe#inner") |
| Back to main | Use the original browser reference; no explicit "switch back". |
Note: Wait until the iframe is attached, e.g. await browser.WaitForSelectorAsync("iframe", 10_000) before FrameAsync.