This documentation is available as Markdown. For the complete index, see llms.txt. Skip to content

Multi-tab & iframes

For the complete documentation index, see llms.txt

unotest scenarios can span tabs and reach into frames.

Switch tabs

When an action opens a new tab, switch to it by index (0-based):

step("Open the invoice in a new tab", () => {
click(getByRole("link", { name: "View invoice" }));
setPage(1); // focus the new tab
assertVisible(getByText("Invoice #"));
});

Inspect tabs with the list_pages / get_active_context MCP tools while exploring.

setPage waits for the tab at that index to appear, so no pause is needed between the click and the switch.

Wait for a new tab

When you don’t know the new tab’s index, use waitForPage(). Examples: a browser extension opens the tab, or other tabs are already open. waitForPage() returns the first tab the scenario hasn’t received yet and waits if there is none. It switches to that tab and returns its index:

step("The extension opens the report", () => {
click(getByRole("button", { name: "Open report" }));
report = waitForPage(); // active now
assertUrl("/report");
});

A tab counts as received when it was open at the start, when setPage switched to it, or when an earlier waitForPage() returned it. A tab that opened before the call counts too, even if the click was a step earlier. No tab is returned twice. The budget is {timeoutMs} and defaults to the navigation timeout. When no new tab arrives in time, the step fails with no new tab within N ms, N tabs open.

Open and close a tab

newPage(url?) opens a tab, makes it active and returns its index. A relative url resolves against the base URL, as in goto. closePage() closes the active tab, and the steps return to the tab that was active before it. closePage(i) closes another one.

step("Compare with the public page", () => {
newPage("/pricing"); // active now
assertVisible(getByText("Pro"));
closePage(); // back to the tab before it
});

A tab the scenario opened itself is never returned by waitForPage().

Keep a tab in front

setPage(i) does two things: the steps act on tab i, and the browser shows it. setPage(i, {activate: false}) does only the first. Use it to drive an extension panel in the background while the site stays in front. bringToFront(i) does only the second: it shows tab i, and the steps stay where they are. Without an index it shows the active tab.

step("Run the panel with the site in front", () => {
setPage(1, { activate: false }); // steps on the panel, site in front
click(getByRole("button", { name: "Run" }));
bringToFront(); // now show the panel too
});

Work inside an iframe

Enter a frame to scope subsequent calls; exit when done:

step("Submit the embedded payment form", () => {
enterFrame(getByTestId("payment-iframe"));
fill(getByLabel("Card number"), TEST_CARD);
click(getByRole("button", { name: "Pay" }));
exitFrame();
});

Or resolve a single element across the boundary with contentFrame(). See the DSL reference.