The express server module provides HTTP server functionality with WebDriver route configuration, static asset serving, middleware support, and WebSocket capabilities for Appium drivers.
import {
server,
STATIC_DIR,
DEFAULT_WS_PATHNAME_PREFIX
} from "appium-base-driver";
// Note: Test page functions (guineaPig, welcome, etc.) are internal
// and automatically registered as routes by the serverimport { server, routeConfiguringFunction } from "appium-base-driver";
const driver = new MyDriver();
const configureRoutes = routeConfiguringFunction(driver);
// Create and start server
const httpServer = await server(configureRoutes, 4723, 'localhost');
console.log('Appium server started on http://localhost:4723');
// Server includes all WebDriver endpoints automatically
// GET /status - Server status
// POST /session - Create session
// GET /sessions - List sessions
// And all other WebDriver routes...function server(configureRoutes, port, hostname);Creates and configures an Express HTTP server with WebDriver routes and middleware.
Parameters:
configureRoutes (function): Route configuration function (from routeConfiguringFunction)port (number): Port number to listen onhostname (string): Hostname to bind to (e.g., 'localhost', '0.0.0.0')Returns: Promise<http.Server> - Configured HTTP server instance
Example:
import express from 'express';
import { server, routeConfiguringFunction } from "appium-base-driver";
class MyDriver extends BaseDriver {
// Driver implementation
}
const driver = new MyDriver();
const configureRoutes = routeConfiguringFunction(driver);
// Create server with custom middleware
const httpServer = await server(configureRoutes, 4723, '0.0.0.0');
// Server is now running and handles:
// - All WebDriver protocol endpoints
// - Static file serving
// - Error handling middleware
// - Request logging
// - CORS headersconst STATIC_DIR;Path to the static assets directory served by the HTTP server.
Usage:
import path from 'path';
import { STATIC_DIR } from "appium-base-driver";
// Access static files
const faviconPath = path.join(STATIC_DIR, 'favicon.ico');
const welcomePage = path.join(STATIC_DIR, 'welcome.html');
// Static files are automatically served at /static/*
// Example: http://localhost:4723/static/welcome.htmlThe server automatically serves static assets like:
Dynamic test pages for automated testing and development:
function guineaPig(req, res);
function guineaPigScrollable(req, res);
function guineaPigAppBanner(req, res);
function welcome(req, res);These functions provide test pages mapped to specific routes:
Usage:
// These routes are automatically registered when using the server:
// GET/POST /test/guinea-pig - Basic test page with forms and elements
// GET/POST /test/guinea-pig-scrollable - Scrollable test page
// GET/POST /test/guinea-pig-app-banner - Test page with app banners
// GET /welcome - Welcome page with navigation
// Test pages support query parameters:
// ?delay=1000 - Add response delay in milliseconds
// ?throwError=true - Simulate server errors for testing
// Example accessing test pages:
// http://localhost:4723/test/guinea-pig
// http://localhost:4723/welcome?delay=500Test Page Features:
const DEFAULT_WS_PATHNAME_PREFIX = '/ws';Default URL prefix for WebSocket connections.
WebSocket Methods (attached to HTTP server):
// Added to HTTP server instance
httpServer.addWebSocketHandler(handlerPathname, handlerServer);
httpServer.removeWebSocketHandler(handlerPathname);
httpServer.removeAllWebSocketHandlers();
httpServer.getWebSocketHandlers(keysFilter);WebSocket Usage:
import { server, routeConfiguringFunction, DEFAULT_WS_PATHNAME_PREFIX } from "appium-base-driver";
const httpServer = await server(configureRoutes, 4723, 'localhost');
// Add WebSocket handler
httpServer.addWebSocketHandler('/ws/logs', (ws, req) => {
// Handle WebSocket connections for log streaming
ws.on('message', (message) => {
console.log('Received:', message);
});
// Send periodic log updates
const logInterval = setInterval(() => {
ws.send(JSON.stringify({ type: 'log', data: 'Log message' }));
}, 1000);
ws.on('close', () => {
clearInterval(logInterval);
});
});
// WebSocket available at: ws://localhost:4723/ws/logsThe Express server includes several built-in middleware components:
import { server, routeConfiguringFunction } from "appium-base-driver";
const configureRoutes = routeConfiguringFunction(driver);
// Add custom middleware by wrapping the configure function
const customConfigureRoutes = (app) => {
// Add custom middleware before WebDriver routes
app.use('/custom', (req, res, next) => {
res.json({ message: 'Custom endpoint' });
});
// Rate limiting middleware
app.use((req, res, next) => {
// Custom rate limiting logic
next();
});
// Configure WebDriver routes
configureRoutes(app);
// Add middleware after WebDriver routes
app.use((req, res, next) => {
// Custom post-processing
next();
});
};
const httpServer = await server(customConfigureRoutes, 4723, 'localhost');The server supports extensive configuration through Express app settings:
// CORS configuration is automatically handled
// Custom CORS settings can be added via middleware
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://trusted-domain.com');
res.header('Access-Control-Allow-Methods', 'GET,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization');
next();
});// Morgan logging is included by default
// Custom logging can be added:
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
next();
});import { BaseDriver, server, routeConfiguringFunction } from "appium-base-driver";
class CustomAppiumDriver extends BaseDriver {
constructor(opts) {
super(opts);
this.driverName = 'Custom';
}
async createSession(jwpDesiredCaps, jwpRequiredCaps, w3cCapabilities) {
const caps = await super.createSession(jwpDesiredCaps, jwpRequiredCaps, w3cCapabilities);
console.log(`${this.driverName} session created:`, caps);
return caps;
}
async findElement(strategy, selector) {
console.log(`Finding element: ${strategy} = ${selector}`);
// Custom element finding logic
return await super.findElement(strategy, selector);
}
}
async function startAppiumServer() {
const driver = new CustomAppiumDriver({
port: 4723,
timeout: 60000
});
const configureRoutes = routeConfiguringFunction(driver);
// Enhanced route configuration with custom middleware
const enhancedConfigureRoutes = (app) => {
// Custom status endpoint
app.get('/custom-status', (req, res) => {
res.json({
status: 'running',
driver: driver.driverName,
uptime: process.uptime()
});
});
// Request timing middleware
app.use((req, res, next) => {
req.startTime = Date.now();
res.on('finish', () => {
const duration = Date.now() - req.startTime;
console.log(`${req.method} ${req.url} - ${res.statusCode} (${duration}ms)`);
});
next();
});
// Configure standard WebDriver routes
configureRoutes(app);
};
try {
const httpServer = await server(enhancedConfigureRoutes, 4723, '0.0.0.0');
// Add WebSocket handler for real-time communication
httpServer.addWebSocketHandler('/ws/driver-events', (ws, req) => {
console.log('WebSocket client connected');
// Send welcome message
ws.send(JSON.stringify({
type: 'welcome',
message: 'Connected to Appium driver WebSocket'
}));
// Handle driver events
const eventHandler = (event) => {
ws.send(JSON.stringify({
type: 'driver-event',
event: event
}));
};
driver.on('command', eventHandler);
ws.on('close', () => {
console.log('WebSocket client disconnected');
driver.removeListener('command', eventHandler);
});
});
console.log('Appium server started successfully');
console.log(`HTTP server: http://localhost:4723`);
console.log(`WebSocket: ws://localhost:4723/ws/driver-events`);
console.log(`Status endpoint: http://localhost:4723/status`);
console.log(`Custom status: http://localhost:4723/custom-status`);
return httpServer;
} catch (err) {
console.error('Failed to start server:', err);
throw err;
}
}
// Start the server
startAppiumServer().catch(console.error);The HTTP server provides several lifecycle events:
const httpServer = await server(configureRoutes, 4723, 'localhost');
// Server events
httpServer.on('listening', () => {
const address = httpServer.address();
console.log(`Server listening on ${address.address}:${address.port}`);
});
httpServer.on('error', (err) => {
console.error('Server error:', err);
});
httpServer.on('close', () => {
console.log('Server closed');
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('Shutting down server...');
httpServer.close(() => {
console.log('Server shut down successfully');
process.exit(0);
});
});The server automatically provides these WebDriver-compliant endpoints:
GET /status - Server status and capabilitiesPOST /session - Create new sessionGET /sessions - List active sessionsGET /session/:sessionId - Get session capabilitiesDELETE /session/:sessionId - Delete sessionPOST /session/:sessionId/element - Find elementPOST /session/:sessionId/elements - Find elementsPOST /session/:sessionId/element/:elementId/click - Click elementPOST /session/:sessionId/element/:elementId/value - Send keys to elementThe server provides a complete WebDriver-compliant HTTP interface with proper error handling, middleware support, and extensibility for custom driver implementations.