Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
registry-url: 'https://registry.npmjs.org'
cache: 'npm'
- run: npm ci
- run: npm test
- run: npm run test:coverage
- run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ jobs:
cd "$WORKSPACE"
npm link @percy/cli
npx percy --version
- run: npm test
- run: npm run test:coverage
14 changes: 14 additions & 0 deletions .github/workflows/typecheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Typecheck
on: push
jobs:
typecheck:
name: Typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run test:types
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
6 changes: 5 additions & 1 deletion .nycrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@
"include": ["index.js", "percy/**/*.js"],
"exclude": ["test/**", "**/*.test.js"],
"reporter": ["text", "html", "lcov"],
"check-coverage": false
"check-coverage": true,
"branches": 100,
"lines": 100,
"functions": 100,
"statements": 100
}
135 changes: 93 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,115 +1,166 @@
# @percy/detox

[![npm](https://img.shields.io/npm/v/@percy/detox.svg)](https://www.npmjs.com/package/@percy/detox)
[![Test](https://github.com/percy/percy-detox/actions/workflows/test.yml/badge.svg)](https://github.com/percy/percy-detox/actions/workflows/test.yml)

Percy visual testing SDK for [Detox](https://wix.github.io/Detox/) — React Native E2E.
[Percy](https://percy.io) visual testing SDK for [Detox](https://wix.github.io/Detox/) — the gray-box E2E framework for React Native apps.

## Install
## Quick start

```sh
npm install --save-dev @percy/cli @percy/detox
```

Requires **Node ≥ 18** and **Detox ≥ 20.32.0**.

## Usage
Add a screenshot call to any Detox test:

```js
const { device, element, by } = require('detox');
const percyScreenshot = require('@percy/detox');

describe('Home flow', () => {
it('renders home', async () => {
await percyScreenshot(device, 'Home');
});

it('captures hero', async () => {
await percyScreenshot(element(by.id('hero')), 'Hero');
describe('Login flow', () => {
it('renders the welcome screen', async () => {
await expect(element(by.id('welcome'))).toBeVisible();
await percyScreenshot(device, 'Welcome screen');
});
});
```

Run tests under Percy CLI's `app:exec`:
Run under Percy CLI's `app:exec` with an **App-type** project token (`app_...`):

```sh
export PERCY_TOKEN=<your project token>
percy app:exec -- detox test -c ios.sim.debug
export PERCY_TOKEN=<app_...>
npx percy app:exec -- npx detox test -c android.emu.debug
```

A working example app is available at [percy/example-percy-detox](https://github.com/percy/example-percy-detox).

## Requirements

- Node ≥ 18
- Detox ≥ 20.32.0
- An App-type Percy project (token starts with `app_`)

## Supported execution targets

| Target | Status |
|---|---|
| Android — local emulator | ✅ Fully supported |
| Android — local USB-attached real device | ✅ Same local driver as emulator |
| iOS — local simulator | ✅ Supported |
| iOS — local real device | ⚠️ Works in principle; uncommon |
| Android — BrowserStack App Automate (cloud) | ⏳ Wired end-to-end, blocked on BS server-side `takeScreenshot` |
| iOS — BrowserStack App Automate (cloud) | ❌ Detox does not support BS iOS ([wix/Detox#4694](https://github.com/wix/Detox/issues/4694)) |

## API

```js
await percyScreenshot(deviceOrElement, name, options)
await percyScreenshot(deviceOrElement, name, options?)
```

- **`deviceOrElement`** — the Detox `device` object, or an element handle from `element(by.id(...))`.
- **`name`** — unique snapshot name.
- **`options`** (all optional):
- **`name`** — unique snapshot name within the build.
- **`options`** *(all optional)*:

| Option | Type | Notes |
| Option | Type | Description |
|---|---|---|
| `fullscreen` | `boolean` | Crop status/nav bars off the comparison. |
| `fullPage` | `boolean` | Accepted for API parity; produces a single-tile capture (warns once). |
| `fullscreen` | `boolean` | Treat the screenshot as full-screen (no chrome cropping). |
| `fullPage` | `boolean` | Accepted for API parity; produces a single-tile capture (warns once per process). |
| `deviceName` | `string` | Override auto-detected device name. |
| `osVersion` | `string` | Override auto-detected OS version. |
| `orientation` | `'portrait' \| 'landscape'` | Override; defaults to `'portrait'`. |
| `statusBarHeight` | `number` | Pixel override; default `0`. |
| `navigationBarHeight` | `number` | Pixel override; default `0`. |
| `ignoreRegionIds` | `string[]` | Mask elements found via `by.id(testID)`. |
| `ignoreRegionElements` | `DetoxElement[]` | Mask pre-matched element handles. |
| `customIgnoreRegions` | `{top,bottom,left,right}[]` | Mask rectangles (coordinates in points). |
| `ignoreRegionIds` | `string[]` | Mask elements located via `by.id(testID)`. |
| `ignoreRegionElements` | `DetoxElement[]` | Mask pre-matched Detox element handles. |
| `customIgnoreRegions` | `{top,bottom,left,right}[]` | Mask rectangles in **point** coordinates. |
| `considerRegionIds` | `string[]` | Restrict diff to these elements. |
| `considerRegionElements` | `DetoxElement[]` | Restrict diff to these handles. |
| `customConsiderRegions` | `{top,bottom,left,right}[]` | Restrict diff to these rects (points). |
| `testCase` | `string` | Group snapshot in the Percy review UI. |
| `labels` | `string` | Comma-separated label chips. |
| `labels` | `string` | Comma-separated label chips for the snapshot. |
| `sync` | `boolean` | Block until the Percy build finalizes. |
| `getSessionId` | `() => string \| null \| Promise<string \| null>` | Resolve BrowserStack session ID for the POA debug link (see below). |
| `getSessionId` | `() => string \| null \| Promise<string \| null>` | Resolve BrowserStack session ID for the debug-URL link. |
| `getBuildId` | `() => string \| null \| Promise<string \| null>` | Resolve BrowserStack build ID. |

Coordinate space: **all region options accept point coordinates**. The SDK multiplies by the device scale factor. Do not pre-multiply.
**Coordinate space:** all region options accept **point** coordinates. The SDK multiplies by the device scale factor automatically. Do not pre-multiply.

## BrowserStack App Automate (Detox Android)
## Examples

BrowserStack runs Detox Android tests on real devices. Percy shows a debug-URL link to the BrowserStack session when both the session ID and build ID are available. Because BrowserStack does **not** auto-inject these into the test process, pass them via a callback — typically calling the BrowserStack REST API with your credentials:
### Element-level capture

```js
const fetch = require('node-fetch');
const hero = element(by.id('hero'));
await expect(hero).toBeVisible();
await percyScreenshot(hero, 'Hero element');
```

### Ignore dynamic regions

```js
await percyScreenshot(device, 'Dashboard', {
ignoreRegionIds: ['live-clock', 'unread-count'],
customIgnoreRegions: [{ top: 0, bottom: 100, left: 0, right: 414 }]
});
```

### Group snapshots by test case

```js
await percyScreenshot(device, 'Login screen', { testCase: 'auth-suite' });
```

## Configuration

| Environment variable | Effect |
|---|---|
| `PERCY_TOKEN` | Required. App-type Percy project token (starts with `app_`). |
| `PERCY_IGNORE_ERRORS` | If `'true'`, screenshot failures are logged and swallowed instead of failing the test. |
| `PERCY_LOGLEVEL` | `silent`, `info`, `debug`. Defaults to `info`. |
| `BROWSERSTACK_SESSION_ID` | Fallback for `getSessionId` callback. |
| `BROWSERSTACK_BUILD_ID` | Fallback for `getBuildId` callback. |

## BrowserStack App Automate

BrowserStack runs Detox Android tests on real devices. Percy shows a debug-URL link to the BrowserStack session when both session ID and build ID are available. Because BrowserStack does **not** auto-inject these into the Detox test process, pass them via callbacks:

```js
await percyScreenshot(device, 'Home', {
getSessionId: async () => {
// Your pipeline resolves this — for example, by calling
// https://api-cloud.browserstack.com/app-automate/detox/v2/android/sessions/{id}.json
return process.env.MY_BS_SESSION_ID || null;
},
getSessionId: async () => process.env.MY_BS_SESSION_ID || null,
getBuildId: async () => process.env.MY_BS_BUILD_ID || null
});
```

Fallback: `BROWSERSTACK_SESSION_ID` and `BROWSERSTACK_BUILD_ID` environment variables are read if no callback is provided. Returning explicit `null` from the callback suppresses the env fallback.
Returning explicit `null` from a callback suppresses the env-var fallback. iOS POA is **not supported** — Detox does not run on BrowserStack real iOS devices.

iOS POA is **not supported** — Detox does not run on BrowserStack real iOS devices.
> **Note (current BrowserStack limitation):** As of the latest `@browserstack/detox` cloud build, the cloud Android driver's `takeScreenshot` is a stub that returns an empty string. The `@percy/detox` SDK and example repo are wired end-to-end against BS cloud — the moment BrowserStack ships server-side screenshot capture, Percy builds will start producing on cloud devices with no SDK changes required.

## Migration from `@percy/appium-app`

| `@percy/appium-app` option | `@percy/detox` equivalent |
|---|---|
| `ignoreRegionXpaths` | **Rejected** — Detox has no XPath. Use `ignoreRegionIds`. |
| `ignoreRegionAccessibilityIds` | `ignoreRegionIds` (collapsed). |
| `ignoreRegionAccessibilityIds` | `ignoreRegionIds`. |
| `ignoreRegionAppiumElements` | `ignoreRegionElements`. |
| `considerRegionXpaths` | **Rejected** — use `considerRegionIds`. |
| `considerRegionAccessibilityIds` | `considerRegionIds`. |
| `considerRegionAppiumElements` | `considerRegionElements`. |
| `scrollableXpath`, `scrollableId`, `screenLengths`, `topScrollviewOffset`, `bottomScrollviewOffset`, `androidScrollAreaPercentage`, `scrollSpeed`, `iosOptimizedFullpage` | **Rejected** — fullPage scroll-and-stitch is not supported for Detox. |
| `scrollableXpath`, `scrollableId`, `screenLengths`, `topScrollviewOffset`, `bottomScrollviewOffset`, `androidScrollAreaPercentage`, `scrollSpeed`, `iosOptimizedFullpage` | **Rejected** — fullPage scroll-and-stitch is not supported on Detox. |
| `thTestCaseExecutionId` | **Rejected** — TestHub is not supported. |

Passing a rejected key throws `TypeError` with a migration hint — nothing is silently ignored.

## Known limitations

- `element.takeScreenshot` misses TextureView, GLSurfaceView, Skia canvases, and other hardware-accelerated content on Android ([wix/Detox#4489](https://github.com/wix/Detox/issues/4489)). Use `device.takeScreenshot` for surfaces backed by these views.
- `element.takeScreenshot` misses TextureView, GLSurfaceView, Skia canvases, and other hardware-accelerated content on Android ([wix/Detox#4489](https://github.com/wix/Detox/issues/4489)). Use `device.takeScreenshot` for views backed by these surfaces.
- iOS: `atIndex(n).getAttributes()` is silently ignored ([wix/Detox#4633](https://github.com/wix/Detox/issues/4633)). When multi-match occurs, `@percy/detox` uses the first result and logs a warning.
- fullPage is accepted but produces a single-tile capture — Detox has no server-side stitching channel (unlike BrowserStack's Appium executor).
- iOS scale factor is inferred from PNG width: `>=1080 → 3x`, otherwise `2x`. iPad retina (2x at 1536+) may be misclassified; override with `deviceName` + a manual `scaleFactor` override (planned).
- `fullPage` is accepted but produces a single-tile capture — Detox has no server-side stitching channel (unlike BrowserStack's Appium executor).
- iOS scale factor is inferred from PNG width: `>=1080 → 3x`, otherwise `2x`. iPad retina at 2x with width 1536+ may be misclassified; override with explicit options or open an issue.

## Resources

- [Example app + tests](https://github.com/percy/example-percy-detox)
- [Percy App Percy docs](https://www.browserstack.com/docs/percy/integrate/overview)
- [Detox docs](https://wix.github.io/Detox/)

## License

Expand Down
Loading
Loading