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

@unrulysystems/rn-playwright-driver

v0.8.1

Published

Playwright-compatible E2E test driver for React Native

Downloads

326

Readme

rn-playwright-driver

Playwright-compatible E2E test driver for React Native apps.

@unrulysystems/rn-playwright-driver runs in Node.js, attaches to a Hermes runtime through Metro's Chrome DevTools Protocol endpoint, and exposes a Playwright-style device fixture for app evaluation, locators, screenshots, lifecycle helpers, and pointer input.

Install

bun add @unrulysystems/rn-playwright-driver \
  @unrulysystems/rn-driver-view-tree \
  @unrulysystems/rn-driver-screenshot \
  @unrulysystems/rn-driver-lifecycle

Install Playwright in the test workspace:

bun add -d @playwright/test

For companion-backed OS-level touch input, install the platform packages:

bun add -d @unrulysystems/rn-playwright-driver-instrumentation-companion \
  @unrulysystems/rn-playwright-driver-xctest-companion

App Harness

Import the harness from the app entry used for E2E/dev builds:

import '@unrulysystems/rn-playwright-driver/harness/dev'

Do not include the harness in production app entries. Use a dev-only entry, an E2E-specific entry file, or another build-time guard so production builds do not install global.__RN_DRIVER__.

Basic Test

import { expect, test } from '@unrulysystems/rn-playwright-driver/test'

test('can tap by testID', async ({ device }) => {
  await device.getByTestId('increment-button').tap()
  await expect(device.getByTestId('count')).toHaveText('1')
})

Touch Backends

The default touch selection is companion-first and fail-closed:

  • iOS uses the XCTest companion (RN_TOUCH_BACKEND=xctest).
  • Android uses the instrumentation companion (RN_TOUCH_BACKEND=instrumentation).
  • Lower-fidelity native-module and cli backends are available only when explicitly selected.

See the companion package READMEs for platform launch steps:

  • @unrulysystems/rn-playwright-driver-instrumentation-companion
  • @unrulysystems/rn-playwright-driver-xctest-companion

Device File I/O

device.files reads and writes the running app's sandbox from the host, so a test can assert the real artifact a feature produces (a CSV/PDF export, a cache, a persisted snapshot) end to end — not just that a button is visible.

// Read a file the app wrote and assert its bytes.
const csv = await device.files.pull('observation_1.csv', { root: 'document' })
expect(csv.toString('utf8')).toContain('Interval,Actor,Engaged')

// Push a fixture (host path or Buffer) into the sandbox.
await device.files.push('./fixtures/seed.json', 'seed.json', { root: 'document' })

pull resolves with a Buffer; every failure is a typed FileIoError with a code (NOT_FOUND | UNAVAILABLE | UNSUPPORTED | TRANSPORT_FAILED | TOO_LARGE) — never a silent empty buffer.

Roots (default document) mirror expo-file-system, so one call points at the app-written file on both platforms:

| Root | iOS (<container>) | Android (/data/data/<pkg>) | | ---------- | ------------------- | ---------------------------- | | document | Documents/ | files/ | | cache | Library/Caches/ | cache/ | | data | container root | app-home root | | absolute | unsupported* | verbatim device path |

Platform support:

| Target | Transport | Status | | ---------------- | -------------------------------- | ---------------------------- | | iOS simulator | xcrun simctl get_app_container | supported | | iOS device | xcrun devicectl device copy | provisional (see below) | | Android emulator | adb … run-as <pkg> | supported (debuggable build) | | Android device | adb … run-as <pkg> | pending hardware verify |

  • *absolute is not supported on iOS (simulator or device) and rejects UNSUPPORTED: device.files is app-sandbox-scoped, and on the simulator an absolute path would resolve to a raw host path. Use it only on Android.
  • On Android, absolute is an intentional escape hatch: the path is passed to run-as <pkg> verbatim, so its reach is exactly whatever the app UID can access — no path-prefix restriction. That is the app-private tree (/data/data/<pkg>/…, the same sandbox the named roots resolve into, minus the fixed prefix) plus any UID-accessible path such as the app's own /proc/self/…. It cannot read another app's sandbox or root/privileged files (the kernel enforces the UID boundary), and — because run-as uses the app's internal-storage mount namespace — external storage is NOT reachable: /sdcard/…, including the app-scoped /sdcard/Android/data/<pkg>/…, returns Permission denied and fails closed as TRANSPORT_FAILED (verified by an E2E). The path is test-author-controlled, so this is a deliberate capability, not an injection surface. .. is rejected so the touched path stays legible, and a path containing shell metacharacters (quotes, backtick, $, backslash, or a newline) is rejected UNSUPPORTED before adb runs — the path is interpolated into a device-side run-as … sh -c command, so it must stay a plain path. Pass a full path you intend; the standard document/cache/data roots are the sandbox-relative alternative.
  • Android requires a debuggable build (run-as); iOS requires a development-signed app — both hold for E2E builds.
  • The iOS-device transport is unit-verified but provisional pending a real-hardware walkthrough. The verified E2E paths are the iOS simulator and the Android emulator; physical Android shares the emulator's run-as transport but its hardware walkthrough is still pending.

Targeting (bundle id, udid, package, adb serial) is supplied automatically by the rn-driver runner via RN_APP_BUNDLE_ID / RN_SIM_UDID / RN_IOS_TARGET_KIND / RN_APP_PACKAGE (ANDROID_SERIAL); set DeviceOptions.target explicitly for a direct createDevice(). When your target carries a complete file-I/O identity (iOS udid + bundleId, or Android serial + packageName) and more than one runtime is connected to the same Metro, also pass a CDP selector (deviceName or pageIndex) — Metro exposes no UDID, so createDevice fails closed (Ambiguous CDP target) rather than risk attaching evaluate() to a different app than device.files reads. A lone udid/serial (e.g. ANDROID_SERIAL set only for touch/adb, no app id) is not a file pin — device.files would be UNAVAILABLE anyway — so it never forces a selector. With a single runtime (the usual runner flow) no selector is needed.

Example E2E Gates

The repo example app owns complete companion-backed scripts:

cd examples/basic-app
nub run test:e2e:android
nub run test:e2e:ios

Requirements

  • Node.js 18+
  • React Native app running Hermes
  • Metro debug endpoint reachable at RN_METRO_URL or http://localhost:8081
  • Expo Modules API for native modules

Full documentation lives in the repository README: https://github.com/unrulysystems/rn-playwright-driver#readme