npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

droideer

v0.1.0

Published

The Puppeteer for Android - Control Android devices with familiar web automation syntax

Downloads

41

Readme

Droideer 🤖📱

Droideer Logo

Puppeteer-like API for Android automation - Control Android devices with familiar web automation syntax for testing, scraping, and automation

npm version License: MIT Node.js Android WIP


⚠️ Work In Progress: This repository is currently under active development. Features and APIs may change. Emulator support is in testing phase.

🚀 What is Droideer?

Droideer brings the beloved Puppeteer API to Android automation. Whether you're testing apps, scraping mobile data, or automating workflows, if you've ever automated web browsers, you'll feel right at home automating Android apps!

Perfect for:

  • 🧪 End-to-end testing of Android applications
  • 📊 Data scraping from mobile apps and services
  • 🤖 Workflow automation and repetitive task automation
  • 🔍 App behavior analysis and reverse engineering
  • 📱 Cross-platform testing across different Android devices

🔧 Android DevTools Companion

Note: For a visual UI inspector and debugging experience similar to Chrome DevTools, check out our companion repository: Droideer DevTools 🛠️

import { Droideer } from 'droideer';

const device = await Droideer.connect();
const page = await device.launch('com.instagram.android');

await page.type({ resourceId: 'login_username' }, 'john_doe');
await page.type({ resourceId: 'password' }, 'secret123');
await page.click({ text: 'Log In' });

await page.waitForSelector({ text: 'Home' });
await page.screenshot('logged-in.png');

✨ Features

Core Automation

  • 🎯 Puppeteer-like API - Familiar syntax for web developers
  • 🔍 Smart Element Finding - Multiple selector strategies (text, ID, class, contains)
  • 📱 Real Device Control - Direct ADB integration for authentic interactions
  • 🤹 Rich Gestures - Tap, swipe, scroll, long press, drag & drop
  • Auto-waiting - Built-in waits for UI stability and element appearance

Testing & Debugging

  • 📸 Screenshot Support - Visual debugging and test verification
  • 🔄 App Management - Launch, close, and manage Android applications
  • 🧩 Element Properties - Access all element attributes and properties
  • 📋 UI Hierarchy Inspection - Comprehensive element tree analysis

Advanced Features

  • 🌐 Network Monitoring - Track HTTP requests and API calls during automation
  • 🎯 Domain-Specific Filtering - Monitor only relevant network traffic
  • 📊 Response Capture - Extract JSON responses and API data
  • 🔧 Reverse Engineering - Analyze app behavior and API endpoints

Scraping Capabilities

  • 📱 Mobile App Scraping - Extract data from native Android applications
  • 🔄 Infinite Scroll Handling - Automatically handle pagination and lazy loading
  • 📊 Structured Data Extraction - Extract listings, properties, products, and more
  • 🎯 Element-Specific Targeting - Precise data extraction using resource IDs and selectors
  • 💾 Export Support - Save scraped data in JSON, CSV, or custom formats

📦 Installation

npm install droideer

Prerequisites

  1. ADB (Android Debug Bridge) installed and in your PATH
  2. Android device with USB debugging enabled or an emulator
  3. Node.js 16 or higher

Note: Emulator support is currently in testing phase. Physical devices are recommended for production use.

🎯 Quick Start

Some Examples can be checked in the Examples folder.

Basic App Automation

import { Droideer } from 'droideer';

// Connect to device
const device = await Droideer.connect();
const page = await device.launch('com.android.settings');

// Interact with UI
await page.click({ text: 'Network & internet' });
await page.waitForSelector({ text: 'Wi-Fi' });
await page.screenshot('wifi-settings.png');

await device.disconnect();

Data Scraping Example

import { Droideer } from 'droideer';

const device = await Droideer.connect();
const page = await device.launch('com.example.marketplace');

// Navigate to search
await page.click({ resourceId: 'search_button' });
await page.type({ resourceId: 'search_input' }, 'smartphones');
await page.pressKey(66); // Enter

// Scrape product listings with infinite scroll
const products = [];
let scrollCount = 0;
const maxScrolls = 20;

while (scrollCount < maxScrolls) {
    // Extract current page products
    const productElements = await page.$$({ resourceId: 'product_item' });

    for (const element of productElements) {
        const title = await element.getText();
        const price = await page.getText({ resourceId: 'product_price' });

        products.push({ title, price });
    }

    // Scroll for more products
    await page.scroll('down');
    await page.waitForTimeout(2000);
    scrollCount++;

    // Check if we've reached the end
    const noMoreItems = await page.$({ text: 'No more items' });
    if (noMoreItems) break;
}

console.log(`Scraped ${products.length} products`);

Network Monitoring

import { Droideer } from 'droideer';

const device = await Droideer.connect();

// Monitor network activity during app usage
const networkResults = await device.networkMonitor.monitorAction(async () => {
    const page = await device.launch('com.booking');

    await page.click({ text: 'Search' });
    await page.type({ resourceId: 'destination' }, 'Paris');
    await page.click({ text: 'Search hotels' });

    // Wait for API calls to complete
    await page.waitForTimeout(5000);
}, {
    targetDomains: ['booking.com', 'api.booking'],  // Only monitor Booking APIs
    captureResponses: true
});

console.log(`Captured ${networkResults.summary.totalRequests} network requests`);
console.log('API Endpoints:', networkResults.data.apiEndpoints);

📚 API Reference

Device

The AndroidDevice class represents a connected Android device.

// Connect to a device
const device = await Droideer.connect();

// Launch an app
const page = await device.launch('com.android.settings');

// Take a screenshot
await device.screenshot('device.png');

// Press hardware buttons
await device.back();
await device.home();

// Disconnect when done
await device.disconnect();

Page

The Page class represents the current UI view of an Android app.

// Find elements
const element = await page.$({ text: 'Settings' });
const elements = await page.$$({ className: 'android.widget.Button' });

// Convenience methods
const button = await page.findByResourceId('submit_button');
const textElement = await page.findByText('Welcome');

// Interact with UI
await page.click({ text: 'Next' });
await page.type({ resourceId: 'username_field' }, 'john_doe');
await page.scroll('down', 500);

// Wait for elements or conditions
await page.waitForSelector({ text: 'Welcome' });
await page.waitForNavigation();

Element

The AndroidElement class represents a UI element on the screen.

// Get element properties
const text = element.text;
const resourceId = element.resourceId;
const isEnabled = element.isEnabled;

// Interact with element
await element.click();
await element.type('Hello world');
await element.longPress();
await element.swipeLeft();

Selectors

Droideer supports multiple selector strategies for finding elements:

// By text
await page.$({ text: 'Login' });

// By partial text
await page.$({ contains: 'Log' });

// By resource ID
await page.$({ resourceId: 'com.example.app:id/username' });

// By class name
await page.$({ className: 'android.widget.EditText' });

// By content description
await page.$({ contentDesc: 'Profile picture' });

// Combined selectors
await page.$({
  className: 'android.widget.Button',
  text: 'Login',
  clickable: true
});

// XPath-like selectors
await page.$x('//android.widget.Button[@text="Submit"]');

Network Monitoring

// Start monitoring
await device.networkMonitor.startMonitoring({
    targetDomains: ['api.example.com'],
    captureResponses: true
});

// Perform actions...

// Stop and get results
const results = await device.networkMonitor.stopMonitoring();

🎭 Use Cases

1. End-to-End Testing

import { test, expect } from '@playwright/test';
import { Droideer } from 'droideer';

test('user can complete purchase flow', async () => {
    const device = await Droideer.connect();
    const page = await device.launch('com.example.shop');

    // Test complete user journey
    await page.click({ text: 'Shop Now' });
    await page.type({ resourceId: 'search' }, 'running shoes');
    await page.click({ text: 'Search' });

    const firstProduct = await page.$({ resourceId: 'product_item' });
    await firstProduct.click();

    await page.click({ text: 'Add to Cart' });
    await page.click({ resourceId: 'cart_button' });
    await page.click({ text: 'Checkout' });

    const confirmationText = await page.waitForSelector({ text: 'Order confirmed' });
    expect(confirmationText).toBeTruthy();
});

2. Data Scraping

// Scrape real estate listings
const device = await Droideer.connect();
const page = await device.launch('com.idealista.android');

const listings = [];
let hasMorePages = true;

while (hasMorePages) {
    const propertyElements = await page.$$({ resourceId: 'property_card' });

    for (const property of propertyElements) {
        const title = await property.getText();
        const price = await page.getText({ resourceId: 'property_price' });
        const location = await page.getText({ resourceId: 'property_location' });

        listings.push({ title, price, location });
    }

    // Try to go to next page
    const nextButton = await page.$({ text: 'Next' });
    if (nextButton) {
        await nextButton.click();
        await page.waitForTimeout(3000);
    } else {
        hasMorePages = false;
    }
}

console.log(`Scraped ${listings.length} property listings`);

3. API Reverse Engineering

// Monitor network traffic to understand app APIs
const device = await Droideer.connect();

const apiAnalysis = await device.networkMonitor.monitorAction(async () => {
    const page = await device.launch('com.airbnb.android');

    await page.type({ resourceId: 'search_location' }, 'New York');
    await page.click({ text: 'Search' });
    await page.waitForTimeout(5000);

    // Scroll to trigger pagination APIs
    for (let i = 0; i < 5; i++) {
        await page.scroll('down');
        await page.waitForTimeout(2000);
    }
}, {
    targetDomains: ['airbnb.com'],
    targetKeywords: ['api', 'search', 'listings'],
    captureResponses: true
});

console.log('Discovered API endpoints:', apiAnalysis.data.apiEndpoints);
console.log('JSON responses:', apiAnalysis.data.jsonResponses.length);

🧪 Testing

Droideer is perfect for end-to-end testing of Android applications. See TESTING.md for detailed testing guidelines.

import { Droideer } from 'droideer';
import { test, expect } from 'your-test-framework';

test('should login successfully', async () => {
  const device = await Droideer.connect();
  const page = await device.launch('com.example.app');

  await page.type({ resourceId: 'username' }, 'testuser');
  await page.type({ resourceId: 'password' }, 'password123');
  await page.click({ text: 'Login' });

  const welcomeElement = await page.waitForSelector({ text: 'Welcome' });
  expect(welcomeElement).not.toBeNull();

  await device.disconnect();
});

🚧 Development Status

This project is currently under active development. Here's what's working and what's planned:

✅ Working Features

  • Core element finding and interaction
  • Basic gestures and navigation
  • Screenshot capabilities
  • App management
  • Network monitoring (basic)
  • Real device support

📖 Documentation

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. See CONTRIBUTING.md for detailed guidelines.

Development Setup

git clone https://github.com/your-username/droideer.git
cd droideer
npm install
npm run build
npm test

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • Inspired by Puppeteer for the web
  • Built on top of Android Debug Bridge (ADB)
  • Thanks to the Android automation community

Built with ❤️ for Android automation, testing, and data extraction

Star ⭐ this repo if you find it useful!