GitHub - testingbot/testingbot-api: NodeJS module to communicate with the TestingBot API
Official NodeJS client for the TestingBot REST API.
TestingBot provides a cloud-based test infrastructure for automated cross-browser testing. This client library allows you to interact with the TestingBot API to manage tests, retrieve browser information, handle test artifacts, and more.
Table of Contents
- Features
- Requirements
- Installation
- Quick Start
- API Reference
- Responses & Pagination
- CLI Usage
- Complete Examples
- Testing
- Contributing
- License
- Support
- More Documentation
Features
Core Features
- Full support for all TestingBot REST API endpoints
- Both callback and async/await patterns supported
- Manage browser sessions and tests
- Upload and manage test artifacts
- Team management capabilities
- Screenshot generation across multiple browsers
- Tunnel management for local testing
- Codeless test management (create, schedule, trigger, alerts, reports)
- Built-in workflow helpers (auto-pagination, polling, batch operations, statistics)
- Typed errors (
AuthenticationError,RateLimitError,ValidationError, …)
Requirements
- NodeJS >= 18.0.0
- A TestingBot account with API credentials
Installation
npm install testingbot-api
Quick Start
1. Get your credentials
Sign up for a TestingBot account and obtain your API key and secret from the account settings.
2. Set up authentication
You can authenticate in three ways:
Environment variables (recommended)
export TESTINGBOT_KEY="your-api-key" export TESTINGBOT_SECRET="your-api-secret"
Constructor options
const tb = new TestingBot({ api_key: 'your-api-key', api_secret: 'your-api-secret' });
Configuration file
Create a ~/.testingbot file with your credentials
3. Initialize the client
const TestingBot = require('testingbot-api'); const tb = new TestingBot({ api_key: process.env.TESTINGBOT_KEY, api_secret: process.env.TESTINGBOT_SECRET });
Usage
All methods support both callback style and async/await patterns. When using async/await, simply omit the callback parameter.
List responses are paginated. Endpoints that return a list (
getTests,getBuilds,getStorageFiles,getScreenshotList,getCodelessTests,getUsersInTeam) resolve to a{ data: [...], meta: { offset, count, total } }object — read the array from.data. See Responses & Pagination. (getBrowsers,getDevicesandgetTunnelListreturn a plain array.)Errors are typed. A failed request rejects (or calls back) with a
TestingBotErrorsubclass carrying.statusCodeand.response. See Error Handling.
API Reference
Browser & Device Management
getBrowsers
Gets a list of browsers you can test on. Returns a plain array.
// Callback style tb.getBrowsers(type, function(error, browsers) {}); // Async/await style const browsers = await tb.getBrowsers(); // With optional type parameter ('webdriver' (default) or 'rc' for legacy Selenium RC) const rcBrowsers = await tb.getBrowsers('rc');
getDevices
Gets a list of physical mobile devices you can test on. Returns a plain array.
// Callback style tb.getDevices(function(error, devices) {}); // Async/await style const devices = await tb.getDevices(); // Filter by OS family: Android, iOS, REAL_ANDROID or REAL_IOS const androidDevices = await tb.getDevices({ platform: 'Android' });
getIpRanges
Gets the list of TestingBot IP ranges, useful for firewall allow-lists. No authentication required.
// Callback style tb.getIpRanges(function(error, ipRanges) {}); // Async/await style const ipRanges = await tb.getIpRanges();
getAvailableDevices
Gets a list of available physical mobile devices for your account
// Callback style tb.getAvailableDevices(function(error, availableDevices) {}); // Async/await style const availableDevices = await tb.getAvailableDevices();
getDevice
Gets details for a specific physical device
// Callback style tb.getDevice(deviceId, function(error, deviceDetails) {}); // Async/await style const deviceDetails = await tb.getDevice(deviceId);
Session Management
createSession
Creates a remote browser on TestingBot and returns its CDP URL, which can be used to interface with the remote browser.
const options = { capabilities: { browserName: 'chrome', browserVersion: 'latest', platform: 'WIN11' } }; // Callback style tb.createSession(options, function(error, data) {}); // Async/await style const session = await tb.createSession(options);
This will return a response with this structure:
{
"session_id": "cb8aaba38ddb-88fd98fca537-a0070d8f1815-175888519321-14646637",
"cdp_url": "wss://cloud.testingbot.com/session/cb8aaba38ddb-88fd98fca537-a0070d8f1815-175888519321-14646637"
}You can now connect to the cdp_url with a CDP client to instruct the remote browser.
User Management
getUserInfo
Gets your user information
// Callback style tb.getUserInfo(function(error, userInfo) {}); // Async/await style const userInfo = await tb.getUserInfo();
updateUserInfo
Updates your user information. Only first_name and last_name are mutable. Pass the fields
directly — they are wrapped in a user object for you.
// Callback style tb.updateUserInfo({ first_name: 'Jane', last_name: 'Doe' }, function(error, userInfo) {}); // Async/await style const userInfo = await tb.updateUserInfo({ first_name: 'Jane', last_name: 'Doe' });
getUserKeys
Returns the account's API key and secret ({ key, secret }).
// Callback style tb.getUserKeys(function(error, keys) {}); // Async/await style const keys = await tb.getUserKeys();
Test Management
getTests
Gets a list of your latest tests. Resolves to { data, meta } — the tests are in .data.
// Callback style tb.getTests(offset, limit, function(error, result) {}); // Async/await style const result = await tb.getTests(); console.log(result.data, result.meta); // With pagination const page = await tb.getTests(10, 20); // offset: 10, count: 20 // With filters (options form) — since (UNIX ts), group, build, browser_id, skip_fields const filtered = await tb.getTests({ offset: 0, count: 50, build: 'ci-42', skip_fields: 'logs' });
getTestDetails
Gets details for a single test, pass the WebDriver's SessionID
// Callback style tb.getTestDetails(sessionId, function(error, testDetails) {}); // Async/await style const testDetails = await tb.getTestDetails(sessionId); // Omit heavy fields from the response const lean = await tb.getTestDetails(sessionId, { skip_fields: 'logs,thumbs' });
updateTest
Updates a single test. For example, update the passed state of a test (whether it was successful or not).
const testData = { "test[success]" : "1", "test[status_message]" : "test" }; // Callback style tb.updateTest(testData, sessionId, function(error, testDetails) {}); // Async/await style const testDetails = await tb.updateTest(testData, sessionId); // Array values (e.g. multiple groups) are encoded correctly and all preserved await tb.updateTest({ groups: ['regression', 'smoke'] }, sessionId);
deleteTest
Deletes a single test, pass the WebDriver's SessionID
// Callback style tb.deleteTest(sessionId, function(error, success) {}); // Async/await style const success = await tb.deleteTest(sessionId);
stopTest
Stops a single test, pass the WebDriver's SessionID
// Callback style tb.stopTest(sessionId, function(error, success) {}); // Async/await style const success = await tb.stopTest(sessionId);
Tunnel Management
getTunnel
Gets the account's currently active tunnel (handy when you only run one).
// Callback style tb.getTunnel(function(error, tunnel) {}); // Async/await style const tunnel = await tb.getTunnel();
getTunnelList
Gets list of active tunnels. Returns a plain array.
// Callback style tb.getTunnelList(function(error, tunnelList) {}); // Async/await style const tunnelList = await tb.getTunnelList();
getTunnelById
Gets a single tunnel by its ID.
// Callback style tb.getTunnelById(tunnelId, function(error, tunnel) {}); // Async/await style const tunnel = await tb.getTunnelById(tunnelId);
deleteTunnel
Deletes a single Tunnel by ID
// Callback style tb.deleteTunnel(tunnelId, function(error, success) {}); // Async/await style const success = await tb.deleteTunnel(tunnelId);
deleteActiveTunnel
Tears down whichever tunnel is currently active, without needing an ID.
// Callback style tb.deleteActiveTunnel(function(error, success) {}); // Async/await style const success = await tb.deleteActiveTunnel();
Build Management
getBuilds
Retrieves the latest builds
// Callback style tb.getBuilds(offset, limit, function(error, builds) {}); // Async/await style const builds = await tb.getBuilds(); // With pagination const builds = await tb.getBuilds(10, 20); // offset: 10, limit: 20
getTestsForBuild
Retrieves the tests for a single build
// Callback style tb.getTestsForBuild(buildId, function(error, tests) {}); // Async/await style const tests = await tb.getTestsForBuild(buildId);
deleteBuild
Deletes a single build
// Callback style tb.deleteBuild(buildId, function(error, success) {}); // Async/await style const success = await tb.deleteBuild(buildId);
Storage Management
uploadFile
Uploads a local file to TestingBot Storage
// Callback style tb.uploadFile(localFilePath, function(error, appUrl) {}); // Async/await style const appUrl = await tb.uploadFile(localFilePath);
uploadRemoteFile
Uploads a remote file to TestingBot Storage
// Callback style tb.uploadRemoteFile(remoteFileUrl, function(error, appUrl) {}); // Async/await style const appUrl = await tb.uploadRemoteFile(remoteFileUrl);
getStorageFile
Retrieve data from a previously uploaded file
// Callback style tb.getStorageFile(appUrl, function(error, fileDetails) {}); // Async/await style const fileDetails = await tb.getStorageFile(appUrl);
getStorageFiles
Retrieve list of previously uploaded files
// Callback style tb.getStorageFiles(offset, limit, function(error, fileDetails) {}); // Async/await style const fileDetails = await tb.getStorageFiles(); // With pagination const fileDetails = await tb.getStorageFiles(10, 20); // offset: 10, limit: 20
deleteStorageFile
Delete a previously uploaded file
// Callback style tb.deleteStorageFile(appUrl, function(error, success) {}); // Async/await style const success = await tb.deleteStorageFile(appUrl);
getAuthenticationHashForSharing
Calculates the authentication hash for sharing, pass the WebDriver's SessionID. This is used to share a test's detail page on TestingBot
// This method is synchronous and returns the hash directly const hash = tb.getAuthenticationHashForSharing(sessionId);
Screenshots
takeScreenshot
Takes screenshots for the specific browsers
// Callback style — note the parameter order: url, browsers, resolution, waitTime, fullPage, callbackURL tb.takeScreenshot(url, browsers, resolution, waitTime, fullPage, callbackURL, function(error, screenshots) {}); // Async/await style const screenshots = await tb.takeScreenshot( 'https://example.com', // url - required [{ browserName: 'chrome', version: 'latest', os: 'WIN11' }], // browsers - required '1920x1080', // resolution - required 5, // waitTime (seconds) - optional true, // fullPage - optional 'https://your-callback.com' // callbackURL - optional );
Once a screenshot job is running, you can use retrieveScreenshots to poll for the results.
retrieveScreenshots
Retrieves screenshots for a specific takeScreenshot call
// Callback style tb.retrieveScreenshots(screenshotId, function(error, screenshots) {}); // Async/await style const screenshots = await tb.retrieveScreenshots(screenshotId); // Delta-fetch: exclude screenshot IDs you already have const delta = await tb.retrieveScreenshots(screenshotId, { excludeIds: '101,102' });
getScreenshotList
Retrieves all screenshots previously generated with your account
// Callback style tb.getScreenshotList(offset, limit, function(error, screenshots) {}); // Async/await style const screenshots = await tb.getScreenshotList(); // With pagination const screenshots = await tb.getScreenshotList(10, 20); // offset: 10, limit: 20
Team Management
getTeam
Retrieves team settings
// Callback style tb.getTeam(function(error, data) {}); // Async/await style const teamInfo = await tb.getTeam();
getUsersInTeam
Get all users in your team
// Callback style tb.getUsersInTeam(function(error, users) {}); // Async/await style const users = await tb.getUsersInTeam();
getUserFromTeam
Retrieves information about a specific user in your team
// Callback style tb.getUserFromTeam(userId, function(error, user) {}); // Async/await style const user = await tb.getUserFromTeam(userId);
createUserInTeam
Add a user to your team. You need ADMIN rights for this. Pass the fields at the top level;
email and password are required.
const userData = { email: 'john@example.com', password: 'a-strong-password', first_name: 'John', last_name: 'Doe' }; // Callback style tb.createUserInTeam(userData, function(error, result) {}); // Async/await style const result = await tb.createUserInTeam(userData);
updateUserInTeam
Update a user in your team. You need ADMIN rights for this. Pass the fields at the top level
(first_name, last_name, email, password, credits, device_credits, concurrency,
concurrencyPhysical).
const userData = { first_name: 'Jane', last_name: 'Smith' }; // Callback style tb.updateUserInTeam(userId, userData, function(error, result) {}); // Async/await style const result = await tb.updateUserInTeam(userId, userData);
getUserClientKey
Gets a sub-account's client key ({ client_key }). You need ADMIN rights for this.
// Callback style tb.getUserClientKey(userId, function(error, result) {}); // Async/await style const result = await tb.getUserClientKey(userId);
resetCredentials
Resets credentials for a specific user in your team. You need ADMIN rights for this.
// Callback style tb.resetCredentials(userId, function(error, result) {}); // Async/await style const result = await tb.resetCredentials(userId);
Codeless Tests
Codeless tests allow you to create automated tests without writing code. These tests can be configured to run on a schedule and include AI-powered test generation.
getCodelessTests
Retrieves a list of codeless tests
// Callback style tb.getCodelessTests(offset, limit, function(error, tests) {}); // Async/await style const tests = await tb.getCodelessTests(0, 10); // offset: 0, limit: 10
createCodelessTest
Creates a new codeless test
const testData = { name: 'My Codeless Test', // Required: Test name url: 'https://example.com', // Required: URL to test cron: '0 0 * * *', // Optional: Cron schedule screenshot: true, // Optional: Take screenshots video: false, // Optional: Record video idletimeout: 60, // Optional: Idle timeout in seconds screenresolution: '1920x1080', // Optional: Screen resolution ai_prompt: 'Test the login flow' // Optional: AI test agent prompt }; // Callback style tb.createCodelessTest(testData, function(error, result) {}); // Async/await style const result = await tb.createCodelessTest(testData);
updateCodelessTest
Updates an existing codeless test
const updateData = { test: { name: 'Updated Test Name', cron: '0 12 * * *' } }; // Callback style tb.updateCodelessTest(updateData, testId, function(error, result) {}); // Async/await style const result = await tb.updateCodelessTest(updateData, testId);
getCodelessTest
Gets a single codeless test by ID
const test = await tb.getCodelessTest(testId);
deleteCodelessTest
Deletes a codeless test
// Callback style tb.deleteCodelessTest(testId, function(error, result) {}); // Async/await style const result = await tb.deleteCodelessTest(testId);
Running & scheduling
await tb.triggerCodelessTest(testId); // run now await tb.triggerAllCodelessTests(); // run every codeless test await tb.stopCodelessTest(testId); // stop a running test // Schedule: once / daily / weekly preset, or a raw cron expression await tb.scheduleCodelessTest(testId, { type: 'daily', hour: '09:00' }); await tb.scheduleCodelessTest(testId, { type: 'custom', cronFormat: '0 9 * * 1' });
Steps & browsers
const steps = await tb.getCodelessSteps(testId); await tb.addCodelessStep(testId, { /* step definition */ }); const browsers = await tb.getCodelessBrowsers(testId); await tb.setCodelessBrowsers(testId, { /* browser selection */ });
Alerts & reports
await tb.createCodelessAlert(testId, { /* alert config */ }); await tb.updateCodelessAlert(testId, { /* alert config */ }); await tb.createCodelessReport(testId, { /* report config */ }); await tb.updateCodelessReport(testId, { /* report config */ });
Workflow Helpers
Promise-based conveniences built on the methods above. These always return promises.
// Fetch every test, paging automatically (returns a flat array) const allTests = await tb.getAllTests(); // Poll a test until it reaches a terminal state (or the timeout elapses) const finished = await tb.waitForTestCompletion(sessionId, 300000, 5000); // Create a session and wait for it to finish const result = await tb.runTestAndWait({ capabilities: { browserName: 'chrome' } }); // Fetch details for many tests at once const { results, errors } = await tb.batchGetTestDetails([id1, id2, id3]); // Aggregate pass/fail/state and browser/platform counts over the last N days const stats = await tb.getTestStatistics(7); // Bulk uploads / screenshots const uploads = await tb.uploadFiles(['/path/a.apk', '/path/b.ipa']); const shots = await tb.takeMultipleScreenshots( ['https://example.com', 'https://example.com/pricing'], [{ browserName: 'chrome', version: 'latest', os: 'WIN11' }], '1920x1080', { waitTime: 3, fullPage: true } ); // Cleanup helpers (destructive) await tb.cleanupOldTests(30); // delete tests older than 30 days await tb.smartCleanup({ keepDays: 30, keepFailed: true, keepMax: 1000 });
Error Handling
Every failed request rejects (or calls back) with a typed error that extends TestingBotError
and carries .statusCode and .response (the raw response body). The classes are exported from
the package:
const TestingBot = require('testingbot-api'); const { TestingBotError, AuthenticationError, RateLimitError, ValidationError, NotFoundError, NetworkError } = TestingBot; try { await tb.getTestDetails('does-not-exist'); } catch (error) { if (error instanceof AuthenticationError) { console.error('Bad credentials'); } else if (error instanceof RateLimitError) { console.error('Slow down, retry after', error.retryAfter); } else { console.error(error.name, error.statusCode, error.message); } }
| Status | Error class |
|---|---|
| 400 | ValidationError (has .fields) |
| 401 | AuthenticationError |
| 429 | RateLimitError (has .retryAfter) |
| other non-2xx | TestingBotError |
| no response (transport failure) | NetworkError |
Responses & Pagination
List endpoints return a { data, meta } envelope rather than a bare array:
const result = await tb.getTests(0, 20); // { // data: [ /* tests */ ], // meta: { offset: 0, count: 20, total: 1234 } // } result.data.forEach(test => console.log(test.id, test.state));
This applies to getTests, getBuilds, getStorageFiles, getScreenshotList, getCodelessTests
and getUsersInTeam. The getAllTests() workflow helper unwraps and concatenates these pages for
you. getBrowsers, getDevices and getTunnelList return a plain array.
CLI Usage
The TestingBot API package includes a command-line interface for quick access to API functionality.
Installation
# Install globally for CLI access npm install -g testingbot-api # Or use npx with local installation npx testingbot <command>
Authentication
The CLI uses the same authentication methods as the API client:
- Environment variables:
TB_KEYandTB_SECRETorTESTINGBOT_KEYandTESTINGBOT_SECRET - Configuration file:
~/.testingbotwithapi_keyandapi_secret
Available Commands
# User management testingbot user info # Get user information testingbot user keys # Get your API key and secret testingbot user update <json> # Update user information # Test management testingbot tests list [offset] [limit] # List tests testingbot tests get <id> # Get test details testingbot tests delete <id> # Delete a test testingbot tests stop <id> # Stop a running test # Codeless tests (Lab) testingbot lab list [offset] [limit] # List codeless tests testingbot lab get <id> # Get a codeless test testingbot lab create <json> # Create a codeless test testingbot lab update <id> <json> # Update a codeless test testingbot lab delete <id> # Delete a codeless test testingbot lab trigger <id> # Trigger a run testingbot lab trigger-all # Trigger all codeless tests testingbot lab stop <id> # Stop a running test testingbot lab schedule <id> <json> # Set a schedule testingbot lab steps <id> # List steps testingbot lab add-step <id> <json> # Add a step testingbot lab browsers <id> # List browsers testingbot lab set-browsers <id> <json> # Set browsers testingbot lab alert <id> <json> # Create an alert testingbot lab update-alert <id> <json> # Update an alert testingbot lab report <id> <json> # Create a report testingbot lab update-report <id> <json> # Update a report # Browsers and devices testingbot browsers list [type] # List browsers (type: webdriver|rc) testingbot devices list [platform] # List devices (platform: Android|iOS|REAL_ANDROID|REAL_IOS) testingbot devices available # List available devices testingbot devices get <id> # Get a device testingbot config ip-ranges # List TestingBot IP ranges # Tunnels testingbot tunnel info # Get the active tunnel testingbot tunnel list # List tunnels testingbot tunnel get <id> # Get a tunnel by ID testingbot tunnel delete <id> # Delete a tunnel testingbot tunnel delete-active # Delete the active tunnel # Team testingbot team info # Get team info testingbot team users # List team users testingbot team get-user <id> # Get a team user testingbot team client-key <id> # Get a user's client key testingbot team create-user <json> # Create a team user # Storage testingbot storage upload <file> # Upload a file testingbot storage list [offset] [limit] # List stored files testingbot storage get <id> # Get a stored file testingbot storage delete <id> # Delete a stored file # Help testingbot --help # Show help testingbot --version # Show version
CLI Examples
# Create a codeless test testingbot lab create '{ "name": "Homepage Test", "url": "https://example.com", "cron": "0 0 * * *", "screenshot": true, "ai_prompt": "Test the homepage loads correctly" }' # List recent tests testingbot tests list 0 20 # Get browser list testingbot browsers list webdriver # Upload a file to storage testingbot storage upload ./test-app.zip
Complete Examples
Basic Usage
const TestingBot = require('testingbot-api'); const tb = new TestingBot({ api_key: "your-tb-key", api_secret: "your-tb-secret" }); async function runTests() { try { // Get user information const userInfo = await tb.getUserInfo(); console.log('User:', userInfo); // Get available browsers (plain array) const browsers = await tb.getBrowsers(); console.log('Available browsers:', browsers.length); // Get recent tests (paginated: { data, meta }) const tests = await tb.getTests(0, 10); console.log('Recent tests:', tests.data.length, 'of', tests.meta.total); // Get test details for the first test if (tests.data.length > 0) { const testDetails = await tb.getTestDetails(tests.data[0].id); console.log('Test details:', testDetails); } // Upload a file const appUrl = await tb.uploadFile('/path/to/your/app.apk'); console.log('Uploaded file:', appUrl); // Take screenshots const screenshots = await tb.takeScreenshot( 'https://testingbot.com', [{ browserName: 'chrome', version: 'latest', os: 'WIN11' }], '1280x1024' ); console.log('Screenshots:', screenshots); } catch (error) { console.error('Error:', error); } } runTests();
Testing
Run lint + the full test suite:
Most suites are integration tests that hit the live API and require valid TestingBot credentials set
as environment variables (TB_KEY and TB_SECRET). A subset are pure unit tests that need no
credentials (typed-error mapping, parameter validation, request shaping, and workflow helpers):
npx mocha --grep "Typed errors|Parameter validation|Request shaping|Workflow helpers"Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
See the LICENSE file for details.
Support
- Documentation: TestingBot REST API
- Issues: GitHub Issues
More documentation
Check out the TestingBot REST API for more information.