Skip to main content

Overview

The TestDriver client is the main entry point for the SDK. It manages authentication, sandbox connections, and provides access to all testing methods — element finding, actions, assertions, and provisioning.

Constructor

import TestDriver from 'testdriverai';

const testdriver = new TestDriver(options?)
// or with explicit API key
const testdriver = new TestDriver(apiKey, options?)
apiKey
string
Your TestDriver API key. If omitted, loaded automatically from the TD_API_KEY environment variable or .env file.
options
TestDriverOptions
Configuration options for the client.

Examples

import TestDriver from 'testdriverai';

// Minimal — API key from .env
const testdriver = new TestDriver();

// Specify OS and resolution
const testdriver = new TestDriver({
  os: 'windows',
  resolution: '1920x1080',
});

// With explicit API key
const testdriver = new TestDriver('your-api-key', {
  os: 'windows',
});

// With AI config and caching
const testdriver = new TestDriver({
  ai: { temperature: 0, top: { p: 0.9, k: 40 } },
  cache: {
    thresholds: {
      find: { screen: 0.03, element: 0.85 },
      assert: 0.03,
    },
  },
});

// Self-hosted
const testdriver = new TestDriver({
  ip: '192.168.1.100',
  apiRoot: 'https://my-testdriver.internal.com',
});

Lifecycle Methods

auth()

Authenticate with the TestDriver API. Exchanges your API key for a session token.
await testdriver.auth()
Returns: Promise<void>
You must call auth() before connect(). When using the Vitest hook or presets, this is handled automatically.

connect()

Connect to a sandbox environment. Creates or reconnects to a virtual machine where tests run.
await testdriver.connect(options?)
options
ConnectOptions
Connection options that override constructor settings.
Returns: Promise<Object> — Sandbox instance details including instanceId, ip, vncPort.
// Basic connection
await testdriver.connect();

// Reconnect to existing sandbox
await testdriver.connect({ sandboxId: 'existing-sandbox-id-123' });

// Self-hosted
await testdriver.connect({ ip: '192.168.1.100' });

ready()

Convenience method that calls auth() + connect() in sequence.
await testdriver.ready()
Returns: Promise<void>
const testdriver = new TestDriver({ os: 'linux' });
await testdriver.ready(); // Authenticates and connects

disconnect()

Disconnect from the sandbox and clean up resources.
await testdriver.disconnect()
Returns: Promise<void>
afterAll(async () => {
  await testdriver.disconnect();
});
Always call disconnect() in cleanup hooks (afterAll, finally blocks). This prevents orphaned sandboxes from consuming resources.

Instance Information

getInstance()

Get the current sandbox instance details.
const instance = testdriver.getInstance()
Returns: Object | null — Sandbox instance info (instanceId, ip, etc.)

getSessionId()

Get the current session ID for tracking and debugging.
const sessionId = testdriver.getSessionId()
Returns: string | null

Properties

PropertyTypeDescription
connectedbooleanWhether the SDK is connected to a sandbox
osstringTarget operating system ('windows' or 'linux')
resolutionstringScreen resolution (e.g., '1366x768')
provisionProvisionAPIProvision API for launching apps
dashcamDashcamLazy-initialized Dashcam instance
dashcamEnabledbooleanWhether Dashcam recording is enabled
autoScreenshotsbooleanWhether auto-screenshots are enabled
optionsobjectResolved configuration options
emitterEventEmitter2Event emitter for custom event handling
loggingEnabledbooleanWhether console logging is enabled

Static Properties

PropertyTypeDescription
TestDriver.versionstringSDK version string

Logging & Events

setLogging()

Toggle console logging at runtime.
testdriver.setLogging(enabled)
enabled
boolean
required
Whether to enable logging.
testdriver.setLogging(false);
await testdriver.disconnect();
testdriver.setLogging(true);

getEmitter()

Get the EventEmitter2 instance for custom event handling.
const emitter = testdriver.getEmitter()
Returns: EventEmitter2 — Supports wildcard events with : delimiter.
const emitter = testdriver.getEmitter();

emitter.on('command:start', (data) => {
  console.log('Command started:', data);
});

emitter.on('command:success', (data) => {
  console.log('Command succeeded:', data);
});

emitter.on('command:error', (error) => {
  console.error('Command failed:', error);
});
See Events for the full list of event namespaces.

getLogs() / clearLogs()

Access and manage the internal log buffer. Logs are stored in JSONL format for S3 upload.
const logData = testdriver.getLogs()    // string (JSONL)
testdriver.clearLogs()

Test Run Tracking

createTestRun()

Create a new test run in the TestDriver API.
const run = await testdriver.createTestRun(options)
Returns: Promise<object> — Test run metadata.

completeTestRun()

Mark a test run as complete.
await testdriver.completeTestRun(options)

recordTestCase()

Record an individual test case result.
await testdriver.recordTestCase(options)

setTestContext()

Set the Vitest test context for automatic test metadata tracking.
testdriver.setTestContext(context)
context
object
required
Vitest task context (context.task).

Unawaited Promise Detection

The SDK tracks the last returned promise. If a new command is called before the previous promise resolves, it logs a warning:
⚠️ Previous TestDriver command was not awaited!
Always await every TestDriver command to prevent race conditions:
// ❌ Missing await — will trigger warning
testdriver.find('button').click();
testdriver.type('hello');

// ✅ Correct
await testdriver.find('button').click();
await testdriver.type('hello');

Types

type PreviewMode = "browser" | "ide" | "none";

interface TestDriverOptions {
  apiKey?: string;
  apiRoot?: string;
  os?: "windows" | "linux";
  resolution?: string;
  analytics?: boolean;
  logging?: boolean;
  autoScreenshots?: boolean;
  newSandbox?: boolean;
  reconnect?: boolean;
  keepAlive?: number;
  preview?: PreviewMode;
  headless?: boolean;             // @deprecated → preview: "none"
  debugOnFailure?: boolean;
  ip?: string;
  sandboxAmi?: string;
  sandboxInstance?: string;
  dashcam?: boolean;
  cache?: boolean | CacheConfig;
  cacheKey?: string;              // @deprecated → cache.cacheKey
  redraw?: boolean | RedrawConfig;
  environment?: Record<string, any>;
  ai?: AIConfig;
}

interface CacheConfig {
  enabled?: boolean;
  cacheKey?: string;
  thresholds?: {
    find?: {
      screen?: number;            // Default: 0.05
      element?: number;           // Default: 0.8
    };
    assert?: number;              // Default: 0.05
  };
}

interface RedrawConfig {
  enabled?: boolean;
  thresholds?: {
    screen?: number | false;      // Default: 0.05
    network?: boolean;            // Default: false
  };
}

interface AIConfig {
  model?: string;
  provider?: string;
  apiKey?: string;
  baseURL?: string;
  thinking?: boolean;
  temperature?: number;
  top?: {
    p?: number;
    k?: number;
  };
}

interface ConnectOptions {
  sandboxId?: string;
  newSandbox?: boolean;
  reconnect?: boolean;
  ip?: string;
  os?: "windows" | "linux";
  sandboxAmi?: string;
  sandboxInstance?: string;
  preview?: PreviewMode;
  reuseConnection?: boolean;      // Default: true
  keepAlive?: number;             // Default: 60000
}

Environment Variables

The SDK reads these environment variables (also from .env files):
VariableDescriptionDefault
TD_API_KEYAPI key for authentication
TD_API_ROOTAPI endpoint URLhttps://testdriver-api.onrender.com
TD_OSDefault OSlinux

Complete Example

import { beforeAll, afterAll, describe, it } from 'vitest';
import TestDriver from 'testdriverai';

describe('My Test Suite', () => {
  let testdriver;

  beforeAll(async () => {
    testdriver = new TestDriver({
      os: 'windows',
      resolution: '1366x768',
      logging: true,
      cache: {
        thresholds: {
          find: { screen: 0.05, element: 0.8 },
          assert: 0.05,
        },
      },
    });
    
    const emitter = testdriver.getEmitter();
    emitter.on('log:info', (msg) => console.log('[INFO]', msg));
    
    await testdriver.auth();
    const instance = await testdriver.connect();
    console.log('Connected:', instance.instanceId);
  });

  afterAll(async () => {
    await testdriver.disconnect();
  });

  it('runs a test', async () => {
    await testdriver.provision.chrome({ url: 'https://example.com' });
    await testdriver.find('Get Started button').click();
  });
});

Best Practices

Use beforeAll/afterAll to create one sandbox per test suite. This significantly reduces execution time vs creating a sandbox per test.
Wrap connect() in try-catch to handle network issues or quota limits:
try {
  await testdriver.connect();
} catch (error) {
  console.error('Connection failed:', error.message);
  throw error;
}
Use afterAll or try-finally blocks to ensure disconnect() is called even if tests fail:
afterAll(async () => {
  await testdriver.disconnect();
});
Never hardcode API keys:
.env
TD_API_KEY=your_api_key_here
// API key loaded automatically
const testdriver = new TestDriver();
When a test fails and you need to inspect the sandbox interactively:
const testdriver = new TestDriver({
  debugOnFailure: true
});
The sandbox stays alive for 5 minutes after failure, and connection instructions are logged.