or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

base-driver.mddevice-settings.mdexpress-server.mdindex.mdjsonwp-proxy.mdprotocol-errors.md
tile.json

device-settings.mddocs/

Device Settings Management

The DeviceSettings class provides dynamic configuration management for Appium drivers, allowing runtime setting modifications with validation and update callbacks. This enables drivers to adjust behavior based on user preferences and test requirements.

Core Imports

import { DeviceSettings } from "appium-base-driver";

Basic Usage

import { DeviceSettings, BaseDriver } from "appium-base-driver";

class CustomDriver extends BaseDriver {
  constructor(opts) {
    super(opts);
    
    // Initialize device settings with defaults
    this.settings = new DeviceSettings({
      elementTimeout: 30000,
      retryCount: 3,
      screenshotQuality: 0.8,
      enableLogging: true
    }, this.onSettingsUpdate.bind(this));
  }
  
  async onSettingsUpdate(key, newValue, oldValue) {
    console.log(`Setting '${key}' changed from ${oldValue} to ${newValue}`);
    
    // React to specific setting changes
    if (key === 'enableLogging') {
      this.configureLogging(newValue);
    } else if (key === 'elementTimeout') {
      this.setImplicitWait(newValue);
    }
  }
  
  // Use settings in driver methods
  async findElement(strategy, selector) {
    const timeout = this.settings.getSettings().elementTimeout;
    const retries = this.settings.getSettings().retryCount;
    
    for (let i = 0; i < retries; i++) {
      try {
        return await this.findElementWithTimeout(strategy, selector, timeout);
      } catch (err) {
        if (i === retries - 1) throw err;
        await this.sleep(1000);
      }
    }
  }
}

// Update settings via WebDriver commands
await driver.updateSettings({
  elementTimeout: 45000,
  retryCount: 5
});

const currentSettings = await driver.getSettings();
console.log(currentSettings);
// { elementTimeout: 45000, retryCount: 5, screenshotQuality: 0.8, enableLogging: true }

Constructor and Configuration

class DeviceSettings {
  constructor(defaultSettings = {}, onSettingsUpdate = null);
}

Parameters:

  • defaultSettings (object, optional): Initial default settings values
  • onSettingsUpdate (function, optional): Callback function called when settings are updated
    • callback(key, newValue, oldValue) - Called for each changed setting

Example:

// Basic settings without callback
const basicSettings = new DeviceSettings({
  timeout: 30000,
  retries: 3
});

// Settings with update callback
const advancedSettings = new DeviceSettings({
  networkTimeout: 60000,
  cacheEnabled: true,
  logLevel: 'info'
}, (key, newValue, oldValue) => {
  console.log(`Setting updated: ${key} = ${newValue} (was: ${oldValue})`);
  
  // Handle specific setting changes
  switch (key) {
    case 'logLevel':
      logger.setLevel(newValue);
      break;
    case 'cacheEnabled':
      cache.setEnabled(newValue);
      break;
    case 'networkTimeout':
      httpClient.setTimeout(newValue);
      break;
  }
});

Settings Management

Updating Settings

update(newSettings);

Updates multiple settings at once with validation and triggers update callbacks.

Parameters:

  • newSettings (object): Object containing setting key-value pairs to update

Returns: void - Updates settings in place

Example:

const settings = new DeviceSettings({
  elementTimeout: 30000,
  implicitWait: 5000,
  retryAttempts: 3
});

// Update multiple settings
settings.update({
  elementTimeout: 45000,
  retryAttempts: 5,
  newSetting: 'value'
});

// All settings are now updated
console.log(settings.getSettings());
// {
//   elementTimeout: 45000,
//   implicitWait: 5000,
//   retryAttempts: 5,
//   newSetting: 'value'
// }

Retrieving Settings

getSettings();

Returns the current settings object.

Returns: object - Copy of current settings

Example:

const settings = new DeviceSettings({
  timeout: 30000,
  enabled: true
});

const currentSettings = settings.getSettings();
console.log(currentSettings);
// { timeout: 30000, enabled: true }

// Returned object is a copy - modifications don't affect internal settings
currentSettings.timeout = 60000;
console.log(settings.getSettings().timeout); // Still 30000

WebDriver Integration

BaseDriver Integration

The DeviceSettings class integrates seamlessly with BaseDriver's settings commands:

// BaseDriver automatically provides these commands when settings is set
async updateSettings(newSettings);
async getSettings();

Complete Integration Example:

import { BaseDriver, DeviceSettings } from "appium-base-driver";

class MyDriver extends BaseDriver {
  constructor(opts) {
    super(opts);
    
    // Define default settings
    const defaultSettings = {
      // Timeouts
      elementTimeout: 30000,
      implicitWait: 5000,
      commandTimeout: 60000,
      
      // Behavior settings
      autoAcceptAlerts: false,
      screenshotOnFailure: true,
      retryFailedElements: true,
      
      // Performance settings
      screenshotQuality: 0.8,
      compressionEnabled: true,
      cacheSize: 100
    };
    
    // Initialize settings with callback
    this.settings = new DeviceSettings(defaultSettings, this.handleSettingUpdate.bind(this));
  }
  
  async handleSettingUpdate(key, newValue, oldValue) {
    console.log(`[Settings] ${key}: ${oldValue} → ${newValue}`);
    
    switch (key) {
      case 'implicitWait':
        await this.setImplicitWait(newValue);
        break;
      case 'commandTimeout':
        this.setNewCommandTimeout(newValue);
        break;
      case 'autoAcceptAlerts':
        this.configureAlertHandling(newValue);
        break;
      case 'screenshotQuality':
        this.updateScreenshotConfig({ quality: newValue });
        break;
    }
  }
  
  // Settings are automatically available via WebDriver protocol
  // POST /session/:sessionId/appium/settings
  // GET /session/:sessionId/appium/settings
}

// Client usage
const driver = new MyDriver();
await driver.createSession(capabilities);

// Update settings via WebDriver API
await driver.updateSettings({
  elementTimeout: 45000,
  autoAcceptAlerts: true,
  screenshotQuality: 0.9
});

// Get current settings
const settings = await driver.getSettings();
console.log(settings.elementTimeout); // 45000

Advanced Settings Patterns

Validation and Constraints

import { DeviceSettings, BaseDriver } from "appium-base-driver";

class ValidatedSettingsDriver extends BaseDriver {
  constructor(opts) {
    super(opts);
    
    this.settingsConstraints = {
      timeout: { min: 1000, max: 300000 },
      quality: { min: 0.1, max: 1.0 },
      logLevel: { values: ['debug', 'info', 'warn', 'error'] }
    };
    
    this.settings = new DeviceSettings({
      timeout: 30000,
      quality: 0.8,
      logLevel: 'info'
    }, this.validateAndUpdateSetting.bind(this));
  }
  
  validateAndUpdateSetting(key, newValue, oldValue) {
    const constraint = this.settingsConstraints[key];
    
    if (constraint) {
      if (constraint.min !== undefined && newValue < constraint.min) {
        throw new Error(`Setting '${key}' must be >= ${constraint.min}`);
      }
      if (constraint.max !== undefined && newValue > constraint.max) {
        throw new Error(`Setting '${key}' must be <= ${constraint.max}`);
      }
      if (constraint.values && !constraint.values.includes(newValue)) {
        throw new Error(`Setting '${key}' must be one of: ${constraint.values.join(', ')}`);
      }
    }
    
    console.log(`✓ Setting '${key}' updated to ${newValue}`);
    this.applySetting(key, newValue);
  }
  
  applySetting(key, value) {
    switch (key) {
      case 'timeout':
        this.defaultTimeout = value;
        break;
      case 'quality':
        this.screenshotQuality = value;
        break;
      case 'logLevel':
        this.logger.setLevel(value);
        break;
    }
  }
}

Settings Persistence

import fs from 'fs';
import path from 'path';
import { DeviceSettings, BaseDriver } from "appium-base-driver";

class PersistentSettingsDriver extends BaseDriver {
  constructor(opts) {
    super(opts);
    
    this.settingsFile = path.join(opts.tmpDir, 'driver-settings.json');
    const savedSettings = this.loadSettings();
    
    this.settings = new DeviceSettings(savedSettings, (key, newValue, oldValue) => {
      console.log(`Setting updated: ${key} = ${newValue}`);
      this.saveSettings();
      this.handleSettingChange(key, newValue, oldValue);
    });
  }
  
  loadSettings() {
    try {
      if (fs.existsSync(this.settingsFile)) {
        const data = fs.readFileSync(this.settingsFile, 'utf8');
        return JSON.parse(data);
      }
    } catch (err) {
      console.warn('Failed to load saved settings:', err.message);
    }
    
    // Return default settings
    return {
      elementTimeout: 30000,
      retryCount: 3,
      screenshotOnFailure: true
    };
  }
  
  saveSettings() {
    try {
      const settings = this.settings.getSettings();
      fs.writeFileSync(this.settingsFile, JSON.stringify(settings, null, 2));
    } catch (err) {
      console.warn('Failed to save settings:', err.message);
    }
  }
  
  handleSettingChange(key, newValue, oldValue) {
    // Apply setting changes to driver behavior
    if (key === 'elementTimeout') {
      this.setImplicitWait(newValue);
    }
  }
}

Conditional Settings

import { DeviceSettings, BaseDriver } from "appium-base-driver";

class ConditionalSettingsDriver extends BaseDriver {
  constructor(opts) {
    super(opts);
    
    // Base settings
    this.settings = new DeviceSettings({
      mode: 'standard',
      debugging: false,
      performance: 'balanced'
    }, this.applyConditionalSettings.bind(this));
  }
  
  applyConditionalSettings(key, newValue, oldValue) {
    if (key === 'mode') {
      this.configureForMode(newValue);
    } else if (key === 'debugging') {
      this.configureDebugging(newValue);
    } else if (key === 'performance') {
      this.configurePerformance(newValue);
    }
  }
  
  configureForMode(mode) {
    const modeSettings = {
      'fast': {
        elementTimeout: 10000,
        implicitWait: 1000,
        screenshotOnFailure: false
      },
      'standard': {
        elementTimeout: 30000,
        implicitWait: 5000,
        screenshotOnFailure: true
      },
      'thorough': {
        elementTimeout: 60000,
        implicitWait: 10000,
        screenshotOnFailure: true,
        retryCount: 5
      }
    };
    
    const settings = modeSettings[mode] || modeSettings['standard'];
    
    // Apply mode-specific settings without triggering callbacks
    Object.assign(this.settings._settings, settings);
    console.log(`Applied ${mode} mode settings:`, settings);
  }
  
  configureDebugging(enabled) {
    if (enabled) {
      this.settings.update({
        logLevel: 'debug',
        screenshotOnFailure: true,
        savePageSource: true,
        networkLogs: true
      });
    } else {
      this.settings.update({
        logLevel: 'info',
        savePageSource: false,
        networkLogs: false
      });
    }
  }
  
  configurePerformance(level) {
    const performanceSettings = {
      'fast': { screenshotQuality: 0.5, compressionLevel: 9 },
      'balanced': { screenshotQuality: 0.8, compressionLevel: 6 },
      'quality': { screenshotQuality: 1.0, compressionLevel: 1 }
    };
    
    const settings = performanceSettings[level] || performanceSettings['balanced'];
    this.settings.update(settings);
  }
}

Settings in Command Execution

Settings can be used throughout the driver to modify behavior dynamically:

import { DeviceSettings, BaseDriver } from "appium-base-driver";

class SettingsAwareDriver extends BaseDriver {
  constructor(opts) {
    super(opts);
    
    this.settings = new DeviceSettings({
      findStrategy: 'native',
      retryDelay: 1000,
      maxRetries: 3,
      timeoutMultiplier: 1.0
    });
  }
  
  async findElement(strategy, selector) {
    const settings = this.settings.getSettings();
    const timeout = this.implicitWaitMs * settings.timeoutMultiplier;
    
    for (let attempt = 0; attempt < settings.maxRetries; attempt++) {
      try {
        if (settings.findStrategy === 'enhanced') {
          return await this.enhancedFindElement(strategy, selector, timeout);
        } else {
          return await this.nativeFindElement(strategy, selector, timeout);
        }
      } catch (err) {
        if (attempt < settings.maxRetries - 1) {
          await this.sleep(settings.retryDelay);
        } else {
          throw err;
        }
      }
    }
  }
  
  async getScreenshot() {
    const settings = this.settings.getSettings();
    const quality = settings.screenshotQuality || 0.8;
    const format = settings.screenshotFormat || 'png';
    
    return await this.captureScreenshot({ quality, format });
  }
}

The DeviceSettings class provides a flexible and powerful way to manage driver configuration, enabling runtime customization while maintaining proper validation and change tracking.