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.
import { DeviceSettings } from "appium-base-driver";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 }class DeviceSettings {
constructor(defaultSettings = {}, onSettingsUpdate = null);
}Parameters:
defaultSettings (object, optional): Initial default settings valuesonSettingsUpdate (function, optional): Callback function called when settings are updated
callback(key, newValue, oldValue) - Called for each changed settingExample:
// 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;
}
});update(newSettings);Updates multiple settings at once with validation and triggers update callbacks.
Parameters:
newSettings (object): Object containing setting key-value pairs to updateReturns: 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'
// }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 30000The 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); // 45000import { 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;
}
}
}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);
}
}
}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 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.