Web Views
Web views are native components that render content not natively supported by the platform, like web pages or PDF documents. However, elements within web views are not native components, making direct interaction through Detox challenging. To address this, Detox offers a suite of matchers, actions, and expectations designed for interacting with content inside web views.
Locating Elements in Web Views
web.element(matcher)
In cases where there's only one web view present on the screen, you may use the web.element()
function, paired with web view matchers, to reference elements within the web view.
Upon obtaining the element reference, you can utilize web view actions and expectations on the webView element.
const innerElement = web.element(by.web.id('inner_element_identifier'));
await expect(innerElement).toHaveText('Hello World!');
In the example above, we locate an inner element by its id
HTML attribute and verify its text content.
web(nativeMatcher).element(matcher)
If you have multiple web views on the screen, you must locate a specific web view first by using a native matcher, e.g.:
const myWebView = web(by.id('webview_identifier'));
Following that, you can locate the element within the identified web view:
const innerElement = myWebView.element(by.web.id('inner_element_identifier'));
await expect(innerElement).toHaveText('Hello World!');
web(nativeMatcher).atIndex(index).element(matcher)
(iOS only)
This matcher is available for iOS only. See this GitHub issue for more information.
If you have multiple web views on the screen and you want to interact with a specific web view, you can use the atIndex()
method to choose the web view at the specified index.
const myWebView = web(by.id('webview_identifier')).atIndex(1);
const innerElement = myWebView.element(by.web.id('inner_element_identifier'));
await expect(innerElement).toHaveText('Hello World!');
In the example above, we use atIndex()
to select the second web view on the screen (that has the specified accessibility identifier) and then locate an HTML element inside that web view.
Matchers
Web view matchers are used to find elements within a web view:
by.web.id()
by.web.className()
by.web.cssSelector()
by.web.name()
by.web.xpath()
by.web.href()
by.web.hrefContains()
by.web.tag()
by.web.value()
(iOS only)by.web.label()
(iOS only, supportsasSecured()
)by.web.type()
(iOS only,asSecured()
only)atIndex()
by.web.id(id)
Match elements with the specified accessibility identifier.
web.element(by.web.id('identifier'));
by.web.className(className)
Match elements with the specified CSS class name.
web.element(by.web.className('className'));
by.web.cssSelector(cssSelector)
Match elements with the specified CSS selector.
web.element(by.web.cssSelector('#cssSelector'));
by.web.name(name)
Match form input elements with the specified name
attribute.
web.element(by.web.name('name'));
by.web.xpath(xpath)
Match elements with the specified XPath.
web.element(by.web.xpath('//*[@id="testingh1-1"]'));
by.web.href(href)
Match elements with the specified href
.
web.element(by.web.href('https://wix.com'));
You might face issues with this matcher on Android. Check this GitHub issue for more information.
by.web.hrefContains(href)
Match elements that contain the specified href
.
web.element(by.web.hrefContains('wix'));
You might face issues with this matcher on Android. Check this GitHub issue for more information.
by.web.tag(tag)
Match elements with the specified tag.
web.element(by.web.tag('h1'));
by.web.value(value)
This matcher is available for iOS only at the moment.
Match elements with the specified value.
web.element(by.web.value('value'));
by.web.label(label)
This matcher is available for iOS only at the moment and supports asSecured()
.
Match elements with the specified label.
web.element(by.web.label('label'));
Supports asSecured()
on iOS only:
web.element(by.web.label('label')).asSecured();
by.web.type(accessibilityType)
This matcher is available for iOS only at the moment and supported with asSecured()
only.
Match elements with the specified type.
web.element(by.web.type('textField')).asSecured();
The type value can be any of XCUIElement.ElementType values, such as 'button' or 'textField'. See XCUIElement.ElementType.
atIndex(index)
Choose the element at the specified index.
web.element(by.web.tag('h1')).atIndex(1);
Use it sparingly for those rare cases when you cannot make your matcher less ambiguous, so it returns one element only.
Actions
Web view actions are used to interact with elements within a web view:
tap()
(supportsasSecured()
)typeText()
(supportsasSecured()
)replaceText()
(supportsasSecured()
)clearText()
(supportsasSecured()
)selectAllText()
getText()
scrollToView()
focus()
moveCursorToEnd()
runScript()
getCurrentUrl()
getTitle()
tap()
Tap the element.
await web.element(by.web.id('identifier')).tap();
Supports asSecured()
on iOS only:
await web.element(by.web.label('Submit')).asSecured().tap();
typeText(text[, isContentEditable])
Type the specified text into the element.
isContentEditable
is an optional parameter that indicates whether the element should be a content-editable (contenteditable
) element, and defaults to false
.
await web.element(by.web.id('identifier')).typeText('Hello World!');
Supports asSecured()
on iOS only:
await web.element(by.web.type('textField')).asSecured().typeText('Hello World!');
The isContentEditable
parameter is currently necessary for content-editable elements only on Android.
On iOS, Detox automatically detects content-editable elements regardless of this parameter.
replaceText(text)
Replace the text of the element with the specified text.
await web.element(by.web.id('identifier')).replaceText('Hello World!');
Supports asSecured()
on iOS only:
await web.element(by.web.type('textField')).asSecured().replaceText('Hello World!');
This action is currently not supported for content-editable elements on Android.
On iOS, Detox automatically detects content-editable elements and replaces their text.
clearText()
Clear the text of the element.
await web.element(by.web.id('identifier')).clearText();
Supports asSecured()
on iOS only:
await web.element(by.web.type('textField')).asSecured().clearText();
This action is currently not supported for content-editable elements on Android.
On iOS, Detox automatically detects content-editable elements and clears their text.
selectAllText()
Select all the text of the element.
await web.element(by.web.id('identifier')).selectAllText();
This action is currently supported for content-editable elements only on Android.
On iOS, Detox can select all the text of any element that supports it (for example, an input element).
getText()
Get the text of the element.
const text = await web.element(by.web.id('identifier')).getText();
scrollToView()
Scroll to the element until its top is at the top of the viewport.
await web.element(by.web.id('identifier')).scrollToView();
focus()
Focus on the element.
await web.element(by.web.id('identifier')).focus();
moveCursorToEnd()
Move the input cursor to the end of the element's content.
await web.element(by.web.id('identifier')).moveCursorToEnd();
This action is currently supported for content-editable elements only on Android.
On iOS, Detox can move the cursor to the end of any element that supports it (for example, an input element).
runScript(script[, args])
Run the specified script on the element. The script should be a string that contains a valid JavaScript function. It will be called with that element as the first argument:
const webElement = web.element(by.web.id('identifier'));
await webElement.runScript('(el) => el.click()');
For convenience, you can pass a function instead of a string, but please note that this will not work if the function uses any variables from the outer scope:
The script can accept additional arguments and return a value. Make sure the values are primitive types or serializable objects, as they will be converted to JSON and back:
const text = await webElement.runScript(function get(element, property) {
return element[property];
}, ['textContent']);
getCurrentUrl()
Get the current URL of the web view.
const url = await web.element(by.web.id('identifier')).getCurrentUrl();
Although this action returns the URL of the presented web document, it can be called from an inner element only (for example, an iframe id or the HTML) and not from the root native web view element itself.
You might face issues with this action on Android. Check this GitHub issue for more information.
getTitle()
Get the title of the web view.
Although this action returns the title of the presented web document, it can be called from an inner element only (for example, an iframe id or the HTML) and not from the root native web view element itself.
const title = await web.element(by.web.id('identifier')).getTitle();
Expectations
Web view expectations are used to assert the state of elements within a web view:
toHaveText()
toExist()
(supportsasSecured()
)not
(supportsasSecured()
)
toHaveText(text)
Assert that the element has the specified text.
await expect(web.element(by.web.id('identifier'))).toHaveText('Hello World!');
toExist()
Assert that the element exists.
await expect(web.element(by.web.id('identifier'))).toExist();
Supports asSecured()
on iOS only:
await expect(web.element(by.web.label('Hello World!')).asSecured()).toExist();
You might face issues with this expectation on Android. Check this GitHub issue for more information.
not
Negate the expectation.
await expect(web.element(by.web.id('identifier'))).not.toHaveText('Hello World!');
Supports asSecured()
on iOS only:
await expect(web.element(by.web.label('Hello World!')).asSecured().atIndex(1)).not.toExist();
asSecured()
This API is available only on iOS and is currently in the experimental phase. It is subject to changes in the near future.
The asSecured()
API is designed for interacting with web pages that use secured protocols, such as PCI DSS for payment pages. Use it when the regular API fails to interact with such pages. Detox uses system-level interactions with the webview in these scenarios. This approach is less performant and has fewer APIs.
Example:
await web.element(by.web.label('Submit')).asSecured().tap();
Why use asSecured()
?
Use asSecured()
for web pages with secured protocols when regular Detox interactions fail. For CORS issues, consider passing the detoxDisableWebKitSecurity
launch argument to enable less strict security limitations for interacting with secured web views.