The protocol module provides complete WebDriver and Mobile JSON Wire Protocol implementation with comprehensive error classes, route mapping, and status code handling. It enables proper WebDriver compliance and standardized error responses.
import {
Protocol,
errors,
routeConfiguringFunction,
ALL_COMMANDS,
METHOD_MAP,
statusCodes
} from "appium-base-driver";import { BaseDriver, errors, routeConfiguringFunction } from "appium-base-driver";
class CustomDriver extends BaseDriver {
async findElement(strategy, selector) {
try {
// Custom element finding logic
const element = await this.customFind(strategy, selector);
if (!element) {
throw new errors.NoSuchElementError(`Element not found with ${strategy}: ${selector}`);
}
return element.id;
} catch (err) {
if (err instanceof errors.ProtocolError) {
throw err; // Re-throw protocol errors
}
throw new errors.UnknownError(`Find element failed: ${err.message}`);
}
}
}
// Configure Express routes
const app = express();
const configureRoutes = routeConfiguringFunction(driver);
configureRoutes(app);class Protocol {
constructor();
}Empty base class that provides the foundation for protocol implementation. Extended by BaseDriver to add WebDriver command support.
function routeConfiguringFunction(driver);Returns a function that configures Express routes for WebDriver endpoints.
Parameters:
driver (BaseDriver): Driver instance to handle commandsReturns: Function that accepts an Express app instance
Example:
import express from 'express';
import { routeConfiguringFunction } from "appium-base-driver";
const app = express();
const driver = new MyDriver();
const configureRoutes = routeConfiguringFunction(driver);
configureRoutes(app);
app.listen(4723);const ALL_COMMANDS = [
'createSession', 'deleteSession', 'getSession', 'getSessions',
'findElement', 'findElements', 'click', 'sendKeys',
// ... complete list of supported commands
];
const METHOD_MAP = {
'/session': { POST: { command: 'createSession' } },
'/sessions': { GET: { command: 'getSessions' } },
'/session/:sessionId': {
GET: { command: 'getSession' },
DELETE: { command: 'deleteSession' }
},
// ... complete HTTP route to command mapping
};
function routeToCommandName(endpoint, method);
function isSessionCommand(command);ALL_COMMANDS - Array of all supported WebDriver commandsMETHOD_MAP - Complete mapping of HTTP routes to commandsrouteToCommandName() - Maps HTTP route and method to command nameisSessionCommand() - Checks if command requires an active sessionExample:
// Get command name from route
const commandName = routeToCommandName('/session/:sessionId/element', 'POST');
// Returns: 'findElement'
// Check if command needs session
const needsSession = isSessionCommand('findElement'); // true
const noSession = isSessionCommand('createSession'); // falseconst NO_SESSION_ID_COMMANDS = [
'createSession', 'getSessions', 'getStatus'
];Array of commands that don't require an active session ID.
function isErrorType(err, type);
function errorFromMJSONWPStatusCode(code, message);
function errorFromW3CJsonCode(code, message, stacktrace);Error Type Checking:
import { errors, isErrorType } from "appium-base-driver";
try {
await driver.findElement('id', 'nonexistent');
} catch (err) {
if (isErrorType(err, 'NoSuchElementError')) {
console.log('Element not found');
}
}Error Creation from Status Codes:
// Create error from JSONWP status code
const error = errorFromMJSONWPStatusCode(7, 'Element not found');
// Create error from W3C status code
const w3cError = errorFromW3CJsonCode('no such element', 'Element not found', stackTrace);All error classes extend ProtocolError and provide standardized WebDriver error responses:
class ProtocolError extends Error {
constructor(message);
}
// Element-related errors
class NoSuchElementError extends ProtocolError {}
class StaleElementReferenceError extends ProtocolError {}
class ElementNotVisibleError extends ProtocolError {}
class ElementNotInteractableError extends ProtocolError {}
class ElementClickInterceptedError extends ProtocolError {}
class ElementIsNotSelectableError extends ProtocolError {}
class InvalidElementStateError extends ProtocolError {}
class InvalidElementCoordinatesError extends ProtocolError {}
// Session and driver errors
class NoSuchDriverError extends ProtocolError {}
class SessionNotCreatedError extends ProtocolError {}
class NoSuchSessionError extends ProtocolError {}
// Command and navigation errors
class UnknownCommandError extends ProtocolError {}
class UnknownError extends ProtocolError {}
class NotImplementedError extends ProtocolError {}
class NotYetImplementedError extends ProtocolError {}
// Timeout errors
class TimeoutError extends ProtocolError {}
class ScriptTimeoutError extends ProtocolError {}
// Window and frame errors
class NoSuchWindowError extends ProtocolError {}
class NoSuchFrameError extends ProtocolError {}
// Alert/modal errors
class UnexpectedAlertOpenError extends ProtocolError {}
class NoAlertOpenError extends ProtocolError {}
class NoSuchAlertError extends ProtocolError {} // Alias for NoAlertOpenError
// Input and interaction errors
class InvalidArgumentError extends ProtocolError {}
class InvalidSelectorError extends ProtocolError {}
class MoveTargetOutOfBoundsError extends ProtocolError {}
// Cookie errors
class InvalidCookieDomainError extends ProtocolError {}
class NoSuchCookieError extends ProtocolError {}
class UnableToSetCookieError extends ProtocolError {}
// Script execution errors
class JavaScriptError extends ProtocolError {}
class XPathLookupError extends ProtocolError {}
// IME errors
class IMENotAvailableError extends ProtocolError {}
class IMEEngineActivationFailedError extends ProtocolError {}
// Context errors
class NoSuchContextError extends ProtocolError {}
class InvalidContextError extends ProtocolError {}
// Screen capture errors
class UnableToCaptureScreen extends ProtocolError {}
// Parameter and proxy errors
class BadParametersError extends ProtocolError {}
class ProxyRequestError extends ProtocolError {}
// Coordinate errors (aliases)
class InvalidCoordinatesError extends ProtocolError {} // Alias for InvalidElementCoordinatesErrorimport { errors } from "appium-base-driver";
class CustomDriver extends BaseDriver {
async findElement(strategy, selector) {
if (!this.supportedStrategies.includes(strategy)) {
throw new errors.InvalidSelectorError(`Unsupported locator strategy: ${strategy}`);
}
const element = await this.nativeFindElement(strategy, selector);
if (!element) {
throw new errors.NoSuchElementError(`Element not found using ${strategy}: ${selector}`);
}
return element.id;
}
async click(elementId) {
const element = await this.getElement(elementId);
if (!element) {
throw new errors.StaleElementReferenceError('Element is no longer attached to DOM');
}
if (!element.isClickable()) {
throw new errors.ElementNotInteractableError('Element is not clickable');
}
try {
await element.click();
} catch (err) {
if (err.message.includes('click intercepted')) {
throw new errors.ElementClickInterceptedError('Click was intercepted by another element');
}
throw new errors.UnknownError(`Click failed: ${err.message}`);
}
}
async executeScript(script, args) {
try {
return await this.evaluateJavaScript(script, args);
} catch (err) {
if (err.name === 'SyntaxError') {
throw new errors.JavaScriptError(`Invalid JavaScript: ${err.message}`);
}
if (err.name === 'TimeoutError') {
throw new errors.ScriptTimeoutError('Script execution timed out');
}
throw new errors.JavaScriptError(`Script execution failed: ${err.message}`);
}
}
}const statusCodes = {
0: { name: 'Success', summary: 'The command executed successfully' },
6: { name: 'NoSuchDriver', summary: 'A session is either terminated or not started' },
7: { name: 'NoSuchElement', summary: 'An element could not be located on the page using the given search parameters' },
8: { name: 'NoSuchFrame', summary: 'A request to switch to a frame could not be satisfied because the frame could not be found' },
9: { name: 'UnknownCommand', summary: 'The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource' },
10: { name: 'StaleElementReference', summary: 'An element command failed because the referenced element is no longer attached to the DOM' },
11: { name: 'ElementNotVisible', summary: 'An element command could not be completed because the element is not visible on the page' },
12: { name: 'InvalidElementState', summary: 'An element command could not be completed because the element is in an invalid state' },
13: { name: 'UnknownError', summary: 'An unknown server-side error occurred while processing the command' },
15: { name: 'ElementIsNotSelectable', summary: 'An attempt was made to select an element that cannot be selected' },
17: { name: 'JavaScriptError', summary: 'An error occurred while executing user supplied JavaScript' },
19: { name: 'XPathLookupError', summary: 'An error occurred while searching for an element by XPath' },
21: { name: 'Timeout', summary: 'An operation did not complete before its timeout expired' },
23: { name: 'NoSuchWindow', summary: 'A request to switch to a different window could not be satisfied because the window could not be found' },
24: { name: 'InvalidCookieDomain', summary: 'An illegal attempt was made to set a cookie under a different domain than the current page' },
25: { name: 'UnableToSetCookie', summary: 'A request to set a cookie\'s value could not be satisfied' },
26: { name: 'UnexpectedAlertOpen', summary: 'A modal dialog was open, blocking this operation' },
27: { name: 'NoAlertOpenError', summary: 'An attempt was made to operate on a modal dialog when one was not open' },
28: { name: 'ScriptTimeout', summary: 'A script did not complete before its timeout expired' },
29: { name: 'InvalidElementCoordinates', summary: 'The coordinates provided to an interactions operation are invalid' },
30: { name: 'IMENotAvailable', summary: 'IME was not available' },
31: { name: 'IMEEngineActivationFailed', summary: 'An IME engine could not be started' },
32: { name: 'InvalidSelector', summary: 'Argument was an invalid selector (e.g. XPath or CSS)' },
33: { name: 'SessionNotCreatedError', summary: 'A new session could not be created' },
34: { name: 'MoveTargetOutOfBounds', summary: 'Target provided for a move action is out of bounds' },
35: { name: 'NoSuchContext', summary: 'No such context found' },
36: { name: 'InvalidContext', summary: 'That command could not be executed because the remote end is not in a valid context' }
};
function getSummaryByCode(code);import { statusCodes, getSummaryByCode } from "appium-base-driver";
// Get status information
const elementNotFoundStatus = statusCodes[7];
console.log(elementNotFoundStatus.name); // 'NoSuchElement'
console.log(elementNotFoundStatus.summary); // 'An element could not be located...'
// Get summary by code
const summary = getSummaryByCode(21); // 'An operation did not complete before its timeout expired'// Protocol types
const PROTOCOL_W3C = 'W3C';
const PROTOCOL_MJSONWP = 'MJSONWP';
// HTTP methods
const SUPPORTED_METHODS = ['GET', 'POST', 'DELETE'];
// Common endpoints
const SESSION_ENDPOINT = '/session';
const ELEMENT_ENDPOINT = '/session/:sessionId/element';
const ELEMENTS_ENDPOINT = '/session/:sessionId/elements';import { errors, isErrorType } from "appium-base-driver";
class CustomDriver extends BaseDriver {
async executeCommand(cmd, ...args) {
try {
return await super.executeCommand(cmd, ...args);
} catch (err) {
// Log all errors for debugging
console.log(`Command ${cmd} failed:`, err.message);
// Convert internal errors to protocol errors
if (err.code === 'ECONNREFUSED') {
throw new errors.SessionNotCreatedError('Could not connect to device');
}
// Re-throw protocol errors unchanged
if (isErrorType(err, 'ProtocolError')) {
throw err;
}
// Wrap unknown errors
throw new errors.UnknownError(`Unexpected error in ${cmd}: ${err.message}`);
}
}
}The protocol module automatically formats errors into proper WebDriver JSON responses:
// JSONWP format
{
"status": 7,
"value": {
"message": "Element not found using id: nonexistent-element",
"origValue": "Element not found using id: nonexistent-element"
},
"sessionId": "abc123"
}
// W3C format
{
"value": {
"error": "no such element",
"message": "Element not found using id: nonexistent-element",
"stacktrace": "NoSuchElementError: Element not found..."
}
}This protocol and error handling system ensures full WebDriver compliance and provides consistent error responses across all Appium drivers.