@react-native-harness/ui 1.0.0-alpha.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "HarnessUI"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => "13.0" }
14
+ s.source = { :git => "https://github.com/callstackincubator/react-native-harness.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
17
+ s.private_header_files = "ios/**/*.h"
18
+
19
+ install_modules_dependencies(s)
20
+ end
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Callstack
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,164 @@
1
+ ![harness-banner](https://react-native-harness.dev/harness-banner.jpg)
2
+
3
+ ### Native UI Testing Module for React Native Harness
4
+
5
+ [![mit licence][license-badge]][license]
6
+ [![npm downloads][npm-downloads-badge]][npm-downloads]
7
+ [![Chat][chat-badge]][chat]
8
+ [![PRs Welcome][prs-welcome-badge]][prs-welcome]
9
+
10
+ Native UI testing module for React Native Harness that provides view queries and touch simulation capabilities. This module enables finding UI elements and simulating user interactions in your React Native tests.
11
+
12
+ ## Features
13
+
14
+ - **View Queries**: Find elements by testID or accessibility label
15
+ - **Touch Simulation**: Simulate user presses and text input
16
+ - **Screenshot Capture**: Capture screenshots of the entire screen or specific elements
17
+ - **Debug-Only**: Automatically excluded from release builds, only available in debug builds
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install @react-native-harness/ui
23
+ # or
24
+ pnpm add @react-native-harness/ui
25
+ # or
26
+ yarn add @react-native-harness/ui
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ Import the UI testing utilities in your test files:
32
+
33
+ ```javascript
34
+ import { screen, userEvent } from '@react-native-harness/ui';
35
+
36
+ describe('My Component', () => {
37
+ it('should handle user interactions', async () => {
38
+ // Find elements on screen
39
+ const button = await screen.findByTestId('my-button');
40
+ const input = await screen.findByAccessibilityLabel('Username input');
41
+
42
+ // Simulate user interactions
43
+ await userEvent.type(input, 'testuser');
44
+ await userEvent.press(button);
45
+
46
+ // Take screenshots for debugging
47
+ const screenshot = await screen.screenshot();
48
+ });
49
+ });
50
+ ```
51
+
52
+ ## API
53
+
54
+ ### `screen`
55
+
56
+ Provides methods to query and interact with UI elements on screen.
57
+
58
+ #### `findByTestId(testId: string): Promise<ElementReference>`
59
+
60
+ Finds an element by its testID (accessibilityIdentifier on iOS, tag on Android).
61
+ Throws an error if no element is found.
62
+
63
+ #### `findAllByTestId(testId: string): Promise<ElementReference[]>`
64
+
65
+ Finds all elements by testID. Throws an error if no elements are found.
66
+
67
+ #### `queryByTestId(testId: string): ElementReference | null`
68
+
69
+ Queries for an element by testID without throwing. Returns null if not found.
70
+
71
+ #### `queryAllByTestId(testId: string): ElementReference[]`
72
+
73
+ Queries for all elements by testID without throwing. Returns an empty array if none found.
74
+
75
+ #### `findByAccessibilityLabel(label: string): Promise<ElementReference>`
76
+
77
+ Finds an element by its accessibility label. Throws an error if no element is found.
78
+
79
+ #### `findAllByAccessibilityLabel(label: string): Promise<ElementReference[]>`
80
+
81
+ Finds all elements by accessibility label. Throws an error if no elements are found.
82
+
83
+ #### `queryByAccessibilityLabel(label: string): ElementReference | null`
84
+
85
+ Queries for an element by accessibility label without throwing. Returns null if not found.
86
+
87
+ #### `queryAllByAccessibilityLabel(label: string): ElementReference[]`
88
+
89
+ Queries for all elements by accessibility label without throwing. Returns an empty array if none found.
90
+
91
+ #### `screenshot(element?: ElementReference): Promise<ScreenshotResult | null>`
92
+
93
+ Captures a screenshot of the entire app window or a specific element.
94
+ Returns a ScreenshotResult with PNG data, or null if capture fails.
95
+
96
+ ### `userEvent`
97
+
98
+ Provides methods to simulate user interactions.
99
+
100
+ #### `press(element: ElementReference): Promise<void>`
101
+
102
+ Simulates a press on the given element at its center point.
103
+
104
+ #### `pressAt(x: number, y: number): Promise<void>`
105
+
106
+ Simulates a press at the specified screen coordinates.
107
+
108
+ #### `type(element: ElementReference, text: string, options?: TypeOptions): Promise<void>`
109
+
110
+ Simulates typing text into a text input element. Focuses the element, types each character, and blurs the element.
111
+
112
+ **TypeOptions:**
113
+ - `skipPress?: boolean` - If true, pressIn and pressOut events will not be triggered
114
+ - `skipBlur?: boolean` - If true, endEditing and blur events will not be triggered
115
+ - `submitEditing?: boolean` - If true, submitEditing event will be triggered after typing
116
+
117
+ ## Types
118
+
119
+ ### `ElementReference`
120
+
121
+ Represents an element found on screen with its position and dimensions.
122
+
123
+ ```typescript
124
+ type ElementReference = {
125
+ x: number;
126
+ y: number;
127
+ width: number;
128
+ height: number;
129
+ // ... additional view info
130
+ };
131
+ ```
132
+
133
+ ### `ScreenshotResult`
134
+
135
+ Screenshot result containing PNG image data.
136
+
137
+ ```typescript
138
+ interface ScreenshotResult {
139
+ data: Uint8Array; // PNG image data
140
+ width: number; // Width in logical pixels
141
+ height: number; // Height in logical pixels
142
+ }
143
+ ```
144
+
145
+ ## Requirements
146
+
147
+ - React Native project with React Native Harness configured
148
+ - This module is only available in debug builds and is automatically excluded from release builds
149
+
150
+ ## Made with ❤️ at Callstack
151
+
152
+ `@react-native-harness/ui` is an open source project and will always remain free to use. If you think it's cool, please star it 🌟. [Callstack][callstack-readme-with-love] is a group of React and React Native geeks, contact us at [hello@callstack.com](mailto:hello@callstack.com) if you need any help with these or just want to say hi!
153
+
154
+ Like the project? ⚛️ [Join the team](https://callstack.com/careers/?utm_campaign=Senior_RN&utm_source=github&utm_medium=readme) who does amazing stuff for clients and drives React Native Open Source! 🔥
155
+
156
+ [callstack-readme-with-love]: https://callstack.com/?utm_source=github.com&utm_medium=referral&utm_campaign=react-native-harness&utm_term=readme-with-love
157
+ [license-badge]: https://img.shields.io/npm/l/@react-native-harness/ui?style=for-the-badge
158
+ [license]: https://github.com/callstackincubator/react-native-harness/blob/main/LICENSE
159
+ [npm-downloads-badge]: https://img.shields.io/npm/dm/@react-native-harness/ui?style=for-the-badge
160
+ [npm-downloads]: https://www.npmjs.com/package/@react-native-harness/ui
161
+ [prs-welcome-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge
162
+ [prs-welcome]: ../../CONTRIBUTING.md
163
+ [chat-badge]: https://img.shields.io/discord/426714625279524876.svg?style=for-the-badge
164
+ [chat]: https://discord.gg/xgGt7KAjxv
@@ -0,0 +1,77 @@
1
+ buildscript {
2
+ ext.getExtOrDefault = {name ->
3
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['HarnessUI_' + name]
4
+ }
5
+
6
+ repositories {
7
+ google()
8
+ mavenCentral()
9
+ }
10
+
11
+ dependencies {
12
+ classpath "com.android.tools.build:gradle:8.7.2"
13
+ // noinspection DifferentKotlinGradleVersion
14
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
15
+ }
16
+ }
17
+
18
+
19
+ apply plugin: "com.android.library"
20
+ apply plugin: "kotlin-android"
21
+
22
+ apply plugin: "com.facebook.react"
23
+
24
+ def getExtOrIntegerDefault(name) {
25
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["HarnessUI_" + name]).toInteger()
26
+ }
27
+
28
+ android {
29
+ namespace "com.harnessui"
30
+
31
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
32
+
33
+ defaultConfig {
34
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
35
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
36
+ }
37
+
38
+ buildFeatures {
39
+ buildConfig true
40
+ }
41
+
42
+ buildTypes {
43
+ release {
44
+ minifyEnabled false
45
+ }
46
+ }
47
+
48
+ lintOptions {
49
+ disable "GradleCompatible"
50
+ }
51
+
52
+ compileOptions {
53
+ sourceCompatibility JavaVersion.VERSION_1_8
54
+ targetCompatibility JavaVersion.VERSION_1_8
55
+ }
56
+
57
+ sourceSets {
58
+ main {
59
+ java.srcDirs += [
60
+ "generated/java",
61
+ "generated/jni"
62
+ ]
63
+ }
64
+ }
65
+ }
66
+
67
+ repositories {
68
+ mavenCentral()
69
+ google()
70
+ }
71
+
72
+ def kotlin_version = getExtOrDefault("kotlinVersion")
73
+
74
+ dependencies {
75
+ implementation "com.facebook.react:react-android"
76
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
77
+ }
@@ -0,0 +1,5 @@
1
+ HarnessUI_kotlinVersion=2.0.21
2
+ HarnessUI_minSdkVersion=24
3
+ HarnessUI_targetSdkVersion=34
4
+ HarnessUI_compileSdkVersion=35
5
+ HarnessUI_ndkVersion=27.1.12297006
@@ -0,0 +1,53 @@
1
+ package com.harnessui
2
+
3
+ import com.facebook.react.bridge.Promise
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.bridge.ReadableMap
6
+ import com.facebook.react.bridge.WritableArray
7
+ import com.facebook.react.bridge.WritableMap
8
+ import com.facebook.react.module.annotations.ReactModule
9
+
10
+ /**
11
+ * TurboModule for HarnessUI - provides UI testing capabilities.
12
+ * Includes touch simulation and view querying.
13
+ */
14
+ @ReactModule(name = HarnessUIModule.NAME)
15
+ class HarnessUIModule(reactContext: ReactApplicationContext) :
16
+ NativeHarnessUISpec(reactContext) {
17
+
18
+ companion object {
19
+ const val NAME = "HarnessUI"
20
+ }
21
+
22
+ private val helper = UIHelperImpl(reactContext)
23
+
24
+ override fun getName(): String = NAME
25
+
26
+ override fun simulatePress(x: Double, y: Double, promise: Promise) {
27
+ helper.simulatePress(x, y, promise)
28
+ }
29
+
30
+ override fun queryByTestId(testId: String): WritableMap? =
31
+ helper.queryByTestId(testId)
32
+
33
+ override fun queryByAccessibilityLabel(label: String): WritableMap? =
34
+ helper.queryByAccessibilityLabel(label)
35
+
36
+ override fun queryAllByTestId(testId: String): WritableArray =
37
+ helper.queryAllByTestId(testId)
38
+
39
+ override fun queryAllByAccessibilityLabel(label: String): WritableArray =
40
+ helper.queryAllByAccessibilityLabel(label)
41
+
42
+ override fun captureScreenshot(bounds: ReadableMap?, promise: Promise) {
43
+ helper.captureScreenshot(bounds, promise)
44
+ }
45
+
46
+ override fun typeChar(character: String, promise: Promise) {
47
+ helper.typeChar(character, promise)
48
+ }
49
+
50
+ override fun blur(options: ReadableMap, promise: Promise) {
51
+ helper.blur(options, promise)
52
+ }
53
+ }
@@ -0,0 +1,33 @@
1
+ package com.harnessui
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+ import java.util.HashMap
9
+
10
+ class HarnessUIPackage : BaseReactPackage() {
11
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
+ return if (name == HarnessUIModule.NAME) {
13
+ HarnessUIModule(reactContext)
14
+ } else {
15
+ null
16
+ }
17
+ }
18
+
19
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
20
+ return ReactModuleInfoProvider {
21
+ val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
22
+ moduleInfos[HarnessUIModule.NAME] = ReactModuleInfo(
23
+ HarnessUIModule.NAME,
24
+ HarnessUIModule.NAME,
25
+ false, // canOverrideExistingModule
26
+ false, // needsEagerInit
27
+ false, // isCxxModule
28
+ true // isTurboModule
29
+ )
30
+ moduleInfos
31
+ }
32
+ }
33
+ }