CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-detox

Gray box end-to-end testing and automation framework for mobile applications with advanced synchronization capabilities

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

element-interaction.mddocs/

Element Interaction

Type-safe element selection, interaction, and assertion system with automatic synchronization and comprehensive action support for mobile UI testing.

Capabilities

Element Selection

Create element references using matcher-based selection with optional indexing for multiple matches.

/**
 * Create element reference from matcher
 * @param matcher - Element selector (by.id, by.text, etc.)
 * @returns Indexable element that can be refined with atIndex()
 */
function element(matcher: NativeMatcher): IndexableNativeElement;

interface IndexableNativeElement extends NativeElement {
  /**
   * Select specific element when multiple matches exist
   * @param index - Zero-based index of element to select
   * @returns Specific element instance
   */
  atIndex(index: number): NativeElement;
}

Usage Examples:

// Select single element
const loginButton = element(by.id('login_button'));

// Select from multiple matches
const secondProduct = element(by.type('ProductCard')).atIndex(1);

Basic Interactions

Core touch interactions for element manipulation.

/**
 * Tap element at center or specific point
 * @param point - Optional coordinates within element (defaults to center)
 */
async function tap(point?: Point2D): Promise<void>;

/**
 * Long press element with optional duration and point
 * @param point - Optional coordinates within element
 * @param duration - Optional press duration in milliseconds
 */
async function longPress(point?: Point2D, duration?: number): Promise<void>;

/**
 * Perform multiple taps on element
 * @param times - Number of taps to perform
 */
async function multiTap(times: number): Promise<void>;

/**
 * Long press and drag to target element (iOS only)
 * @param duration - Long press duration
 * @param normalizedPositionX - X position within source element (0-1)
 * @param normalizedPositionY - Y position within source element (0-1)
 * @param targetElement - Target element to drag to
 * @param normalizedTargetPositionX - X position within target element (0-1)
 * @param normalizedTargetPositionY - Y position within target element (0-1)
 * @param speed - Drag speed
 * @param holdDuration - Duration to hold at target
 */
async function longPressAndDrag(
  duration: number,
  normalizedPositionX: number,
  normalizedPositionY: number,
  targetElement: NativeElement,
  normalizedTargetPositionX: number,
  normalizedTargetPositionY: number,
  speed: Speed,
  holdDuration: number
): Promise<void>;

Usage Examples:

// Basic tap
await element(by.id('submit_button')).tap();

// Tap at specific point
await element(by.id('canvas')).tap({ x: 50, y: 75 });

// Long press with custom duration
await element(by.id('context_menu')).longPress({ x: 10, y: 10 }, 2000);

// Double tap
await element(by.id('photo')).multiTap(2);

Text Input

Text entry and manipulation for input fields and text areas.

/**
 * Type text using device keyboard
 * @param text - Text to type
 */
async function typeText(text: string): Promise<void>;

/**
 * Replace all text in field with new text
 * @param text - Replacement text
 */
async function replaceText(text: string): Promise<void>;

/**
 * Clear all text from field
 */
async function clearText(): Promise<void>;

/**
 * Tap backspace key on device keyboard
 */
async function tapBackspaceKey(): Promise<void>;

/**
 * Tap return/enter key on device keyboard
 */
async function tapReturnKey(): Promise<void>;

Usage Examples:

// Type in text field
await element(by.id('email_input')).typeText('user@example.com');

// Replace existing text
await element(by.id('name_input')).replaceText('New Name');

// Clear field
await element(by.id('search_input')).clearText();

// Use keyboard keys
await element(by.id('password_input')).tapBackspaceKey();
await element(by.id('message_input')).tapReturnKey();

Scrolling and Navigation

Scroll operations for lists, scroll views, and navigation.

/**
 * Scroll by pixel amount in specified direction
 * @param pixels - Distance to scroll in pixels
 * @param direction - Scroll direction
 * @param startPositionX - Starting X position (0-1 percentage, NaN for auto)
 * @param startPositionY - Starting Y position (0-1 percentage, NaN for auto)
 */
async function scroll(
  pixels: number,
  direction: Direction,
  startPositionX?: number,
  startPositionY?: number
): Promise<void>;

/**
 * Scroll to edge of scrollable content
 * @param edge - Target edge
 * @param startPositionX - Starting X position (0-1 percentage, NaN for auto)
 * @param startPositionY - Starting Y position (0-1 percentage, NaN for auto)
 */
async function scrollTo(
  edge: Direction,
  startPositionX?: number,
  startPositionY?: number
): Promise<void>;

/**
 * Scroll to specific index in list/collection
 * @param index - Target index
 */
async function scrollToIndex(index: number): Promise<void>;

type Direction = 'left' | 'right' | 'top' | 'bottom' | 'up' | 'down';

Usage Examples:

// Scroll down by 200 pixels
await element(by.id('scroll_view')).scroll(200, 'down');

// Scroll to bottom
await element(by.id('product_list')).scrollTo('bottom');

// Scroll to specific item
await element(by.id('message_list')).scrollToIndex(10);

// Scroll with custom start position
await element(by.id('horizontal_list')).scroll(100, 'right', 0.5, 0.8);

Swipe Gestures

Swipe interactions with configurable speed and distance.

/**
 * Swipe in specified direction
 * @param direction - Swipe direction
 * @param speed - Swipe speed (defaults to 'fast')
 * @param percentage - Screen percentage to swipe (0-1, defaults based on direction)
 * @param normalizedStartingPointX - Starting X position (0-1)
 * @param normalizedStartingPointY - Starting Y position (0-1)
 */
async function swipe(
  direction: Direction,
  speed?: Speed,
  percentage?: number,
  normalizedStartingPointX?: number,
  normalizedStartingPointY?: number
): Promise<void>;

type Speed = 'fast' | 'slow';

Usage Examples:

// Basic swipe
await element(by.id('card')).swipe('left');

// Slow swipe with custom distance
await element(by.id('gallery')).swipe('right', 'slow', 0.8);

// Swipe from specific starting point
await element(by.id('drawer')).swipe('up', 'fast', 0.5, 0.1, 0.9);

Specialized Input Controls

Interactions for specialized UI controls like sliders, pickers, and date selectors.

/**
 * Adjust slider to specific position
 * @param newPosition - Target position (0-1)
 */
async function adjustSliderToPosition(newPosition: number): Promise<void>;

/**
 * Set picker column to specific value (iOS only)
 * @param column - Column index (starts from 0)
 * @param value - Target value string
 */
async function setColumnToValue(column: number, value: string): Promise<void>;

/**
 * Set date picker to specific date
 * @param dateString - Date in specified format
 * @param dateFormat - Format specification ('ISO8601' or custom format)
 */
async function setDatePickerDate(dateString: string, dateFormat: string): Promise<void>;

Usage Examples:

// Set slider to 75%
await element(by.id('volume_slider')).adjustSliderToPosition(0.75);

// Set picker values
await element(by.type('UIPickerView')).setColumnToValue(0, 'January');
await element(by.type('UIPickerView')).setColumnToValue(1, '15');

// Set date picker
await element(by.id('date_picker')).setDatePickerDate('2024-01-15T00:00:00Z', 'ISO8601');
await element(by.id('date_picker')).setDatePickerDate('2024/01/15', 'yyyy/MM/dd');

Pinch and Zoom (iOS Only)

Multi-touch pinch gestures for zoom operations.

/**
 * Pinch with specified scale factor
 * @param scale - Scale factor (>1 for zoom in, <1 for zoom out)
 * @param speed - Pinch speed (defaults to 'fast')
 * @param angle - Rotation angle in radians (defaults to 0)
 */
async function pinch(scale: number, speed?: Speed, angle?: number): Promise<void>;

/**
 * @deprecated Use pinch() instead
 * Pinch with direction, speed, and angle
 */
async function pinchWithAngle(direction: PinchDirection, speed: Speed, angle: number): Promise<void>;

type PinchDirection = 'outward' | 'inward';

Usage Examples:

// Zoom in 2x
await element(by.id('map_view')).pinch(2.0);

// Zoom out to 50%
await element(by.id('image_view')).pinch(0.5);

// Pinch with rotation
await element(by.id('photo')).pinch(1.5, 'slow', Math.PI / 4);

Accessibility Actions

Trigger accessibility actions defined by the app.

/**
 * Perform accessibility action on element
 * @param actionName - Name of accessibility action to trigger
 */
async function performAccessibilityAction(actionName: string): Promise<void>;

Usage Example:

// Trigger custom accessibility action
await element(by.id('custom_control')).performAccessibilityAction('activate');

Element Inspection

Retrieve element attributes and properties for validation and debugging.

/**
 * Get element attributes and properties
 * @returns Element attributes object or array for multiple elements
 */
async function getAttributes(): Promise<
  IosElementAttributes | 
  AndroidElementAttributes | 
  { elements: IosElementAttributes[] } | 
  { elements: AndroidElementAttributes[] }
>;

/**
 * Take screenshot of specific element
 * @param name - Name for screenshot artifact
 * @returns Temporary path to screenshot file
 */
async function takeScreenshot(name: string): Promise<string>;

// Platform-specific attribute interfaces
interface ElementAttributes {
  enabled: boolean;
  identifier: string;
  visible: boolean;
  text?: string;
  label?: string;
  placeholder?: string;
  value?: unknown;
}

interface IosElementAttributes extends ElementAttributes {
  activationPoint: Point2D;
  hittable: boolean;
  frame: ElementFrame;
  // ... additional iOS-specific properties
}

interface AndroidElementAttributes extends ElementAttributes {
  visibility: 'visible' | 'invisible' | 'gone';
  width: number;
  height: number;
  focused: boolean;
  // ... additional Android-specific properties
}

Usage Examples:

// Get element attributes
const attributes = await element(by.id('status_label')).getAttributes();
console.log('Element is visible:', attributes.visible);
console.log('Element text:', attributes.text);

// Take element screenshot
const imagePath = await element(by.id('chart')).takeScreenshot('chart_state');

// Handle multiple elements
const multipleAttributes = await element(by.type('ListItem')).getAttributes();
if ('elements' in multipleAttributes) {
  console.log('Found', multipleAttributes.elements.length, 'list items');
}

Advanced Interactions

Coordinate-Based Actions

Direct coordinate manipulation for complex gestures.

/**
 * @deprecated Use tap() with point parameter instead
 * Tap at specific point within element
 */
async function tapAtPoint(point: Point2D): Promise<void>;

Error Handling

Common error scenarios and handling patterns:

// Element not found
try {
  await element(by.id('missing_element')).tap();
} catch (error) {
  console.log('Element not found:', error.message);
}

// Multiple elements without index
try {
  await element(by.type('Button')).tap(); // May fail if multiple buttons exist
} catch (error) {
  // Use atIndex() to specify which button
  await element(by.type('Button')).atIndex(0).tap();
}

// Element not visible
await waitFor(element(by.id('delayed_element')))
  .toBeVisible()
  .withTimeout(5000);
await element(by.id('delayed_element')).tap();

Types

interface Point2D {
  x: number;
  y: number;
}

interface ElementFrame {
  x: number;
  y: number;
  width: number;
  height: number;
}

interface NativeElement {
  // All the methods documented above
}

Install with Tessl CLI

npx tessl i tessl/npm-detox

docs

assertions.md

configuration.md

device-management.md

element-interaction.md

element-selection.md

index.md

synchronization.md

web-view-testing.md

tile.json