Appium
Introduction
Appium scripts can be used to automate testing on TV Labs devices through the TV Labs Appium proxy at appium.tvlabs.ai
. This page explains how to use the TV Labs Appium proxy to run Appium scripts on TV Labs devices.
What to expect:
- ⏱️ Duration: 10-15 minutes
- 🎯 Audience: Developers interested in using Appium for automated testing on the TV Labs platform
- 🧠 Outcome: Understanding how to use Appium for automated testing on the TV Labs platform
Also see: The Appium documentation provides examples for writing Appium scripts in various languages.
Overview
The TV Labs Appium proxy serves as a gateway to the TV Labs platform for Appium scripts. It enables users to run Appium-based test scripts on TV Labs devices from a local machine, CICD pipeline, or other environments. The proxy provides a single endpoint for device matching, application sideloading, and Appium server session management. Once a session starts, you can view a live stream of the device in the TV Labs sessions dashboard, along with Appium server logs and other session assets and information. Previous sessions are also accessible on this page.
Important: Platform support is a work in progress. Please see Platforms for the list of currently supported platforms.
Connection Information
To use the TV Labs Appium proxy, you will first need to generate an API key from the API Keys page. This key will be used to authenticate requests to the TV Labs Appium proxy, and will tie the session to your TV Labs account.
After generating an API key, set your web driver endpoint to https://appium.tvlabs.ai:4723
and add the API key to the Authorization
header as a Bearer token.
Example for NodeJS WebDriverIO script
const apiKey = "YOUR_API_KEY_HERE"
const webDriverOptions = {
hostname: "appium.tvlabs.ai",
port: 4723,
protocol: "https",
headers: {
Authorization: `Bearer ${apiKey}`
},
capabilities: { ... }
}
Capabilities
The TV Labs Appium proxy supports the following capabilities, in addition to the capabilities provided by Appium and driver specific capabilities.
The platformName
, appium:automationName
, and device connection capabilities are automatically added to the request based on the matched platform.
tvlabs:constraints
(required)
An API to explore available values for tvlabs:constraints
is coming soon.
The tvlabs:constraints
capability matches your test script to a device on the TV Labs platform. It accepts a map of key-value pairs with the following supported keys:
platform_key
: The platform key of the device (see available values in the Platforms section)make
: The device manufacturer (e.g.,Samsung
)device_type
: The device type (tv
,stb
, ormobile
)model
: The device model (e.g.,Express 4K+
)year
: The device model year (e.g.,2024
)minimum_chromedriver_major_version
: The minimum major version of the ChromeDriver.supports_chromedriver
: Whether the device supports ChromeDriver.
Example tvlabs:constraints
capability to match with any Roku device:
{ platform_key: "roku" }
tvlabs:build
(required)
The tvlabs:build
capability specifies a TV Labs build ID for sideloading onto the device before an Appium session starts. You can obtain a build ID using the tvlabs upload
command, which will upload your application to a secure blob storage:
❯ tvlabs upload -i ./path/to/application.zip
e6f2a4b1-8160-4147-8493-5e3aa18ddcc8
tvlabs:match_timeout
(optional)
The tvlabs:match_timeout
capability defines the maximum time (in seconds) to wait for a device matching your tvlabs:constraints
. If no device is found within this timeout, the session fails with a 400 response code. Set this value lower for fast failures or higher to allow more matching time. The default is 300 seconds (5 minutes).
tvlabs:device_timeout
(optional)
The tvlabs:device_timeout
capability defines the maximum time (in seconds) to wait for a matched device to become ready. If the device isn't ready within this timeout, the session fails with a 400 response code. The default is 60 seconds, which should be sufficient for most use cases.
Many webdriver clients have client-side configuration for timeouts, for example, webdriverio
provides a connectionRetryTimeout
configuration option that defaults to 2 minutes. If this value is set too low, the request may timeout before the TV Labs device has been matched to the request, or before the device has had time to warm up. To avoid this, configure your webdriver client request timeout to be greater than the sum of tvlabs:match_timeout
and tvlabs:device_timeout
capabilities.
tvlabs:session_id
(optional)
If a TV Labs session has already been created, the tvlabs:session_id
capability can be provided to skip TV Labs session creation on the WebDriver New Session
command. This is used by the @tvlabs/wdio-service
WebdriverIO service to provide a pre-created session, and is not normally provided by the end user.
Platforms
The following platforms are supported by the TV Labs Appium proxy:
Platform | platform_key | Appium Driver | Build Format |
---|---|---|---|
Roku | roku | appium-roku-driver | .zip |
WebOS (LG) | web_os | appium-lg-webos-driver | .ipk |
Tizen TV (Samsung) | tizen | appium-tizen-tv-driver | .wgt |
Android TV | android_tv | Coming Soon | .apk |
SmartCast (Vizio) | smartcast | Coming Soon | Coming Soon |
When uploading a build to be used with Appium via the tvlabs upload
command, the build will need to be in an expected format for the platform. Please view the documentation provided by each platform to see how to prepare the build.
On the Tizen platform, uploaded application packages will be re-signed using the TV Labs author and distributor certificates before sideloading. It is not necessary to sign the package before uploading.
ChromeDrivers
For Appium drivers that use ChromeDriver to perform automation on the device, the ChromeDriver will be automatically selected based on the version of Chromium browser that is running on the device. The version of ChromeDriver may not always be an exact match if there is not one available, but the closest compatible version will be selected.
Use the supports_chromedriver
and minimum_chromedriver_major_version
constraint capabilities to ensure that a device which supports your script is matched. For example, if you need to use the actions
API introduced in major version 76 of ChromeDriver, you can set the following capabilities:
{
"tvlabs:constraints": {
"supports_chromedriver": true,
"minimum_chromedriver_major_version": 76,
// ...
}
}
WebdriverIO Service
To better support the use case of running Appium tests on the TV Labs platform with the WebdriverIO framework, we have created the @tvlabs/wdio-service
WebdriverIO service package.
The @tvlabs/wdio-service
package uses a websocket to connect to the TV Labs platform before an Appium session begins, logging events relating to TV Labs session creation as they occur. This offloads the responsibility of creating the TV Labs session from the POST /session Webdriver endpoint, leading to more reliable session requests and creation.
To use @tvlabs/wdio-service
, install the package with npm install @tvlabs/wdio-service
or your favorite package manager, and add it to your WebdriverIO test runner configuration.
// wdio.conf.js
export const config = {
// ...
services: [
[TVLabsService, { apiKey: process.env.TVLABS_API_KEY }]
],
// ...
}
The service can be used with the remote API as well, see the package README and example below for more information.
Once the service is installed, you can observe logs in the test runner output relating to the TV Labs session creation process before your Appium test script begins.
[0-0] 2025-03-28T17:15:44.459Z INFO wdio-tvlabs-service: Requesting TV Labs session
[0-0] 2025-03-28T17:15:45.461Z INFO wdio-tvlabs-service: Received session request ID: 534465. Waiting for a match...
[0-0] 2025-03-28T17:15:45.872Z INFO wdio-tvlabs-service: Session request 534465 matching...
[0-0] 2025-03-28T17:15:45.873Z INFO wdio-tvlabs-service: Session request 534465 filled: https://tvlabs.ai/app/sessions/b936e307-8280-4949-8d13-ac0da83b1f5e
[0-0] 2025-03-28T17:15:45.874Z INFO wdio-tvlabs-service: Waiting for device to be ready...
[0-0] 2025-03-28T17:15:52.301Z INFO wdio-tvlabs-service: Session b936e307-8280-4949-8d13-ac0da83b1f5e ready!
Example Remote Scripts
Roku
Below is a complete NodeJS script demonstrating the TV Labs Appium proxy. To run this script, first set up your environment:
mkdir tvlabs-appium-roku-example && cd tvlabs-appium-roku-example
npm init -y
npm install --save webdriverio
touch roku-appium.js
echo "TVLABS_API_KEY=<tvlabs_api_key>" >> .env
echo "TVLABS_BUILD_ID=$(tvlabs upload -i ./path/to/application.zip)" >> .env
Copy and paste the following script into the roku-appium.js
file, then run it with node --env-file=.env roku-appium.js
:
import { remote } from "webdriverio";
import fs from "fs";
const capabilities = {
"tvlabs:build": process.env.TVLABS_BUILD_ID,
"tvlabs:constraints": {
"platform_key": "roku",
},
};
const wdOpts = {
hostname: process.env.APPIUM_HOST || "appium.tvlabs.ai",
port: parseInt(process.env.APPIUM_PORT, 10) || 4723,
protocol: "https",
logLevel: "info",
capabilities,
headers: {
"Authorization": `Bearer ${process.env.TVLABS_API_KEY}`
},
connectionRetryTimeout: 300000
};
async function runTest() {
const driver = await remote(wdOpts);
try {
await driver.executeScript("roku: deviceInfo", []);
await driver.executeScript("roku: pressKey", [{ key: "Right" }]);
await driver.pause(1000);
await driver.executeScript("roku: pressKey", [{ key: "Left" }]);
const screenshot = await driver.takeScreenshot();
fs.writeFileSync("./screenshot.png", screenshot, { encoding: "base64" });
} finally {
await driver.pause(1000);
await driver.deleteSession();
}
}
runTest();
WebOS (LG)
Below is a complete NodeJS script demonstrating the TV Labs Appium proxy. To run this script, first set up your environment:
mkdir tvlabs-appium-webos-example && cd tvlabs-appium-webos-example
npm init -y
npm install --save webdriverio
touch webos-appium.js
echo "TVLABS_API_KEY=<tvlabs_api_key>" >> .env
echo "TVLABS_BUILD_ID=$(tvlabs upload -i ./path/to/application.ipk)" >> .env
# found in the appinfo.json file for your application.
echo "APP_ID=<app_id>" >> .env
When using the WebOS driver, the appium:appId
capability is required for the driver to function properly.
Copy and paste the following script into the webos-appium.js
file, then run it with node --env-file=.env webos-appium.js
:
import { remote } from "webdriverio";
const capabilities = {
"tvlabs:constraints": {
platform_key: "web_os"
},
"tvlabs:build": process.env.TVLABS_BUILD_ID,
"appium:appId": process.env.APP_ID,
};
const wdOpts = {
hostname: process.env.APPIUM_HOST || "appium.tvlabs.ai",
port: parseInt(process.env.APPIUM_PORT, 10) || 4723,
protocol: "https",
logLevel: "info",
capabilities,
headers: {
"Authorization": `Bearer ${process.env.TVLABS_API_KEY}`
},
connectionRetryTimeout: 300000
};
async function runTest() {
const driver = await remote(wdOpts);
try {
await driver.executeScript("webos: pressKey", [{ key: "right" }]);
await driver.pause(1000);
await driver.executeScript("webos: pressKey", [{ key: "left" }]);
} finally {
await driver.pause(1000);
await driver.deleteSession();
}
}
runTest();
Tizen TV (Samsung)
Below is a complete NodeJS script demonstrating the TV Labs Appium proxy. To run this script, first set up your environment:
mkdir tvlabs-appium-tizen-example && cd tvlabs-appium-tizen-example
npm init -y
npm install --save webdriverio
touch tizen-appium.js
echo "TVLABS_API_KEY=<tvlabs_api_key>" >> .env
echo "TVLABS_BUILD_ID=$(tvlabs upload -i ./path/to/application.wgt)" >> .env
# found in the config.xml file for your application.
echo "APP_ID=<app_id>" >> .env
When using the Tizen TV driver, the appium:appPackage
capability is required for the driver to function properly.
Copy and paste the following script into the tizen-appium.js
file, then run it with node --env-file=.env tizen-appium.js
:
import { remote } from "webdriverio";
const capabilities = {
"tvlabs:constraints": {
platform_key: "tizen"
},
"tvlabs:build": process.env.TVLABS_BUILD_ID,
"appium:appPackage": process.env.APP_ID,
};
const wdOpts = {
hostname: process.env.APPIUM_HOST || "appium.tvlabs.ai",
port: parseInt(process.env.APPIUM_PORT, 10) || 4723,
protocol: "https",
logLevel: "info",
capabilities,
headers: {
"Authorization": `Bearer ${process.env.TVLABS_API_KEY}`
},
connectionRetryTimeout: 300000
};
async function runTest() {
const driver = await remote(wdOpts);
try {
await driver.executeScript("tizen: pressKey", [{ key: "KEY_RIGHT" }]);
await driver.pause(1000);
await driver.executeScript("tizen: pressKey", [{ key: "KEY_LEFT" }]);
} finally {
await driver.pause(1000);
await driver.deleteSession();
}
}
runTest();
With @tvlabs/wdio-service
Beginning with the Roku example above, run the following command to install the WebdriverIO service:
npm install --save @tvlabs/wdio-service
Then modify the script to use the service:
import { remote } from "webdriverio";
import TVLabsService from "@tvlabs/wdio-service";
import fs from "fs";
const capabilities = {
"tvlabs:build": process.env.TVLABS_BUILD_ID,
"tvlabs:constraints": {
"platform_key": "roku",
},
};
const wdOpts = {
hostname: process.env.APPIUM_HOST || "appium.tvlabs.ai",
port: parseInt(process.env.APPIUM_PORT, 10) || 4723,
protocol: "https",
logLevel: "info",
capabilities,
headers: {
"Authorization": `Bearer ${process.env.TVLABS_API_KEY}`
},
connectionRetryTimeout: 300000
};
const serviceOpts = {
apiKey: process.env.TVLABS_API_KEY,
}
async function runTest() {
// Unused here, but usually provided by the WebdriverIO test runner.
const wdioOptions = {};
const specs = [];
const cid = "";
const service = new TVLabsService(serviceOpts, capabilities, wdioOptions);
// This modifies capabilities, adding the TV Labs session ID.
await service.beforeSession(wdOpts, capabilities, [], "");
// Proceed normally!
const driver = await remote(wdOpts);
try {
await driver.executeScript("roku: deviceInfo", []);
await driver.executeScript("roku: pressKey", [{ key: "Right" }]);
await driver.pause(1000);
await driver.executeScript("roku: pressKey", [{ key: "Left" }]);
const screenshot = await driver.takeScreenshot();
fs.writeFileSync("./screenshot.png", screenshot, { encoding: "base64" });
} finally {
await driver.pause(1000);
await driver.deleteSession();
}
}
runTest();