Skip to content

Commit 5f81922

Browse files
committed
feat: basic text input state
1 parent 239e046 commit 5f81922

File tree

8 files changed

+63
-4
lines changed

8 files changed

+63
-4
lines changed

src/__tests__/fire-event.test.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
View,
1010
} from 'react-native';
1111
import { fireEvent, render, screen } from '..';
12+
import '../matchers/extend-expect';
1213

1314
type OnPressComponentProps = {
1415
onPress: () => void;
@@ -443,4 +444,14 @@ describe('native events', () => {
443444
fireEvent(screen.getByTestId('test-id'), 'onMomentumScrollEnd');
444445
expect(onMomentumScrollEndSpy).toHaveBeenCalled();
445446
});
447+
448+
it('sets native state value for unmanaged text inputs', () => {
449+
render(<TextInput testID="input" />);
450+
451+
const input = screen.getByTestId('input');
452+
expect(input).toHaveDisplayValue('');
453+
454+
fireEvent.changeText(input, 'abc');
455+
expect(input).toHaveDisplayValue('abc');
456+
});
446457
});

src/cleanup.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import * as React from 'react';
1+
import { clearNativeState } from './native-state';
22
import { clearRenderResult } from './screen';
33

4-
type CleanUpFunction = (nextElement?: React.ReactElement<any>) => void;
5-
let cleanupQueue = new Set<CleanUpFunction>();
4+
type CleanUpFunction = () => void;
5+
6+
const cleanupQueue = new Set<CleanUpFunction>();
67

78
export default function cleanup() {
9+
clearNativeState();
810
clearRenderResult();
11+
912
cleanupQueue.forEach((fn) => fn());
1013
cleanupQueue.clear();
1114
}

src/fire-event.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { isHostTextInput } from './helpers/host-component-names';
1212
import { isPointerEventEnabled } from './helpers/pointer-events';
1313
import { isTextInputEditable } from './helpers/text-input';
1414
import { StringWithAutocomplete } from './types';
15+
import { nativeState } from './native-state';
1516

1617
type EventHandler = (...args: unknown[]) => unknown;
1718

@@ -120,6 +121,10 @@ type EventName = StringWithAutocomplete<
120121
>;
121122

122123
function fireEvent(element: ReactTestInstance, eventName: EventName, ...data: unknown[]) {
124+
if (eventName === 'changeText' && isHostTextInput(element) && typeof data[0] === 'string') {
125+
nativeState?.elementValues.set(element, data[0]);
126+
}
127+
123128
const handler = findEventHandler(element, eventName);
124129
if (!handler) {
125130
return;

src/helpers/text-input.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ReactTestInstance } from 'react-test-renderer';
2+
import { nativeState } from '../native-state';
23
import { isHostTextInput } from './host-component-names';
34

45
export function isTextInputEditable(element: ReactTestInstance) {
@@ -14,5 +15,10 @@ export function getTextInputValue(element: ReactTestInstance) {
1415
throw new Error(`Element is not a "TextInput", but it has type "${element.type}".`);
1516
}
1617

17-
return element.props.value ?? element.props.defaultValue;
18+
return (
19+
element.props.value ??
20+
nativeState?.elementValues.get(element) ??
21+
element.props.defaultValue ??
22+
''
23+
);
1824
}

src/native-state.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { ReactTestInstance } from 'react-test-renderer';
2+
3+
export type NativeState = {
4+
elementValues: WeakMap<ReactTestInstance, string>;
5+
};
6+
7+
export let nativeState: NativeState | null = null;
8+
9+
export function initNativeState(): void {
10+
nativeState = {
11+
elementValues: new WeakMap(),
12+
};
13+
}
14+
15+
export function clearNativeState(): void {
16+
nativeState = null;
17+
}

src/render.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { validateStringsRenderedWithinText } from './helpers/string-validation';
1212
import { renderWithAct } from './render-act';
1313
import { setRenderResult } from './screen';
1414
import { getQueriesForElement } from './within';
15+
import { initNativeState } from './native-state';
1516

1617
export interface RenderOptions {
1718
wrapper?: React.ComponentType<any>;
@@ -127,6 +128,8 @@ function buildRenderResult(
127128
});
128129

129130
setRenderResult(result);
131+
initNativeState();
132+
130133
return result;
131134
}
132135

src/user-event/type/__tests__/type.test.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { TextInput, TextInputProps, View } from 'react-native';
33
import { createEventLogger, getEventsNames, lastEventPayload } from '../../../test-utils';
44
import { render, screen } from '../../..';
55
import { userEvent } from '../..';
6+
import '../../../matchers/extend-expect';
67

78
beforeEach(() => {
89
jest.useRealTimers();
@@ -372,4 +373,15 @@ describe('type()', () => {
372373
},
373374
});
374375
});
376+
377+
it('supports value of unmanaged text inputs', async () => {
378+
render(<TextInput testID="input" />);
379+
380+
const user = userEvent.setup();
381+
const input = screen.getByTestId('input');
382+
expect(input).toHaveDisplayValue('');
383+
384+
await user.type(input, 'abc');
385+
expect(input).toHaveDisplayValue('abc');
386+
});
375387
});

src/user-event/type/type.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ReactTestInstance } from 'react-test-renderer';
22
import { isHostTextInput } from '../../helpers/host-component-names';
3+
import { nativeState } from '../../native-state';
34
import { EventBuilder } from '../event-builder';
45
import { ErrorWithStack } from '../../helpers/errors';
56
import { isTextInputEditable } from '../../helpers/text-input';
@@ -96,6 +97,7 @@ export async function emitTypingEvents(
9697

9798
dispatchEvent(element, 'change', EventBuilder.TextInput.change(text));
9899
dispatchEvent(element, 'changeText', text);
100+
nativeState?.elementValues.set(element, text);
99101

100102
const selectionRange = {
101103
start: text.length,

0 commit comments

Comments
 (0)