Skip to content

Commit 185e51c

Browse files
chore: use TestInstance (#1901)
1 parent cd4f1ef commit 185e51c

87 files changed

Lines changed: 910 additions & 887 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

skills/react-native-testing/references/api-reference-v14.md

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Complete API reference for `@testing-library/react-native` v14.x (React 19+).
44

55
**Test renderer:** `test-renderer` (not `react-test-renderer`)
6-
**Element type:** `HostElement` (not `ReactTestInstance`)
6+
**Element type:** `TestInstance` (not `ReactTestInstance`)
77

88
## Table of Contents
99

@@ -106,8 +106,8 @@ let screen: {
106106
unmount(): Promise<void>; // async
107107
debug(options?: { message?: string; mapProps?: MapPropsFunction }): void;
108108
toJSON(): RendererJSON | null;
109-
container: HostElement; // safe root host element
110-
root: HostElement; // root host element
109+
container: TestInstance; // safe root host element
110+
root: TestInstance; // root host element
111111
};
112112
```
113113

@@ -132,14 +132,14 @@ Each query = **variant** + **predicate** (e.g., `getByRole` = `getBy` + `ByRole`
132132

133133
### Query Variants
134134

135-
| Variant | Assertion | Return Type | Async |
136-
| ------------- | ------------------ | ------------------------------------ | ----- |
137-
| `getBy*` | Exactly one match | `HostElement` (throws if 0 or >1) | No |
138-
| `getAllBy*` | At least one match | `HostElement[]` (throws if 0) | No |
139-
| `queryBy*` | Zero or one match | `HostElement \| null` (throws if >1) | No |
140-
| `queryAllBy*` | No assertion | `HostElement[]` (empty if 0) | No |
141-
| `findBy*` | Exactly one match | `Promise<HostElement>` | Yes |
142-
| `findAllBy*` | At least one match | `Promise<HostElement[]>` | Yes |
135+
| Variant | Assertion | Return Type | Async |
136+
| ------------- | ------------------ | ------------------------------------- | ----- |
137+
| `getBy*` | Exactly one match | `TestInstance` (throws if 0 or >1) | No |
138+
| `getAllBy*` | At least one match | `TestInstance[]` (throws if 0) | No |
139+
| `queryBy*` | Zero or one match | `TestInstance \| null` (throws if >1) | No |
140+
| `queryAllBy*` | No assertion | `TestInstance[]` (empty if 0) | No |
141+
| `findBy*` | Exactly one match | `Promise<TestInstance>` | Yes |
142+
| `findAllBy*` | At least one match | `Promise<TestInstance[]>` | Yes |
143143

144144
`findBy*` / `findAllBy*` accept optional `waitForOptions: { timeout?, interval?, onTimeout? }`.
145145

@@ -157,7 +157,7 @@ getByRole(role: TextMatch, options?: {
157157
expanded?: boolean;
158158
value?: { min?: number; max?: number; now?: number; text?: TextMatch };
159159
includeHiddenElements?: boolean;
160-
}): HostElement;
160+
}): TestInstance;
161161
```
162162

163163
Matches elements by `role` or `accessibilityRole`. Element must be an accessibility element:
@@ -177,47 +177,47 @@ screen.getByRole('slider', { value: { now: 50, min: 0, max: 100 } });
177177
#### `*ByLabelText`
178178

179179
```ts
180-
getByLabelText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;
180+
getByLabelText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): TestInstance;
181181
```
182182

183183
Matches by `aria-label`/`accessibilityLabel` or text content of element referenced by `aria-labelledby`/`accessibilityLabelledBy`.
184184

185185
#### `*ByPlaceholderText`
186186

187187
```ts
188-
getByPlaceholderText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;
188+
getByPlaceholderText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): TestInstance;
189189
```
190190

191191
Matches `TextInput` by `placeholder` prop.
192192

193193
#### `*ByText`
194194

195195
```ts
196-
getByText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;
196+
getByText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): TestInstance;
197197
```
198198

199199
Matches by text content. Joins `<Text>` siblings to find matches (like RN runtime).
200200

201201
#### `*ByDisplayValue`
202202

203203
```ts
204-
getByDisplayValue(value: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;
204+
getByDisplayValue(value: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): TestInstance;
205205
```
206206

207207
Matches `TextInput` by current display value.
208208

209209
#### `*ByHintText`
210210

211211
```ts
212-
getByHintText(hint: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;
212+
getByHintText(hint: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): TestInstance;
213213
```
214214

215215
Matches by `accessibilityHint` prop. Also available as `getByA11yHint` / `getByAccessibilityHint`.
216216

217217
#### `*ByTestId` (last resort)
218218

219219
```ts
220-
getByTestId(testId: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;
220+
getByTestId(testId: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): TestInstance;
221221
```
222222

223223
Matches by `testID` prop. Use only when other queries don't work.
@@ -326,7 +326,7 @@ Use when `userEvent` doesn't support the event or when triggering events on comp
326326

327327
```ts
328328
async function fireEvent(
329-
element: HostElement,
329+
instance: TestInstance,
330330
eventName: string,
331331
...data: unknown[]
332332
): Promise<void>;
@@ -361,7 +361,7 @@ Available automatically with any `@testing-library/react-native` import. No setu
361361
| Matcher | Signature | Description |
362362
| --------------------- | ------------------------------------------------------------- | --------------------------- |
363363
| `toHaveTextContent()` | `(text: string \| RegExp, options?: { exact?, normalizer? })` | Text content match |
364-
| `toContainElement()` | `(element: HostElement \| null)` | Contains child element |
364+
| `toContainElement()` | `(instance: TestInstance \| null)` | Contains child element |
365365
| `toBeEmptyElement()` || No children or text content |
366366

367367
### Element State
@@ -430,7 +430,7 @@ Waits until the queried element is removed. Element must be initially present.
430430
### `within`
431431

432432
```ts
433-
function within(element: HostElement): Queries;
433+
function within(instance: TestInstance): Queries;
434434
```
435435

436436
Scoped queries on a subtree. Useful for querying within a single `FlatList` item or a specific screen.
@@ -533,7 +533,7 @@ Note: `concurrentRoot` option is removed (always on). `unstable_validateStringsR
533533
### `isHiddenFromAccessibility`
534534

535535
```ts
536-
function isHiddenFromAccessibility(element: HostElement | null): boolean;
536+
function isHiddenFromAccessibility(instance: TestInstance | null): boolean;
537537
```
538538

539539
Also available as `isInaccessible()` alias.

src/__tests__/fire-event.test.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ describe('fireEvent.changeText', () => {
161161
const input = screen.getByTestId('input');
162162
await fireEvent.changeText(input, 'new text');
163163
expect(onChangeText).toHaveBeenCalledWith('new text');
164-
expect(nativeState.valueForElement.get(input)).toBe('new text');
164+
expect(nativeState.valueForInstance.get(input)).toBe('new text');
165165
});
166166

167167
test('does not fire on non-editable TextInput', async () => {
@@ -170,7 +170,7 @@ describe('fireEvent.changeText', () => {
170170
const input = screen.getByTestId('input');
171171
await fireEvent.changeText(input, 'new text');
172172
expect(onChangeText).not.toHaveBeenCalled();
173-
expect(nativeState.valueForElement.get(input)).toBeUndefined();
173+
expect(nativeState.valueForInstance.get(input)).toBeUndefined();
174174
});
175175
});
176176

@@ -286,7 +286,7 @@ describe('fireEvent.scroll', () => {
286286
const scrollView = screen.getByTestId('scroll');
287287
await fireEvent.scroll(scrollView, verticalScrollEvent);
288288
expect(onScroll.mock.calls[0][0]).toMatchObject(verticalScrollEvent);
289-
expect(nativeState.contentOffsetForElement.get(scrollView)).toEqual({ x: 0, y: 200 });
289+
expect(nativeState.contentOffsetForInstance.get(scrollView)).toEqual({ x: 0, y: 200 });
290290
});
291291

292292
test.each([
@@ -301,7 +301,7 @@ describe('fireEvent.scroll', () => {
301301
const scrollView = screen.getByTestId('scroll');
302302
await fireEvent(scrollView, eventName, verticalScrollEvent);
303303
expect(handler).toHaveBeenCalledWith(verticalScrollEvent);
304-
expect(nativeState.contentOffsetForElement.get(scrollView)).toEqual({ x: 0, y: 200 });
304+
expect(nativeState.contentOffsetForInstance.get(scrollView)).toEqual({ x: 0, y: 200 });
305305
});
306306

307307
test('without contentOffset scrolls to (0, 0)', async () => {
@@ -316,7 +316,7 @@ describe('fireEvent.scroll', () => {
316316
expect(onScroll.mock.calls[0][0]).toMatchObject({
317317
nativeEvent: { contentOffset: { x: 0, y: 0 } },
318318
});
319-
expect(nativeState.contentOffsetForElement.get(scrollView)).toEqual({ x: 0, y: 0 });
319+
expect(nativeState.contentOffsetForInstance.get(scrollView)).toEqual({ x: 0, y: 0 });
320320
});
321321

322322
test('with non-finite contentOffset values uses 0', async () => {
@@ -331,7 +331,7 @@ describe('fireEvent.scroll', () => {
331331
nativeEvent: { contentOffset: { y: Infinity } },
332332
});
333333
expect(onScroll).toHaveBeenCalled();
334-
expect(nativeState.contentOffsetForElement.get(scrollView)).toEqual({ x: 0, y: 0 });
334+
expect(nativeState.contentOffsetForInstance.get(scrollView)).toEqual({ x: 0, y: 0 });
335335
});
336336

337337
test('with horizontal scroll updates native state', async () => {
@@ -344,7 +344,7 @@ describe('fireEvent.scroll', () => {
344344
const scrollView = screen.getByTestId('scroll');
345345
await fireEvent.scroll(scrollView, horizontalScrollEvent);
346346
expect(onScroll.mock.calls[0][0]).toMatchObject(horizontalScrollEvent);
347-
expect(nativeState.contentOffsetForElement.get(scrollView)).toEqual({ x: 50, y: 0 });
347+
expect(nativeState.contentOffsetForInstance.get(scrollView)).toEqual({ x: 50, y: 0 });
348348
});
349349

350350
test('without contentOffset via fireEvent() does not update native state', async () => {
@@ -357,7 +357,7 @@ describe('fireEvent.scroll', () => {
357357
const scrollView = screen.getByTestId('scroll');
358358
await fireEvent(scrollView, 'scroll', { nativeEvent: {} });
359359
expect(onScroll).toHaveBeenCalled();
360-
expect(nativeState.contentOffsetForElement.get(scrollView)).toBeUndefined();
360+
expect(nativeState.contentOffsetForInstance.get(scrollView)).toBeUndefined();
361361
});
362362

363363
test('with non-finite x contentOffset value uses 0', async () => {
@@ -372,7 +372,7 @@ describe('fireEvent.scroll', () => {
372372
nativeEvent: { contentOffset: { x: Infinity } },
373373
});
374374
expect(onScroll).toHaveBeenCalled();
375-
expect(nativeState.contentOffsetForElement.get(scrollView)).toEqual({ x: 0, y: 0 });
375+
expect(nativeState.contentOffsetForInstance.get(scrollView)).toEqual({ x: 0, y: 0 });
376376
});
377377
});
378378

src/fire-event.ts

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@ import type {
55
TextProps,
66
ViewProps,
77
} from 'react-native';
8-
import type { Fiber, HostElement } from 'test-renderer';
8+
import type { Fiber, TestInstance } from 'test-renderer';
99

1010
import { act } from './act';
1111
import { buildScrollEvent, buildTouchEvent } from './event-builder';
1212
import type { EventHandler } from './event-handler';
1313
import { getEventHandlerFromProps } from './event-handler';
14-
import { isElementMounted } from './helpers/component-tree';
14+
import { isInstanceMounted } from './helpers/component-tree';
1515
import { isHostScrollView, isHostTextInput } from './helpers/host-component-names';
1616
import { isPointerEventEnabled } from './helpers/pointer-events';
1717
import { isEditableTextInput } from './helpers/text-input';
1818
import { nativeState } from './native-state';
1919
import type { Point, StringWithAutocomplete } from './types';
2020

21-
function isTouchResponder(element: HostElement) {
22-
return Boolean(element.props.onStartShouldSetResponder) || isHostTextInput(element);
21+
function isTouchResponder(instance: TestInstance) {
22+
return Boolean(instance.props.onStartShouldSetResponder) || isHostTextInput(instance);
2323
}
2424

2525
/**
@@ -46,9 +46,9 @@ const textInputEventsIgnoringEditableProp = new Set([
4646
]);
4747

4848
function isEventEnabled(
49-
element: HostElement,
49+
instance: TestInstance,
5050
eventName: string,
51-
nearestTouchResponder?: HostElement,
51+
nearestTouchResponder?: TestInstance,
5252
) {
5353
if (nearestTouchResponder != null && isHostTextInput(nearestTouchResponder)) {
5454
return (
@@ -57,7 +57,7 @@ function isEventEnabled(
5757
);
5858
}
5959

60-
if (eventsAffectedByPointerEventsProp.has(eventName) && !isPointerEventEnabled(element)) {
60+
if (eventsAffectedByPointerEventsProp.has(eventName) && !isPointerEventEnabled(instance)) {
6161
return false;
6262
}
6363

@@ -71,24 +71,24 @@ function isEventEnabled(
7171
}
7272

7373
function findEventHandler(
74-
element: HostElement,
74+
instance: TestInstance,
7575
eventName: string,
76-
nearestTouchResponder?: HostElement,
76+
nearestTouchResponder?: TestInstance,
7777
): EventHandler | null {
78-
const touchResponder = isTouchResponder(element) ? element : nearestTouchResponder;
78+
const touchResponder = isTouchResponder(instance) ? instance : nearestTouchResponder;
7979

8080
const handler =
81-
getEventHandlerFromProps(element.props, eventName, { loose: true }) ??
82-
findEventHandlerFromFiber(element.unstable_fiber, eventName);
83-
if (handler && isEventEnabled(element, eventName, touchResponder)) {
81+
getEventHandlerFromProps(instance.props, eventName, { loose: true }) ??
82+
findEventHandlerFromFiber(instance.unstable_fiber, eventName);
83+
if (handler && isEventEnabled(instance, eventName, touchResponder)) {
8484
return handler;
8585
}
8686

87-
if (element.parent === null) {
87+
if (instance.parent === null) {
8888
return null;
8989
}
9090

91-
return findEventHandler(element.parent, eventName, touchResponder);
91+
return findEventHandler(instance.parent, eventName, touchResponder);
9292
}
9393

9494
function findEventHandlerFromFiber(fiber: Fiber | null, eventName: string): EventHandler | null {
@@ -123,14 +123,14 @@ type EventName = StringWithAutocomplete<
123123
| EventNameExtractor<ScrollViewProps>
124124
>;
125125

126-
async function fireEvent(element: HostElement, eventName: EventName, ...data: unknown[]) {
127-
if (!isElementMounted(element)) {
126+
async function fireEvent(instance: TestInstance, eventName: EventName, ...data: unknown[]) {
127+
if (!isInstanceMounted(instance)) {
128128
return;
129129
}
130130

131-
setNativeStateIfNeeded(element, eventName, data[0]);
131+
setNativeStateIfNeeded(instance, eventName, data[0]);
132132

133-
const handler = findEventHandler(element, eventName);
133+
const handler = findEventHandler(instance, eventName);
134134
if (!handler) {
135135
return;
136136
}
@@ -145,25 +145,25 @@ async function fireEvent(element: HostElement, eventName: EventName, ...data: un
145145

146146
type EventProps = Record<string, unknown>;
147147

148-
fireEvent.changeText = async (element: HostElement, text: string) =>
149-
await fireEvent(element, 'changeText', text);
148+
fireEvent.changeText = async (instance: TestInstance, text: string) =>
149+
await fireEvent(instance, 'changeText', text);
150150

151-
fireEvent.press = async (element: HostElement, eventProps?: EventProps) => {
151+
fireEvent.press = async (instance: TestInstance, eventProps?: EventProps) => {
152152
const event = buildTouchEvent();
153153
if (eventProps) {
154154
mergeEventProps(event, eventProps);
155155
}
156156

157-
await fireEvent(element, 'press', event);
157+
await fireEvent(instance, 'press', event);
158158
};
159159

160-
fireEvent.scroll = async (element: HostElement, eventProps?: EventProps) => {
160+
fireEvent.scroll = async (instance: TestInstance, eventProps?: EventProps) => {
161161
const event = buildScrollEvent();
162162
if (eventProps) {
163163
mergeEventProps(event, eventProps);
164164
}
165165

166-
await fireEvent(element, 'scroll', event);
166+
await fireEvent(instance, 'scroll', event);
167167
};
168168

169169
export { fireEvent };
@@ -176,15 +176,15 @@ const scrollEventNames = new Set([
176176
'momentumScrollEnd',
177177
]);
178178

179-
function setNativeStateIfNeeded(element: HostElement, eventName: string, value: unknown) {
180-
if (eventName === 'changeText' && typeof value === 'string' && isEditableTextInput(element)) {
181-
nativeState.valueForElement.set(element, value);
179+
function setNativeStateIfNeeded(instance: TestInstance, eventName: string, value: unknown) {
180+
if (eventName === 'changeText' && typeof value === 'string' && isEditableTextInput(instance)) {
181+
nativeState.valueForInstance.set(instance, value);
182182
}
183183

184-
if (scrollEventNames.has(eventName) && isHostScrollView(element)) {
184+
if (scrollEventNames.has(eventName) && isHostScrollView(instance)) {
185185
const contentOffset = tryGetContentOffset(value);
186186
if (contentOffset) {
187-
nativeState.contentOffsetForElement.set(element, contentOffset);
187+
nativeState.contentOffsetForInstance.set(instance, contentOffset);
188188
}
189189
}
190190
}

0 commit comments

Comments
 (0)