Overview
BrowserTest SDK provides comprehensive TypeScript support with full type definitions, IntelliSense support, and type safety for all operations.Installation
Copy
npm install browsertest-sdk
# TypeScript definitions are included automatically
Basic TypeScript Usage
Initialization with Types
Copy
import { BrowserTest } from 'browsertest-sdk';
const bt = new BrowserTest({
apiKey: process.env.BROWSERTEST_API_KEY!
});
Typed Operations
Copy
// Screenshots with full type safety
const screenshot = await bt.screenshot.take({
url: 'https://example.com',
fullPage: true,
format: 'png'
});
// Type: ScreenshotResult
// Agentic testing with structured output
const testResult = await bt.testing.execute({
instructions: 'Test login functionality',
url: 'https://example.com/login',
outputSchema: {
type: 'object',
properties: {
loginSuccessful: { type: 'boolean' },
userName: { type: 'string' }
}
}
});
// Type: TestResult
Type Definitions
Core Types
Copy
// Configuration
interface BrowserTestConfig {
apiKey: string;
baseUrl?: string;
timeout?: number;
retries?: number;
}
// Usage information
interface QuotaInfo {
plan: string;
usage: {
screenshot: {
used: number;
limit: number;
remaining: number;
};
agentic: {
used: number;
limit: number;
remaining: number;
};
total: {
screenshot: number;
agentic: number;
};
};
}
Screenshot Types
Copy
interface ScreenshotOptions {
url: string;
fullPage?: boolean;
width?: number;
height?: number;
format?: 'png' | 'jpeg';
quality?: number;
waitFor?: number;
selector?: string | null;
timeout?: number;
blockAds?: boolean;
}
interface ScreenshotResult {
success: boolean;
data: {
screenshot: string;
url: string;
title: string;
width: number;
height: number;
format: string;
fullPage: boolean;
};
meta: {
timeMs: number;
size: number;
};
}
Testing Types
Copy
interface TestOptions {
instructions: string;
url: string;
outputSchema?: object;
config?: TestConfig;
}
interface TestResult {
success: boolean;
results: {
actions: Array<{
type: string;
description: string;
timestamp: string;
}>;
structuredData?: any;
output: string;
};
meta: {
timeMs: number;
url: string;
};
}
Advanced TypeScript Patterns
Generic Types for Structured Output
Copy
// Define your expected output types
interface LoginTestOutput {
loginSuccessful: boolean;
userName?: string;
errorMessage?: string;
redirectUrl?: string;
}
interface EcommerceTestOutput {
checkoutSuccessful: boolean;
orderNumber?: string;
totalAmount?: string;
errors: string[];
}
// Typed test functions
async function testLogin(url: string): Promise<LoginTestOutput> {
const result = await bt.testing.execute({
instructions: 'Test login with valid credentials',
url,
outputSchema: {
type: 'object',
properties: {
loginSuccessful: { type: 'boolean' },
userName: { type: 'string' },
errorMessage: { type: 'string' },
redirectUrl: { type: 'string' }
}
}
});
return result.results.structuredData as LoginTestOutput;
}
async function testCheckout(url: string): Promise<EcommerceTestOutput> {
const result = await bt.testing.execute({
instructions: 'Complete checkout process',
url,
outputSchema: {
type: 'object',
properties: {
checkoutSuccessful: { type: 'boolean' },
orderNumber: { type: 'string' },
totalAmount: { type: 'string' },
errors: { type: 'array', items: { type: 'string' } }
}
}
});
return result.results.structuredData as EcommerceTestOutput;
}
// Usage with type safety
const loginResult = await testLogin('https://example.com/login');
if (loginResult.loginSuccessful) {
console.log('Login successful for user:', loginResult.userName);
}
const checkoutResult = await testCheckout('https://shop.com/checkout');
if (checkoutResult.checkoutSuccessful) {
console.log('Order placed:', checkoutResult.orderNumber);
}
Utility Types
Copy
// Extract successful results only
type SuccessfulScreenshot = ScreenshotResult & { success: true };
// Create a type guard
function isSuccessfulScreenshot(result: ScreenshotResult): result is SuccessfulScreenshot {
return result.success;
}
// Usage
const results = await bt.screenshot.takeBatch({ urls: ['https://site1.com', 'https://site2.com'] });
const successfulResults = results.results.filter(isSuccessfulScreenshot);
// Type: SuccessfulScreenshot[]
successfulResults.forEach(result => {
// TypeScript knows result.success is true and data exists
console.log('Success:', result.data.url, result.data.width);
});
Template Types
Copy
interface TemplateConfig<TOutput = any> {
name: string;
description: string;
instructions: string;
outputSchema: object;
tags?: string[];
}
interface TemplateInstance<TOutput = any> {
id: string;
config: TemplateConfig<TOutput>;
}
// Typed template creation
function createTypedTemplate<TOutput>(
config: TemplateConfig<TOutput>
): Promise<TemplateInstance<TOutput>> {
return bt.template.create(config);
}
// Typed template invocation
async function invokeTypedTemplate<TOutput>(
templateId: string,
params: { url: string; [key: string]: any }
): Promise<TOutput> {
const result = await bt.template.invoke(templateId, params);
return result.results.structuredData as TOutput;
}
// Usage
interface UserRegistrationOutput {
registrationSuccessful: boolean;
userId: string;
emailVerified: boolean;
}
const registrationTemplate = await createTypedTemplate<UserRegistrationOutput>({
name: 'User Registration Test',
instructions: 'Test user registration flow',
outputSchema: {
type: 'object',
properties: {
registrationSuccessful: { type: 'boolean' },
userId: { type: 'string' },
emailVerified: { type: 'boolean' }
}
}
});
const registrationResult = await invokeTypedTemplate<UserRegistrationOutput>(
registrationTemplate.id,
{ url: 'https://app.com/register' }
);
// Type: UserRegistrationOutput
Error Handling with Types
Typed Error Classes
Copy
// Custom error types
class BrowserTestError extends Error {
constructor(message: string, public status?: number) {
super(message);
this.name = 'BrowserTestError';
}
}
class BrowserTestAuthError extends BrowserTestError {
constructor(message = 'Authentication failed') {
super(message);
this.name = 'BrowserTestAuthError';
}
}
class BrowserTestQuotaError extends BrowserTestError {
constructor(message = 'Quota exceeded', public quotaInfo?: QuotaInfo) {
super(message);
this.name = 'BrowserTestQuotaError';
}
}
// Type guard functions
function isBrowserTestAuthError(error: unknown): error is BrowserTestAuthError {
return error instanceof Error && error.name === 'BrowserTestAuthError';
}
function isBrowserTestQuotaError(error: unknown): error is BrowserTestQuotaError {
return error instanceof Error && error.name === 'BrowserTestQuotaError';
}
// Typed error handling
async function safeScreenshot(url: string): Promise<ScreenshotResult | null> {
try {
return await bt.screenshot.take({ url });
} catch (error) {
if (isBrowserTestAuthError(error)) {
console.error('Auth error:', error.message);
// Handle auth error
return null;
}
if (isBrowserTestQuotaError(error)) {
console.error('Quota exceeded:', error.quotaInfo);
// Handle quota error
return null;
}
throw error; // Re-throw unknown errors
}
}
Result Types with Discriminated Unions
Copy
// Result types
type OperationResult<TData> =
| { success: true; data: TData }
| { success: false; error: string; code?: string };
// Generic operation wrapper
async function executeOperation<TData>(
operation: () => Promise<TData>
): Promise<OperationResult<TData>> {
try {
const data = await operation();
return { success: true, data };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
code: error instanceof Error ? error.name : undefined
};
}
}
// Usage with type safety
const screenshotResult = await executeOperation(() =>
bt.screenshot.take({ url: 'https://example.com' })
);
if (screenshotResult.success) {
// TypeScript knows screenshotResult.data is ScreenshotResult
console.log('Screenshot size:', screenshotResult.data.meta.size);
} else {
// TypeScript knows screenshotResult.error exists
console.error('Screenshot failed:', screenshotResult.error);
}
Class-Based SDK Wrapper
Copy
class TypedBrowserTest {
constructor(private bt: BrowserTest) {}
async screenshot(options: ScreenshotOptions): Promise<ScreenshotResult> {
return this.bt.screenshot.take(options);
}
async test<TOutput = any>(
instructions: string,
url: string,
schema?: object
): Promise<TestResult & { structuredData: TOutput }> {
const result = await this.bt.testing.execute({
instructions,
url,
outputSchema: schema
});
return {
...result,
results: {
...result.results,
structuredData: result.results.structuredData as TOutput
}
};
}
async batchScreenshots(urls: string[]): Promise<ScreenshotResult[]> {
const batch = await this.bt.screenshot.takeBatch({ urls });
return batch.results.filter(r => r.success).map(r => r.data!);
}
async getUsage(): Promise<QuotaInfo> {
return this.bt.getUsage();
}
}
// Usage
const typedBt = new TypedBrowserTest(bt);
// Type-safe operations
const screenshot = await typedBt.screenshot({
url: 'https://example.com',
fullPage: true
});
interface LoginResult {
success: boolean;
username?: string;
}
const loginTest = await typedBt.test<LoginResult>(
'Test login functionality',
'https://example.com/login',
{
type: 'object',
properties: {
success: { type: 'boolean' },
username: { type: 'string' }
}
}
);
// TypeScript knows loginTest.results.structuredData is LoginResult
if (loginTest.results.structuredData.success) {
console.log('Logged in as:', loginTest.results.structuredData.username);
}
Configuration Types
Environment-Based Configuration
Copy
type Environment = 'development' | 'staging' | 'production';
interface EnvironmentConfig {
apiKey: string;
baseUrl: string;
timeout: number;
retries: number;
}
const environmentConfigs: Record<Environment, EnvironmentConfig> = {
development: {
apiKey: process.env.BROWSERTEST_DEV_KEY!,
baseUrl: 'https://api.browsertest.in',
timeout: 30000,
retries: 1
},
staging: {
apiKey: process.env.BROWSERTEST_STAGING_KEY!,
baseUrl: 'https://staging-api.browsertest.in',
timeout: 45000,
retries: 3
},
production: {
apiKey: process.env.BROWSERTEST_PROD_KEY!,
baseUrl: 'https://api.browsertest.in',
timeout: 60000,
retries: 5
}
};
function createBrowserTestForEnvironment(env: Environment): BrowserTest {
const config = environmentConfigs[env];
// Validate required environment variables
if (!config.apiKey) {
throw new Error(`Missing API key for environment: ${env}`);
}
return new BrowserTest(config);
}
// Usage
const env = (process.env.NODE_ENV as Environment) || 'development';
const bt = createBrowserTestForEnvironment(env);
Configuration Validation
Copy
import { z } from 'zod'; // Using zod for runtime validation
const BrowserTestConfigSchema = z.object({
apiKey: z.string().min(10, 'API key too short'),
baseUrl: z.string().url().optional(),
timeout: z.number().min(1000).max(300000).optional(),
retries: z.number().min(0).max(10).optional()
});
type ValidatedBrowserTestConfig = z.infer<typeof BrowserTestConfigSchema>;
function createValidatedBrowserTest(config: unknown): BrowserTest {
const validatedConfig = BrowserTestConfigSchema.parse(config);
return new BrowserTest(validatedConfig);
}
// Usage with runtime validation
try {
const config = {
apiKey: process.env.BROWSERTEST_API_KEY!,
timeout: 30000,
retries: 3
};
const bt = createValidatedBrowserTest(config);
// Config is validated at runtime
} catch (error) {
console.error('Invalid configuration:', error);
}
Utility Types
Extract and Transform Types
Copy
// Extract successful results
type SuccessfulResults<T> = T extends { success: true; data: infer D } ? D : never;
// Extract error types
type ErrorResults<T> = T extends { success: false; error: infer E } ? E : never;
// Transform batch results
type BatchResults<T> = Array<
| { success: true; data: T }
| { success: false; error: string }
>;
type SuccessfulBatchResults<T> = Extract<BatchResults<T>, { success: true }>['data'];
// Usage
type ScreenshotBatch = BatchResults<ScreenshotResult['data']>;
type SuccessfulScreenshots = SuccessfulBatchResults<ScreenshotResult['data']>;
API Response Types
Copy
// Generic API response wrapper
interface ApiResponse<TData = unknown, TError = string> {
success: boolean;
data?: TData;
error?: TError;
meta?: {
timeMs: number;
requestId?: string;
};
}
// Specific response types
type ScreenshotApiResponse = ApiResponse<ScreenshotResult['data']>;
type TestApiResponse = ApiResponse<TestResult>;
type UsageApiResponse = ApiResponse<QuotaInfo>;
// Generic API call wrapper
async function apiCall<TData>(
operation: () => Promise<TData>
): Promise<ApiResponse<TData>> {
const startTime = Date.now();
try {
const data = await operation();
return {
success: true,
data,
meta: {
timeMs: Date.now() - startTime
}
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
meta: {
timeMs: Date.now() - startTime
}
};
}
}
// Usage
const screenshotResponse = await apiCall(() =>
bt.screenshot.take({ url: 'https://example.com' })
);
if (screenshotResponse.success) {
// TypeScript knows screenshotResponse.data is ScreenshotResult
console.log('Screenshot taken:', screenshotResponse.data?.meta.size);
}
Best Practices
Type Safety
- Always define interfaces for structured output schemas
- Use strict null checks with TypeScript’s
strictNullChecks - Leverage discriminated unions for result types
- Create type guards for error handling
Development Experience
Copy
// Enable strict TypeScript settings in tsconfig.json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}
IntelliSense and Auto-completion
Copy
// Use JSDoc comments for better IntelliSense
/**
* Takes a screenshot of the specified URL with optional configuration
* @param url - The URL to screenshot
* @param options - Screenshot configuration options
* @returns Promise resolving to screenshot result
*/
async function takeScreenshot(
url: string,
options: Partial<ScreenshotOptions> = {}
): Promise<ScreenshotResult> {
return bt.screenshot.take({ url, ...options });
}
// IntelliSense will show parameter descriptions and types
const result = await takeScreenshot('https://example.com', {
fullPage: true, // IntelliSense shows: "Capture full page (default: false)"
format: 'png' // IntelliSense shows: "'png' | 'jpeg'"
});
Testing with Types
Copy
// Typed test utilities
interface TestCase<TInput, TOutput> {
name: string;
input: TInput;
expectedOutput: TOutput;
schema?: object;
}
class TypedTester {
constructor(private bt: BrowserTest) {}
async runTest<TOutput>(
testCase: TestCase<any, TOutput>
): Promise<{ success: boolean; result?: TOutput; error?: string }> {
try {
const result = await this.bt.testing.execute({
instructions: `Test: ${testCase.name}`,
url: testCase.input.url || 'https://example.com',
outputSchema: testCase.schema
});
return {
success: true,
result: result.results.structuredData as TOutput
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
}
// Usage
interface LoginOutput {
success: boolean;
username?: string;
}
const tester = new TypedTester(bt);
const loginTest: TestCase<{ url: string }, LoginOutput> = {
name: 'User Login',
input: { url: 'https://app.com/login' },
expectedOutput: { success: true },
schema: {
type: 'object',
properties: {
success: { type: 'boolean' },
username: { type: 'string' }
}
}
};
const testResult = await tester.runTest(loginTest);
if (testResult.success) {
// TypeScript knows testResult.result is LoginOutput
console.log('Test passed:', testResult.result);
}
