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

express-server.mddocs/

Express HTTP Server

The express server module provides HTTP server functionality with WebDriver route configuration, static asset serving, middleware support, and WebSocket capabilities for Appium drivers.

Core Imports

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 server

Basic Usage

import { 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...

Server Creation

Main Server Function

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 on
  • hostname (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 headers

Static Asset Serving

Static Directory

const 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.html

The server automatically serves static assets like:

  • Welcome page and documentation
  • Favicon
  • Client-side tools and utilities
  • CSS and JavaScript assets

Test Page Functions

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=500

Test Page Features:

  • Form inputs and buttons for interaction testing
  • Cookie management for session testing
  • Dynamic content based on request parameters
  • Responsive layouts for different screen sizes
  • JavaScript interaction elements

WebSocket Support

WebSocket Configuration

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/logs

Server Middleware

The Express server includes several built-in middleware components:

Request Processing Middleware

  • Body Parser: Handles JSON and URL-encoded request bodies
  • Method Override: Supports HTTP method override via headers
  • CORS: Configures Cross-Origin Resource Sharing headers
  • Morgan Logging: HTTP request logging
  • Static File Serving: Serves static assets

Error Handling Middleware

  • Protocol Error Handler: Converts protocol errors to proper WebDriver responses
  • 404 Handler: Returns appropriate WebDriver error for unknown endpoints
  • General Error Handler: Catches and formats unexpected errors

Custom Middleware

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');

Server Configuration Options

The server supports extensive configuration through Express app settings:

Security Configuration

// 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();
});

Logging Configuration

// 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();
});

Complete Server Setup Example

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);

Server Events and Lifecycle

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);
  });
});

Built-in Endpoints

The server automatically provides these WebDriver-compliant endpoints:

  • GET /status - Server status and capabilities
  • POST /session - Create new session
  • GET /sessions - List active sessions
  • GET /session/:sessionId - Get session capabilities
  • DELETE /session/:sessionId - Delete session
  • POST /session/:sessionId/element - Find element
  • POST /session/:sessionId/elements - Find elements
  • POST /session/:sessionId/element/:elementId/click - Click element
  • POST /session/:sessionId/element/:elementId/value - Send keys to element
  • And many more WebDriver protocol endpoints...

The server provides a complete WebDriver-compliant HTTP interface with proper error handling, middleware support, and extensibility for custom driver implementations.