The BrowsingContext Object
A browsing context is a tab, a window or a frame that you hold as an object. Commands you call on it run in that tab or frame, while the session and every other context stay where they are. Since v10 this is how WebdriverIO works with tabs, windows and frames in a WebDriver BiDi session, and it replaces switchWindow() and switchFrame() there.
import { browser, expect } from '@wdio/globals'
describe('browsing contexts', () => {
it('works with two tabs and a frame at the same time', async () => {
const page = await browser.url('https://the-internet.herokuapp.com/nested_frames')
const docs = await browser.newWindow('https://webdriver.io/docs/api', { type: 'tab' })
const top = await page.frame({ selector: 'frame[name="frame-top"]' })
const middle = await top.frame({ selector: 'frame[name="frame-middle"]' })
await expect(middle.$('#content')).toHaveText('MIDDLE')
await expect(docs.$('h1')).toBeDisplayed()
console.log(await page.getTitle(), await docs.getTitle())
})
})
Get a browsing context​
| Call | Returns |
|---|---|
browser.url(url) | The session's first top-level context, after navigating it. browser.url() always navigates this one. |
browser.newWindow(url, { type }) | A new tab (type: 'tab') or window, once its page has loaded. The session does not switch to it. |
browser.browsingContexts() | Every open top-level context (tabs and windows, not frames), e.g. a tab the page opened itself. |
context.frame(query) | A frame of a context, also cross-origin and nested ones. |
Hold on to the object and call commands on it. There is no "current" tab or frame to switch between, so contexts can also be used in parallel:
const [titleA, titleB] = await Promise.all([pageA.getTitle(), pageB.getTitle()])
WebDriver BiDi and Classic sessions​
Browsing contexts need a WebDriver BiDi session, which is the default since v10 for Chrome, Edge and Firefox. In a WebDriver Classic session, e.g. with Appium or Safari, there is only the session's current context. There:
browser.url()returns a stand-in for the browser. Commands like$,executeorgetTitlerun on the browser,url,isFrameandparentdescribe the current page, andcontextIdisundefined.frame(),navigate()andactivate()reject and name the Classic command to use instead:browser.switchFrame(),browser.url()orbrowser.switchWindow().
Check browser.isBidi when the same code runs in both kinds of session.
Properties​
| Name | Type | Details |
|---|---|---|
contextId | String | The WebDriver BiDi browsing context id. undefined in a Classic session. |
url | String | The URL the context was last navigated to with browser.url(), navigate() or newWindow(). Navigations the page does itself (links, location, history.pushState) only show after getUrl(). |
isFrame | Boolean | true for a frame, false for a tab or window. |
parent | BrowsingContext | undefined | For a frame, the context frame() was called on (or the frame in between, for a frame nested deeper). undefined for a tab or window. |
browser | Browser | The browser object of the session. |
request | Request | undefined | Load information of the last navigation through browser.url() or navigate(): URL, headers, response, redirects and the requests the page made. |
sessionId | String | Session id, the same as browser.sessionId. |
capabilities | Object | Session capabilities, the same as browser.capabilities. |
options | Object | WebdriverIO options, the same as browser.options. |
isBidi | Boolean | Whether the session uses WebDriver BiDi. |
isMobile | Boolean | Whether the session automates a mobile device. |
Methods​
Commands of a browsing context​
These commands act on the context they are called on. Each has its own reference page.
| Command | Details |
|---|---|
frame | Get a frame of this context as a browsing context of its own. |
navigate | Navigate this context, with the same options as browser.url(). |
refresh | Reload this context. A frame reloads only its own document. |
back / forward | Move through the history of this tab or window. |
activate | Bring this tab or window to the front. |
closeWindow | Close this tab or window. |
getTitle / getUrl | Read the title or URL of the document shown in this context. |
acceptAlert / dismissAlert / getAlertText | Answer or read the user prompt open in this context. |
Browser commands that run in a context​
These are the browser commands of the same name, applied to this context instead of the session's first one. They take the same arguments.
| Command | In a browsing context |
|---|---|
$, $$, custom$, custom$$, react$, react$$ | Find elements in this context's document. |
execute | Run a script in this context's document. |
action, actions, keys, scroll | Send input to this context, also when it is a background tab. |
saveScreenshot, savePDF | Capture this context. |
getCookies, setCookies, deleteCookies | Read and change the cookies of this context's storage partition. |
setViewport | Resize the viewport of this tab or window. |
addInitScript | Run a script before the page scripts, in this tab or window only. |
mock, mockClearAll, mockRestoreAll | Mock the requests of this tab or window only. A mock ends when its tab closes. |
emulate | Emulate a device property, e.g. geolocation or the clock, in this tab or window only. |
restore | Restore emulations, the same as browser.restore(). |
waitUntil, pause | The same as on the browser. |
import { browser, expect } from '@wdio/globals'
it('mocks the requests of one tab only', async () => {
const page = await browser.url('https://webdriver.io')
const tab = await browser.newWindow('https://webdriver.io', { type: 'tab' })
const mock = await tab.mock('**/api/users')
mock.respond([{ name: 'Mocked user' }])
// requests of `tab` get the mocked response, requests of `page` reach the server
})
Top-level only​
A frame shares its tab's history, viewport, network and emulation, so these commands reject on a frame with `<command>` is only available on a top-level browsing context. Call them on the tab: frame.parent until parent is undefined, or the context you called frame() on.
back, forward, activate, closeWindow, setViewport, addInitScript, mock, mockClearAll, mockRestoreAll, emulate, restore
Not available on a browsing context​
Session commands, such as deleteSession, newWindow or browsingContexts, are only on the browser object. Custom commands are too: addCommand and overwriteCommand reject on a context, register them on browser.
Events​
on, once, off, emit, removeListener and removeAllListeners register listeners on the browser, so the events are those of the whole session. For example, a dialog event fires for a prompt in any tab or frame.
Elements of a browsing context​
An element you get through a context belongs to that context. Element commands such as click, setValue or getText run in that context's document, also when it is a background tab or a frame. They follow the WebDriver spec like the drivers do, so they return the same results and the same errors (e.g. element click intercepted) as for an element of the page in front. getComputedRole and getComputedLabel reject for an element of a context other than the session's first one.
const page = await browser.url('https://the-internet.herokuapp.com/nested_frames')
const bottom = await page.frame({ selector: 'frame[name="frame-bottom"]' })
const body = await bottom.$('body')
console.log(await body.getText()) // outputs: "BOTTOM"
Troubleshooting​
| Error | Cause and fix |
|---|---|
`switchFrame` was removed for WebDriver BiDi sessions in WebdriverIO v10. | Call frame() on the context returned by browser.url() or browser.newWindow(). |
`switchWindow` was removed for WebDriver BiDi sessions in WebdriverIO v10. | Hold the context returned by browser.url() or browser.newWindow(), or find one with browser.browsingContexts(). |
`frame()` needs a WebDriver BiDi session, but this session uses WebDriver Classic | The session is a Classic session (e.g. Appium or Safari). Use the Classic command the message names. |
`<command>` is only available on a top-level browsing context | The command was called on a frame. Call it on the frame's tab, see Top-level only. |
`addCommand` is only available on the browser, not on a browsing context | Register custom commands on browser. |
no such frame: the frame "…" was discarded because the page it belongs to navigated away | The page that contained the frame navigated. Get the frame again with frame() on the new page. |