{"id":4463,"url":"https://github.com/rumax/react-native-PixelsCatcher","last_synced_at":"2025-08-04T01:32:31.397Z","repository":{"id":38185438,"uuid":"164686958","full_name":"rumax/react-native-PixelsCatcher","owner":"rumax","description":":eyes: Library for UI snapshot testing of React Native","archived":false,"fork":false,"pushed_at":"2024-11-10T05:40:06.000Z","size":12696,"stargazers_count":116,"open_issues_count":7,"forks_count":10,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-06-30T09:44:37.250Z","etag":null,"topics":["react-native","screenshot","testing","ui","view"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rumax.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-01-08T16:13:56.000Z","updated_at":"2024-04-05T15:23:41.000Z","dependencies_parsed_at":"2023-01-17T18:00:43.112Z","dependency_job_id":"d31a8f4b-c1bf-4ea5-8407-84f56ab5afc4","html_url":"https://github.com/rumax/react-native-PixelsCatcher","commit_stats":{"total_commits":170,"total_committers":4,"mean_commits":42.5,"dds":0.4529411764705882,"last_synced_commit":"c682f1caa5aa3bcb2c5a506a47007e4375b11a0d"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rumax/react-native-PixelsCatcher","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumax%2Freact-native-PixelsCatcher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumax%2Freact-native-PixelsCatcher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumax%2Freact-native-PixelsCatcher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumax%2Freact-native-PixelsCatcher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rumax","download_url":"https://codeload.github.com/rumax/react-native-PixelsCatcher/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rumax%2Freact-native-PixelsCatcher/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268356088,"owners_count":24237380,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["react-native","screenshot","testing","ui","view"],"created_at":"2024-01-05T20:17:13.363Z","updated_at":"2025-08-04T01:32:30.757Z","avatar_url":"https://github.com/rumax.png","language":"TypeScript","readme":"# pixels-catcher\n\n[![npm](https://img.shields.io/npm/l/express.svg)](https://github.com/rumax/react-native-PixelsCatcher)\n[![npm version](https://badge.fury.io/js/pixels-catcher.svg)](https://badge.fury.io/js/pixels-catcher)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)\n[![Build Status](https://tfsprodweu6.visualstudio.com/A94b22b53-118f-4630-a94a-6d5bd33d6515/react-native-PixelsCatcher/_apis/build/status/rumax.react-native-PixelsCatcher?branchName=master)](https://tfsprodweu6.visualstudio.com/A94b22b53-118f-4630-a94a-6d5bd33d6515/react-native-PixelsCatcher/_build/latest?definitionId=1\u0026branchName=master)\n\nLibrary for testing React Native UI components and screens\n\n## Getting started\n\n### Install and link\n\n    $ npm install pixels-catcher --save-dev\n\nor\n\n    $ yarn add pixels-catcher\n\nThe library depends on\n[react-native-save-view](https://www.npmjs.com/package/react-native-save-view)\nwhich is used to convert `View` to base64 data and has native implementation.\nStarting from [RN 0.60](https://github.com/facebook/react-native/releases/tag/v0.60.0) there is no need to link - [Native Modules are now Autolinked](https://facebook.github.io/react-native/blog/2019/07/03/version-60), otherwise check\n[official react native documentation](https://facebook.github.io/react-native/docs/linking-libraries-ios).\n\n*Note: [react-native-save-view](https://www.npmjs.com/package/react-native-save-view) can be added to devDependencies of you project, otherwise auto-linking may not work. Check the version in [package.json](https://github.com/rumax/react-native-PixelsCatcher/blob/master/package.json#L31)*\n\n### Create test\n\nCreate new entry file, for example, `indexSnapshot`, and import\n`registerSnapshot`, `runSnapshots` and `Snapshot` from `pixels-catcher`:\n\n```\nimport {\n  registerSnapshot,\n  runSnapshots,\n  Snapshot,\n} from 'pixels-catcher';\n```\n\nAfter that create the snapshot component, which should extend `Snapshot` and\nimplement `static snapshotName` and `renderContent` method. Be sure that your\ncomponent can accept `collapsable` property, otherwise React can make an\noptimization and drop the view. The implementation can be:\n\n```\nclass AppSnapshot extends Snapshot {\n  static snapshotName = 'AppSnapshot';\n\n  renderContent() {\n    return (\u003cApp /\u003e);\n  }\n}\n```\n\nafter that register `AppSnapshot` component:\n\n```\nregisterSnapshot(AppSnapshot);\n```\n\nand trigger `runSnapshots` which will register the application component and\nrun all snapshots:\n\n```\nrunSnapshots(PUT_YOUR_APP_NAME_HERE);\n```\n\nSnapshots testing will be started as soon as the application is started.\n\nEach `Snapshot` gets `onReady` property that is triggered after all\ninteractions ([InteractionManager](https://reactnative.dev/docs/interactionmanager)) are completed. In case if it is not enough, which can be some network requests, etc., it is possible to do:\n\n  * register animations by creating an interaction 'handle' and clearing it upon completion\n  * override `componentDidMount` of the `Snapshot` and call `onReady` whenever you need it. `WebViewTest` in [demo](https://github.com/rumax/react-native-PixelsCatcher/blob/master/demo/indexSnapshot.js) project for more details\n\n### React Native Navigation support\n\nRegister your component and `Navigation.setRoot` using `registerComponent`\nproperty in `runSnapshots`:\n\n```\nrunSnapshots(appName, {\n  registerComponent: snapshot =\u003e {\n    Navigation.registerComponent(appName, () =\u003e snapshot)\n\n    Navigation.events().registerAppLaunchedListener(async () =\u003e {\n      Navigation.setRoot({\n        root: {\n          stack: {\n            children: [\n              {\n                component: {\n                  name: appName,\n                },\n              },\n            ],\n          },\n        },\n      })\n    })\n  },\n})\n```\n\n### React Navigation support\n\nIn case if some children components use [useNavigation hooks](https://reactnavigation.org/docs/use-navigation/), it might be necessary to use `NavigationContainer`. To do that use `getRootElement` config, which is available in the config.\n\nExample:\n\n```\nimport { NavigationContainer } from '@react-navigation/native';\nimport { createStackNavigator } from '@react-navigation/stack';\n\nconst Stack = createStackNavigator();\n\nfunction getRootElement(SnapshotsContainer) {\n  const RootElement = ({children}) =\u003e (\n    \u003cNavigationContainer\u003e\n      \u003cStack.Navigator\u003e\n        \u003cStack.Screen\n          name=\"SnapshotsContainer\"\n          options={{ headerShown: false, title: '' }}\n          component={SnapshotsContainer} /\u003e\n      \u003c/Stack.Navigator\u003e\n    \u003c/NavigationContainer\u003e\n  )\n  return RootElement;\n}\n\nrunSnapshots(appName, { baseUrl, getRootElement });\n```\n\n### Configuration\n\nThere are two options to define config:\n\n  - Using `pixels-catcher.json` file in the root of the project\n  - Using `package.json` file with new property `PixelsCatcher`\n\nAnd both of these two options should describe the configuration according to the\nfollowing format:\n\n```\nPixelsCatcher: {\n  PLATFORM: {\n    ...SHARED_CONFIGURATION,\n    CONFIGURATION: {\n      ...CONFIGURATION_SPECIFIC\n    }\n  },\n  logLevel: number,\n  timeout: number,\n  canStopDevice: boolean\n}\n```\n\nwhere\n\n  - `PLATFORM` can be `android` or `ios`\n  - `CONFIGURATION` is a configuration with the following properties:\n    * `activityName` - (*Android only*) Activity name, example: com.demo.MainActivity.\n    * `appFile` - (*Optional*) Path to apk file on android or app folder on iOS,\n      example: ./app/build/outputs/apk/debug/app-debug.apk\n    * `deviceName` - Device name, for example emulator: Nexus_5X or iOS:\n      iPhone 8 Plus\n    * `deviceParams` - (*Optional*) Array of emulator params like -no-audio,\n      -no-snapshot, -no-window, etc.\n    * `physicalDevice` - (*Optional*) Boolean value that indicates if real device should be used (*iOS devices are not supported yet*)\n    * `packageName` -\n        **Android** package name, example: *com.demo**.\n        **iOS** bundle identifier, example: *org.reactjs.native.example.demo*\n    * `snapshotsPath` - Path to snapshots, example: ./snapshotsImages\n    * `port` - Server port. Default value is `3000`\n    * `locale` - Locale to be used, for example `uk-UA`, `nl-NL`, etc. At this moment supported only on iOS simulators. ([Pull request welcome](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) for android implementation)\n  - `SHARED_CONFIGURATION`. In case more that one configurations exists, shared parameters can be moved here.\n  - `logLevel` - log levels: `e`, `w`, `i`, `d`, `v`. This corresponds to ERROR, WARN, INFO, DEBUG, VERBOSE\n  - `timeout` - tests timeout, with default value 2500ms. If timeout is reached, tests will fail automatically\n  - `canStopDevice` [Optional] Boolean parameter that allows to stop device (used to restart simulator/emulator). If set to false, the runner will start a new simulator/emulator if none is started. If a simulator/emulator is already started, it will be used for tests. The runner will also stop the device after tests. **If set to \"false\" it is possible that wrong device will be used!**. Default value is `true`.\n\nExample for `package.json` configuration (or check\n[demo](https://github.com/rumax/PixelsCatcher/tree/master/demo) project):\n\n```\n\"PixelsCatcher\": {\n  \"android\": {\n    \"activityName\": \"MainActivity\",\n    \"deviceName\": \"Nexus_5X\",\n    \"packageName\": \"com.rumax.pixelscatcher.testapp\",\n    \"snapshotsPath\": \"./snapshotsImages\",\n    \"debug\": {\n      \"deviceParams\": [\"-no-audio\", \"-no-snapshot\"],\n      \"appFile\": \"./android/app/build/outputs/apk/debug/app-debug.apk\"\n    },\n    \"release\": {\n      \"deviceParams\": [\"-no-audio\", \"-no-snapshot\", \"-no-window\"],\n      \"appFile\": \"./android/app/build/outputs/apk/debug/app-debug.apk\"\n    }\n  },\n  \"ios\": {\n    \"deviceName\": \"iPhone 8 Plus\",\n    \"packageName\": \"org.reactjs.native.example.testApp\",\n    \"snapshotsPath\": \"./snapshotsImagesIOS\",\n    \"dev\": {},\n    \"debug\": {\n      \"appFile\": \"./ios/build/Build/Products/Debug-iphonesimulator/testApp.app\"\n    }\n  }\n}\n```\n\n### Run android\n\n*To run android emulator, [emulator command](https://developer.android.com/studio/run/emulator-commandline) is used. It has to be defined in the system PATH or an `ANDROID_EMULATOR` system variable can be used to specify it. If none is defined, it will try to fallback to `~/Library/Android/sdk/emulator/emulator` on mac*\n\nThere are two options to run UI snapshots:\n\n  1) Using the generated `apk` file, provided via the `appFile`. In this case\n     pixels-catcher will open android emulator, install `apk` file, execute all\n     the tests and will provide a report at the end. This scenario can be used\n     to integrate the screenshot testing with CI.\n\n  2) In cases `appFile` is not defined, the development mode will be used. This\n     means that only the server will be started and the application should be\n     started manually. This scenario can be used to debug snapshots, create\n     new reference images, etc.\n\nTo run tests execute the following command:\n\n```\n$ ./node_modules/.bin/pixels-catcher android debug\n```\n\n#### Generating APK file\n\nBy default the `index.android.js` file is used which refer to your application.\nTo fix it, in `android/app/build.gradle` add the following config:\n\n```\nproject.ext.react = [\n    entryFile: System.getProperty(\"entryFile\") ?: \"index.js\",\n    bundleInDebug: System.getProperty(\"bundleInDebug\") ? System.getProperty(\"bundleInDebug\").toBoolean() : false\n]\n```\n\nAnd generate the `apk`:\n\n```\ncd android \u0026\u0026 ./gradlew assembleDebug -DentryFile=\"indexSnapshot.js\"\n```\n\n### Run iOS\n\nSame as android there are two options to run UI snapshots:\n\n  1) Using the generated app, provided via the `appFile`. In this case\n     pixels-catcher will open iOS simulator, install `app`, execute all\n     the tests and will provide a report at the end. This scenario can be used\n     to integrate the screenshot testing with CI.\n\n  2) In cases `appFile` is not defined, the development mode will be used. This\n     means that only the server will be started and the application should be\n     started manually. This scenario can be used to debug snapshots, create new\n     reference images, etc.\n\nTo run tests execute the following command:\n\n```\n$ ./node_modules/.bin/pixels-catcher ios debug\n```\n\n#### Generating iOS app\n\nTo make a valid app you will need to do the following actions:\n\n  - Set the `FORCE_BUNDLING` environment variable, which is required to generate\n    a bundle file\n  - Set `RCT_NO_LAUNCH_PACKAGER` to ignore the packager\n  - Use different entry file which includes only snapshots or some flag that\n    will switch your app to \"testing\" mode\n\nYou can also check the `demo` project and check the required changes.\n\n### Run device\n\nWhile android emulator or iOS simulator is able to work with localhost with default values `http://10.0.2.2:3000` for android and `http://127.0.0.1:3000` for iOS, using the real device will require connecting to the server by real IP. To make it possible, `pixels-catcher` allows to define it using the `baseUrl` property that is passed to the `runSnapshots` method:\n\n```\nconst baseUrl = 'http://127.0.0.1:3000';\n\n// Snapshots implementation\n\nrunSnapshots(appName, { baseUrl });\n```\n\n### JUnit test report\n\nEach run of tests produces a JUnit test report that is generated and available in `junit.xml` file. For integrating it with Azure DevOps, follow the [Azure DevOps and React Native UI testing](https://itnext.io/azure-devops-and-react-native-ui-testing-8144ba1a9eb) article that describes how to automate iOS testing or [Azure DevOps and React Native UI testing part 2 - Android](https://itnext.io/azure-devops-and-react-native-ui-testing-part-2-android-580c44d8c7ec) for Android.\n\nJUnit test report does not specify attachments, but to upload attachments to test report use third parameter `azureAttachments`. This can be done with azure task:\n\n```\nscript: ./node_modules/.bin/pixels-catcher ios debug azureAttachments\n  condition: failed()\n  env:\n    SYSTEM_ACCESSTOKEN: $(System.AccessToken)\n  workingDirectory: '$(Build.SourcesDirectory)/demo'\n  displayName: 'Upload screenshots'\n```\n\nthat has to be executed after `PublishTestResults@2` task. In this case the `pixels-catcher` will be started with the same parameters that were used for tests and will upload attachments for filed tests.\n\n![Azure DevOps Test Results](https://raw.githubusercontent.com/rumax/react-native-PixelsCatcher/master/res/testResults.png)\n\n### Device logs\n\nEach run of the tests collects logs for iOS (`ios_logs.log`) or android (`android_logs.log`) that can be used for further analysis of failed tests.\n## Demo\nCheck the [demo](https://github.com/rumax/PixelsCatcher/tree/master/demo) which\nincludes an example how the snapshots can be done and also has some useful\nscripts that can be used to integrate with CI.\n\nLog report:\n\n```bash\n==\u003e All tests completed: \u003c==\n┌─────────┬──────────────────────────────┬──────────┬───────┬────────────┬────────────────────────────────────┐\n│ (index) │             name             │  status  │ time  │ renderTime │              failure               │\n├─────────┼──────────────────────────────┼──────────┼───────┼────────────┼────────────────────────────────────┤\n│    0    │        'AppSnapshot'         │ 'PASSED' │ 0.909 │    0.13    │                 ''                 │\n│    1    │ 'AppSnapshotWithWrongRefImg' │ 'FAILED' │ 1.116 │   0.054    │ 'Files mismatch with 5088 pixels'  │\n│    2    │       'someComponent'        │ 'PASSED' │ 0.684 │   0.019    │                 ''                 │\n│    3    │        'WebViewTest'         │ 'FAILED' │ 3.123 │   2.697    │ 'Files mismatch with 19930 pixels' │\n│    4    │        'longContent'         │ 'PASSED' │ 0.793 │   0.017    │                 ''                 │\n└─────────┴──────────────────────────────┴──────────┴───────┴────────────┴────────────────────────────────────┘\n\n==\u003e Summary: \u003c==\n┌─────────┬───────────────────┬────────────────────────┐\n│ (index) │         0         │           1            │\n├─────────┼───────────────────┼────────────────────────┤\n│    0    │   'Total tests'   │           5            │\n│    1    │  'Passed tests'   │           3            │\n│    2    │  'Skipped tests'  │           0            │\n│    3    │  'Failed tests'   │           2            │\n│    4    │ 'Min render time' │  '17ms (longContent)'  │\n│    5    │ 'Max render time' │ '2697ms (WebViewTest)' │\n└─────────┴───────────────────┴────────────────────────┘\n==\u003e Failed tests: \u003c==\n┌─────────┬──────────────────────────────┐\n│ (index) │            Values            │\n├─────────┼──────────────────────────────┤\n│    0    │ 'AppSnapshotWithWrongRefImg' │\n│    1    │        'WebViewTest'         │\n└─────────┴──────────────────────────────┘\n```\n\njunit report:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003ctestsuites name=\"UI tests for ios/iPhone 8 Plus\" tests=\"4\" skipped=\"0\" errors=\"0\" failures=\"0\" time=\"4261\" \u003e\n  \u003ctestsuite name=\"UI tests for ios/iPhone 8 Plus\" tests=\"4\" skipped=\"0\" errors=\"0\" failures=\"0\" time=\"4261\" \u003e\n    \u003ctestcase classname=\"AppSnapshot\" name=\"AppSnapshot\" time=\"989\"\u003e\n    \u003c/testcase\u003e\n    \u003ctestcase classname=\"someComponent\" name=\"someComponent\" time=\"738\"\u003e\n    \u003c/testcase\u003e\n    \u003ctestcase classname=\"WebViewTest\" name=\"WebViewTest\" time=\"1678\"\u003e\n    \u003c/testcase\u003e\n    \u003ctestcase classname=\"longContent\" name=\"longContent\" time=\"856\"\u003e\n    \u003c/testcase\u003e\n  \u003c/testsuites\u003e\n\u003c/testsuites\u003e\n```\n\nAzure Devops integration result\n\n![Azure Devops](https://raw.githubusercontent.com/rumax/react-native-PixelsCatcher/master/res/azureDevops.png)\n\n## License\n\n[MIT](https://opensource.org/licenses/MIT)\n\n## Author\n\n  - [rumax](https://github.com/rumax)\n\n### Other information\n\n  - If you think that something is missing or would like to propose new feature,\n  please, discuss it with the author\n  - Please, ⭐️ the project. This gives the confidence that you like it and a\n  great job was done by publishing and supporting it 🤩\n","funding_links":[],"categories":["Components"],"sub_categories":["Utils \u0026 Infra"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frumax%2Freact-native-PixelsCatcher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frumax%2Freact-native-PixelsCatcher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frumax%2Freact-native-PixelsCatcher/lists"}