Browser Extension
While running Automated tests, you might want to have a browser extension installed. Below is you can find information on how to add a browser extension to a Chrome, Firefox, Edge or Safari browser running in the TestingBot cloud.
To get started, make sure you have the extension file ready, then upload it either to a public URL or use the TestingBot Storage.
Once you have a URL, you can specify the load-extension
capability. This will instruct the remote TestingBot browsers to download the extension and add it to the browser at startup.
"load-extension" : "https://..."
"tb:options" : {
"load-extension" : "https://..."
}
Browser | Supported Format(s) | Example |
---|---|---|
Chrome |
|
|
Firefox |
|
|
Edge |
|
|
Safari & Safari Technology Preview |
|
|
TestingBot Storage
With TestingBot Storage, you can upload your extensions to the TestingBot servers.
The advantage of this is that our test VMs can immediately download your extension from the TestingBot network, which is much faster than downloading from the public internet.
The API call will return a unique identifier, similar to tb://efe932jfk
.
You can then use this identifier with the capability: "load-extension" : "tb://..."
Chrome Browser Extension Testing
Once you've specified "load-extension"
, the Chrome browser will start up with the extension preloaded.
You can now run your Selenium tests while the extension is active on the remote browser.
UI Testing Chrome Extensions
To test the UI of your Chrome extension, you can have your Selenium test navigate to a chrome-extension://
URL.
For example, if the ID of your addon is paaamahlcokbnafoiahanekijojkhbdm
, you can navigate to chrome-extension://paaamahlcokbnafoiahanekijojkhbdm
.
Please see the NodeJS example below for a complete rundown on how to test the UI of your Chrome extension:
const webdriver = require('selenium-webdriver');
const capabilities = {
'platform' : 'WIN10',
'browserName' : 'chrome',
'version' : 'latest',
'tb:options': {
'load-extension': 'https://...path..to..extension'
}
}
async function runTest () {
let driver = new webdriver.Builder()
.usingServer('https://api_key:api_secret@hub.testingbot.com/wd/hub')
.withCapabilities(capabilities)
.build();
await driver.get('chrome-extension://paaamahlcokbnafoiahanekijojkhbdm/popup/index.html');
const element = await driver.findElement(By.css('#environments'));
const isElementDisplayed = await element.isDisplayed();
if (isElementDisplayed) {
console.log('Element is present on the page.');
} else {
console.log('Element is not present on the page.');
}
await driver.quit();
}
runTest();
Firefox Browser Extension Testing
You can specify the URL to a xpi
file in the load-extension
capability.
This will make sure the Firefox browser has the extension installed (as a temporary extension) at the start of the Selenium test.
UI Testing Firefox Extensions
If you want to test the UI of your Firefox extension, the moz-extension://
URL, you will need to specify the extension ID in the URL.
Because TestingBot installs the extension as a temporary extension, the id will always be different as it is generated as a random uuid(4).
TestingBot provides a capability to hardcode your Firefox exension to a fixed uuid which you've generated, by using the firefoxExtensionMap
capability. To use this, you could set the ID in the manifest.json
of the extension:
"browser_specific_settings": {
"gecko": {
"id": "addon@testingbot.com",
"strict_min_version": "109.0"
}
}
You can now link the gecko id to a uuid you've generated:
const webdriver = require('selenium-webdriver');
const capabilities = {
'platform' : 'WIN10',
'browserName' : 'chrome',
'version' : 'latest',
'tb:options': {
'load-extension': 'https://...path..to..extension',
'firefoxExtensionMap': {
'addon@testingbot.com': '3d9b1639-77fb-44a1-888a-6d97d773e96b'
}
}
}
async function runTest () {
let driver = new webdriver.Builder()
.usingServer('https://api_key:api_secret@hub.testingbot.com/wd/hub')
.withCapabilities(capabilities)
.build();
await driver.get('moz-extension://3d9b1639-77fb-44a1-888a-6d97d773e96b/popup/index.html');
const element = await driver.findElement(By.css('#environments'));
const isElementDisplayed = await element.isDisplayed();
if (isElementDisplayed) {
console.log('Element is present on the page.');
} else {
console.log('Element is not present on the page.');
}
await driver.quit();
}
runTest();
Edge Browser Extension Testing
Once you've specified "load-extension"
, the Edge browser will start up with the extension preloaded.
You can now run your Selenium tests while the extension is active on the remote Edge browser.
UI Testing Edge Extensions
To test the UI of your Edge extension, you can have your Selenium test navigate to a chrome-extension://
or extension://
URL.
For example, if the ID of your addon is paaamahlcokbnafoiahanekijojkhbdm
, you can navigate to extension://paaamahlcokbnafoiahanekijojkhbdm
.
Please see the NodeJS example below for a complete rundown on how to test the UI of your Chrome extension:
const webdriver = require('selenium-webdriver');
const capabilities = {
'platform' : 'WIN10',
'browserName' : 'microsoftedge',
'version' : 'latest',
'tb:options': {
'load-extension': 'https://...path..to..extension'
}
}
async function runTest () {
let driver = new webdriver.Builder()
.usingServer('https://api_key:api_secret@hub.testingbot.com/wd/hub')
.withCapabilities(capabilities)
.build();
await driver.get('extension://paaamahlcokbnafoiahanekijojkhbdm/popup/index.html');
const element = await driver.findElement(By.css('#environments'));
const isElementDisplayed = await element.isDisplayed();
if (isElementDisplayed) {
console.log('Element is present on the page.');
} else {
console.log('Element is not present on the page.');
}
await driver.quit();
}
runTest();
Safari Extension Testing
Safari 16 and higher onlyTestingBot allows you to upload a zip file containing your (unsigned) Safari Web Extension or Safari App Extension. Simply provide a URL to a zip file containing the extension, or upload the zip file on TestingBot Storage.
You can now specify the URL to this file in the load-extension
capability. When your test starts on a remote Safari instance, the extension will be enabled by default.
TestingBot provides Safari Technology Preview as well, allowing you to test on the upcoming new Safari version.
You can use the same extension testing features, simply change the version
to dev
.
Safari Extension Automation
TestingBot will automatically install and enable the extension in the Safari browser. Permissions will be granted automatically for the extension to access all webpages during the test.

UI Testing Safari Extensions
To test the UI of your Safari extension, you will need to know the safari-web-extension://
or safari-app-extension://
URI. As Safari can change this to a random UUID, you will need to use the tb:getExtensionId
command in your test to determine the correct URI to use.
Safaridriver currently contains a bug that does not detect the load completion of a safari-web-extension://
request. To work around this, set the pageLoad timeout to a low value and catch any timeout error.
Please see the examples below on how to run a UI test against a Safari Web/App Extension:
require 'rubygems'
require 'selenium-webdriver'
caps = Selenium::WebDriver::Remote::Capabilities.new
caps["browserName"] = "safari"
caps["browserVersion"] = "18.0"
caps["platformName"] = "SEQUOIA"
caps["tb:options"] = {
"load-extension": "tb://...safari-extension-path..."
}
http_client = Selenium::WebDriver::Remote::Http::Default.new
http_client.read_timeout = 300
http_client.open_timeout = 300
driver = Selenium::WebDriver.for(
:remote,
:url => "https://API_KEY:API_SECRET@hub.testingbot.com/wd/hub",
:options => caps,
:http_client => http_client)
driver.navigate.to "http://www.google.com/ncr"
driver.manage.timeouts.page_load = 5
extension_id = driver.execute_script("tb:getExtensionId")
begin
driver.navigate.to "#{extension_id}popup/index.html"
rescue => e
puts e.message
end
puts driver.page_source
driver.quit
import org.openqa.selenium.*;
import org.openqa.selenium.remote.*;
import java.net.URL;
import java.util.*;
public class SafariExtensionTest {
public static void main(String[] args) throws Exception {
MutableCapabilities caps = new MutableCapabilities();
caps.setCapability("browserName", "safari");
caps.setCapability("browserVersion", "18.0");
caps.setCapability("platformName", "SEQUOIA");
Map<String, Object> tbOptions = new HashMap<>();
tbOptions.put("load-extension", "tb://...safari-extension-path...");
caps.setCapability("tb:options", tbOptions);
WebDriver driver = new RemoteWebDriver(
new URL("https://API_KEY:API_SECRET@hub.testingbot.com/wd/hub"),
caps
);
driver.get("http://www.google.com/ncr");
((JavascriptExecutor) driver).executeScript("tb:setPageLoadTimeout", 5);
String extensionId = (String) ((JavascriptExecutor) driver).executeScript("tb:getExtensionId");
try {
driver.get(extensionId + "popup/index.html");
} catch (TimeoutException e) {
System.out.println(e.getMessage());
}
System.out.println(driver.getPageSource());
driver.quit();
}
}
const { Builder } = require("selenium-webdriver");
(async () => {
let caps = {
browserName: "safari",
browserVersion: "18.0",
platformName: "SEQUOIA",
"tb:options": {
"load-extension": "tb://...safari-extension-path..."
}
};
let driver = await new Builder()
.usingServer("https://API_KEY:API_SECRET@hub.testingbot.com/wd/hub")
.withCapabilities(caps)
.build();
await driver.get("http://www.google.com/ncr");
await driver.manage().setTimeouts({ pageLoad: 5000 });
const extensionId = await driver.executeScript("tb:getExtensionId");
try {
await driver.get(`${extensionId}popup/index.html`);
} catch (e) {
console.log(e.message);
}
const pageSource = await driver.getPageSource();
console.log(pageSource);
await driver.quit();
})();
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
caps = {
"browserName": "safari",
"browserVersion": "18.0",
"platformName": "SEQUOIA",
"tb:options": {
"load-extension": "tb://...safari-extension-path..."
}
}
driver = webdriver.Remote(
command_executor="https://API_KEY:API_SECRET@hub.testingbot.com/wd/hub",
desired_capabilities=caps
)
driver.get("http://www.google.com/ncr")
driver.set_page_load_timeout(5)
extension_id = driver.execute_script("tb:getExtensionId")
try:
driver.get(f"{extension_id}popup/index.html")
except Exception as e:
print(e)
print(driver.page_source)
driver.quit()
using OpenQA.Selenium;
using OpenQA.Selenium.Remote;
using System;
using System.Collections.Generic;
class SafariExtensionTest
{
static void Main()
{
var capabilities = new DesiredCapabilities();
capabilities.SetCapability("browserName", "safari");
capabilities.SetCapability("browserVersion", "18.0");
capabilities.SetCapability("platformName", "SEQUOIA");
capabilities.SetCapability("tb:options", new Dictionary<string, object> {
{ "load-extension", "tb://...safari-extension-path..." }
});
var driver = new RemoteWebDriver(
new Uri("https://API_KEY:API_SECRET@hub.testingbot.com/wd/hub"),
capabilities
);
driver.Navigate().GoToUrl("http://www.google.com/ncr");
driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(5);
string extensionId = (string)((IJavaScriptExecutor)driver).ExecuteScript("tb:getExtensionId");
try
{
driver.Navigate().GoToUrl(extensionId + "popup/index.html");
}
catch (WebDriverTimeoutException e)
{
Console.WriteLine(e.Message);
}
Console.WriteLine(driver.PageSource);
driver.Quit();
}
}
Older Safari versions
For Safari versions up to Safari 12, you can use a .safariextz
format, in a zip
file, to run and test Safari extensions.
iOS Mobile Safari Browser Extension Testing
You can test your iOS Mobile Safari native extension with TestingBot. Simply upload your extension .ipa
file to TestingBot Storage and use it in your capabilities:
{
"appium:app": "tb://path-to-mobile-safari-ext-ipa",
"tb:realDevice": true,
"appium:includeSafariInWebviews": true,
"deviceName": "iPhone 16"
}
Next, you will need to allow this extension through the settings (preferences) app. Finally, we'll switch to the Safari app and change the context to Safari (WEBVIEW). Please see the examples below.
require 'appium_lib'
caps = {
caps: {
platformName: 'iOS',
automationName: 'XCUITest',
'appium:app' => 'tb://path-to-mobile-safari-ext-ipa',
'tb:realDevice' => true,
'deviceName' => 'iPhone 16',
'appium:includeSafariInWebviews' => true
},
appium_lib: {
server_url: 'https://hub.testingbot.com/wd/hub'
}
}
driver = Appium::Driver.new(caps, true)
driver.start_driver
begin
# 1. Open Safari settings via deep link
driver.execute_script('mobile: deepLink', args: { url: 'app-prefs:com.apple.mobilesafari' })
# 2. Tap "Extensions"
extensions = driver.find_element(:predicate, "type == 'XCUIElementTypeCell' AND name == 'Extensions'")
extensions.click
# 3. Tap your extension
extension = driver.find_element(:predicate, "type == 'XCUIElementTypeCell' AND name == 'webrequest-har-collect'")
extension.click
# 4. Enable "Allow Extension"
allow_switch = driver.find_element(:predicate, "type == 'XCUIElementTypeSwitch' AND name == 'Allow Extension'")
allow_switch.click
# 5. Tap "All Websites"
all_sites = driver.find_element(:predicate, "type == 'XCUIElementTypeCell' AND name == 'All Websites'")
all_sites.click
# 6. Tap "Allow"
allow = driver.find_element(:predicate, "type == 'XCUIElementTypeCell' AND name == 'Allow'")
allow.click
# 7. Terminate Settings app
driver.execute_script('mobile: terminateApp', args: { bundleId: 'com.apple.Preferences' })
# 8. Launch Safari
driver.execute_script('mobile: launchApp', args: { bundleId: 'com.apple.mobilesafari' })
# 9. Switch to WebView context
contexts = driver.available_contexts
webview = contexts.find { |ctx| ctx.include?('WEBVIEW') }
driver.set_context(webview) if webview
puts 'Finished automation steps.'
ensure
driver.quit_driver
end
import io.appium.java_client.AppiumBy;
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.remote.IOSMobileCapabilityType;
import io.appium.java_client.remote.MobileCapabilityType;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.net.URL;
import java.util.List;
import java.util.Set;
public class IOSAppiumAutomation {
public static void main(String[] args) {
try {
DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability(MobileCapabilityType.PLATFORM_NAME, "iOS");
caps.setCapability(MobileCapabilityType.AUTOMATION_NAME, "XCUITest");
caps.setCapability("appium:app", "tb://path-to-mobile-safari-ext-ipa");
caps.setCapability("tb:realDevice", true);
caps.setCapability(MobileCapabilityType.DEVICE_NAME, "iPhone 16");
caps.setCapability("appium:includeSafariInWebviews", true);
IOSDriver<WebElement> driver = new IOSDriver<>(
new URL("https://hub.testingbot.com/wd/hub"), caps);
// 1. Open Safari Settings via deep link
driver.executeScript("mobile: deepLink",
java.util.Map.of("url", "app-prefs:com.apple.mobilesafari"));
// 2. Tap "Extensions"
WebElement extensionsCell = driver.findElement(
new AppiumBy.ByIosNsPredicate("type == 'XCUIElementTypeCell' AND name == 'Extensions'"));
extensionsCell.click();
// 3. Tap your extension name
WebElement extensionItem = driver.findElement(
new AppiumBy.ByIosNsPredicate("type == 'XCUIElementTypeCell' AND name == 'webrequest-har-collect'"));
extensionItem.click();
// 4. Toggle "Allow Extension"
WebElement allowSwitch = driver.findElement(
new AppiumBy.ByIosNsPredicate("type == 'XCUIElementTypeSwitch' AND name == 'Allow Extension'"));
allowSwitch.click();
// 5. Tap "All Websites"
WebElement allWebsites = driver.findElement(
new AppiumBy.ByIosNsPredicate("type == 'XCUIElementTypeCell' AND name == 'All Websites'"));
allWebsites.click();
// 6. Tap "Allow"
WebElement allow = driver.findElement(
new AppiumBy.ByIosNsPredicate("type == 'XCUIElementTypeCell' AND name == 'Allow'"));
allow.click();
// 7. Terminate Settings
driver.executeScript("mobile: terminateApp",
java.util.Map.of("bundleId", "com.apple.Preferences"));
// 8. Launch Safari
driver.executeScript("mobile: launchApp",
java.util.Map.of("bundleId", "com.apple.mobilesafari"));
// 9. Switch to WebView context
Set<String> contexts = driver.getContextHandles();
for (String context : contexts) {
if (context.contains("WEBVIEW")) {
driver.context(context);
break;
}
}
System.out.println("Finished automation steps.");
driver.quit();
} catch (Exception e) {
e.printStackTrace();
}
}
}
const { remote } = require('webdriverio');
(async () => {
const driver = await remote({
path: 'https://hub.testingbot.com/wd/hub',
port: 443,
capabilities: {
'appium:app': 'tb://path-to-mobile-safari-ext-ipa',
platformName: 'iOS',
automationName: 'XCUITest',
"tb:realDevice": true,
"appium:includeSafariInWebviews": true,
"deviceName": "iPhone 16"
},
});
// 1. Open Safari settings via deep link
await driver.execute('mobile: deepLink', {
url: 'app-prefs:com.apple.mobilesafari',
});
// 2. Tap "Extensions"
const extensionsCell = await driver.$(`-ios predicate string:type == 'XCUIElementTypeCell' AND name == 'Extensions'`);
await extensionsCell.waitForExist({ timeout: 10000 });
await extensionsCell.click();
// 3. Tap on your extension (e.g., "webrequest-har-collect")
const extensionItem = await driver.$(`-ios predicate string:type == 'XCUIElementTypeCell' AND name == 'webrequest-har-collect'`);
await extensionItem.waitForExist({ timeout: 60000 });
await extensionItem.click();
// 4. Enable the switch "Allow Extension"
const allowSwitch = await driver.$(`-ios predicate string:type == 'XCUIElementTypeSwitch' AND name == 'Allow Extension'`);
await allowSwitch.waitForExist({ timeout: 60000 });
await allowSwitch.click();
// 5. Tap "All Websites"
const allWebsites = await driver.$(`-ios predicate string:type == 'XCUIElementTypeCell' AND name == 'All Websites'`);
await allWebsites.waitForExist({ timeout: 60000 });
await allWebsites.click();
// 6. Tap "Allow"
const allow = await driver.$(`-ios predicate string:type == 'XCUIElementTypeCell' AND name == 'Allow'`);
await allow.waitForExist({ timeout: 60000 });
await allow.click();
// 7. Terminate Settings app
await driver.execute('mobile: terminateApp', {
bundleId: 'com.apple.Preferences',
});
// 8. Launch Safari
await driver.execute('mobile: launchApp', {
bundleId: 'com.apple.mobilesafari',
});
// 9. Switch to WebView context
const contexts = await driver.getContexts();
const webview = contexts.find(ctx => ctx.includes('WEBVIEW'));
if (webview) {
await driver.switchContext(webview);
}
console.log('Finished automation steps.');
await driver.deleteSession();
})();
from appium import webdriver
from selenium.webdriver.common.by import By
import time
caps = {
"platformName": "iOS",
"automationName": "XCUITest",
"appium:app": "tb://path-to-mobile-safari-ext-ipa",
"appium:includeSafariInWebviews": True,
"deviceName": "iPhone 16",
"tb:realDevice": True,
}
driver = webdriver.Remote(
command_executor='https://hub.testingbot.com/wd/hub',
desired_capabilities=caps
)
try:
# 1. Open Safari settings via deep link
driver.execute_script('mobile: deepLink', {
'url': 'app-prefs:com.apple.mobilesafari'
})
# 2. Tap "Extensions"
extensions_cell = driver.find_element(
By.IOS_PREDICATE,
"type == 'XCUIElementTypeCell' AND name == 'Extensions'"
)
extensions_cell.click()
# 3. Tap on your extension (e.g., "webrequest-har-collect")
extension_item = driver.find_element(
By.IOS_PREDICATE,
"type == 'XCUIElementTypeCell' AND name == 'webrequest-har-collect'"
)
extension_item.click()
# 4. Enable the switch "Allow Extension"
allow_switch = driver.find_element(
By.IOS_PREDICATE,
"type == 'XCUIElementTypeSwitch' AND name == 'Allow Extension'"
)
allow_switch.click()
# 5. Tap "All Websites"
all_websites = driver.find_element(
By.IOS_PREDICATE,
"type == 'XCUIElementTypeCell' AND name == 'All Websites'"
)
all_websites.click()
# 6. Tap "Allow"
allow = driver.find_element(
By.IOS_PREDICATE,
"type == 'XCUIElementTypeCell' AND name == 'Allow'"
)
allow.click()
# 7. Terminate Settings app
driver.execute_script('mobile: terminateApp', {
'bundleId': 'com.apple.Preferences'
})
# 8. Launch Safari
driver.execute_script('mobile: launchApp', {
'bundleId': 'com.apple.mobilesafari'
})
# 9. Switch to WebView context
contexts = driver.contexts
webview = next((ctx for ctx in contexts if 'WEBVIEW' in ctx), None)
if webview:
driver.switch_to.context(webview)
print("Finished automation steps.")
finally:
driver.quit()
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.iOS;
using System;
using System.Linq;
using System.Collections.ObjectModel;
class Program
{
static void Main(string[] args)
{
var options = new AppiumOptions();
options.AddAdditionalCapability(MobileCapabilityType.PlatformName, "iOS");
options.AddAdditionalCapability(MobileCapabilityType.AutomationName, "XCUITest");
options.AddAdditionalCapability("appium:app", "tb://path-to-mobile-safari-ext-ipa");
options.AddAdditionalCapability("tb:realDevice", true);
options.AddAdditionalCapability("deviceName", "iPhone 16");
options.AddAdditionalCapability("appium:includeSafariInWebviews", true);
var driver = new IOSDriver<IWebElement>(
new Uri("https://hub.testingbot.com/wd/hub"),
options
);
try
{
// 1. Open Safari settings via deep link
driver.ExecuteScript("mobile: deepLink", new
{
url = "app-prefs:com.apple.mobilesafari"
});
// 2. Tap "Extensions"
var extensionsCell = driver.FindElementByIosNsPredicate(
"type == 'XCUIElementTypeCell' AND name == 'Extensions'");
extensionsCell.Click();
// 3. Tap extension (e.g., "webrequest-har-collect")
var extensionItem = driver.FindElementByIosNsPredicate(
"type == 'XCUIElementTypeCell' AND name == 'webrequest-har-collect'");
extensionItem.Click();
// 4. Tap "Allow Extension" switch
var allowSwitch = driver.FindElementByIosNsPredicate(
"type == 'XCUIElementTypeSwitch' AND name == 'Allow Extension'");
allowSwitch.Click();
// 5. Tap "All Websites"
var allWebsites = driver.FindElementByIosNsPredicate(
"type == 'XCUIElementTypeCell' AND name == 'All Websites'");
allWebsites.Click();
// 6. Tap "Allow"
var allow = driver.FindElementByIosNsPredicate(
"type == 'XCUIElementTypeCell' AND name == 'Allow'");
allow.Click();
// 7. Terminate Settings app
driver.ExecuteScript("mobile: terminateApp", new
{
bundleId = "com.apple.Preferences"
});
// 8. Launch Safari
driver.ExecuteScript("mobile: launchApp", new
{
bundleId = "com.apple.mobilesafari"
});
// 9. Switch to WebView context
ReadOnlyCollection<string> contexts = driver.Contexts;
string webview = contexts.FirstOrDefault(c => c.Contains("WEBVIEW"));
if (webview != null)
{
driver.Context = webview;
}
Console.WriteLine("Finished automation steps.");
}
finally
{
driver.Quit();
}
}
}