Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: callstack/react-native-testing-library
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v12.1.1
Choose a base ref
...
head repository: callstack/react-native-testing-library
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v12.1.2
Choose a head ref
  • 8 commits
  • 15 files changed
  • 3 contributors

Commits on May 2, 2023

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9100c21 View commit details
  2. docs: add config example for pnpm (#1400)

    * Add config example for pnpm 
    
    While using `pnpm` as package manger `transformIgnorePatterns` should refernce to package inside `.pnpm` folder.
    [source](https://jestjs.io/docs/configuration#transformignorepatterns-arraystring)
    
    * Update website/docs/ReactNavigation.md
    
    ---------
    
    Co-authored-by: Maciej Jastrzebski <mdjastrzebski@gmail.com>
    yjose and mdjastrzebski authored May 2, 2023
    Copy the full SHA
    74acd58 View commit details
  3. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    01af9e7 View commit details

Commits on May 3, 2023

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    72e3133 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    674b2f9 View commit details

Commits on May 4, 2023

  1. fix: non-editable wrapped TextInput events (#1385)

    Co-authored-by: Maciej Jastrzebski <mdjastrzebski@gmail.com>
    TMaszko and mdjastrzebski authored May 4, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    a110fd8 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8003a76 View commit details
  3. v12.1.2

    mdjastrzebski committed May 4, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    a29a8c1 View commit details
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@testing-library/react-native",
"version": "12.1.1",
"version": "12.1.2",
"description": "Simple and complete React Native testing utilities that encourage good testing practices.",
"main": "build/index.js",
"types": "build/index.d.ts",
8 changes: 6 additions & 2 deletions src/__tests__/config.test.ts
Original file line number Diff line number Diff line change
@@ -35,9 +35,13 @@ test('resetToDefaults() resets config to defaults', () => {

test('resetToDefaults() resets internal config to defaults', () => {
configureInternal({
hostComponentNames: { text: 'A', textInput: 'A' },
hostComponentNames: { text: 'A', textInput: 'A', switch: 'A' },
});
expect(getConfig().hostComponentNames).toEqual({
text: 'A',
textInput: 'A',
switch: 'A',
});
expect(getConfig().hostComponentNames).toEqual({ text: 'A', textInput: 'A' });

resetToDefaults();
expect(getConfig().hostComponentNames).toBe(undefined);
154 changes: 154 additions & 0 deletions src/__tests__/fireEvent-textInput.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import React from 'react';
import { Text, TextInput, TextInputProps } from 'react-native';
import { render, fireEvent } from '..';

function WrappedTextInput(props: TextInputProps) {
return <TextInput {...props} />;
}

function DoubleWrappedTextInput(props: TextInputProps) {
return <WrappedTextInput {...props} />;
}

const layoutEvent = { nativeEvent: { layout: { width: 100, height: 100 } } };

test('should fire only non-touch-related events on non-editable TextInput', () => {
const onFocus = jest.fn();
const onChangeText = jest.fn();
const onSubmitEditing = jest.fn();
const onLayout = jest.fn();

const view = render(
<TextInput
editable={false}
testID="subject"
onFocus={onFocus}
onChangeText={onChangeText}
onSubmitEditing={onSubmitEditing}
onLayout={onLayout}
/>
);

const subject = view.getByTestId('subject');
fireEvent(subject, 'focus');
fireEvent.changeText(subject, 'Text');
fireEvent(subject, 'submitEditing', { nativeEvent: { text: 'Text' } });
fireEvent(subject, 'layout', layoutEvent);

expect(onFocus).not.toHaveBeenCalled();
expect(onChangeText).not.toHaveBeenCalled();
expect(onSubmitEditing).not.toHaveBeenCalled();
expect(onLayout).toHaveBeenCalledWith(layoutEvent);
});

test('should fire only non-touch-related events on non-editable TextInput with nested Text', () => {
const onFocus = jest.fn();
const onChangeText = jest.fn();
const onSubmitEditing = jest.fn();
const onLayout = jest.fn();

const view = render(
<TextInput
editable={false}
testID="subject"
onFocus={onFocus}
onChangeText={onChangeText}
onSubmitEditing={onSubmitEditing}
onLayout={onLayout}
>
<Text>Nested Text</Text>
</TextInput>
);

const subject = view.getByText('Nested Text');
fireEvent(subject, 'focus');
fireEvent(subject, 'onFocus');
fireEvent.changeText(subject, 'Text');
fireEvent(subject, 'submitEditing', { nativeEvent: { text: 'Text' } });
fireEvent(subject, 'onSubmitEditing', { nativeEvent: { text: 'Text' } });
fireEvent(subject, 'layout', layoutEvent);
fireEvent(subject, 'onLayout', layoutEvent);

expect(onFocus).not.toHaveBeenCalled();
expect(onChangeText).not.toHaveBeenCalled();
expect(onSubmitEditing).not.toHaveBeenCalled();
expect(onLayout).toHaveBeenCalledTimes(2);
expect(onLayout).toHaveBeenCalledWith(layoutEvent);
});

/**
* Historically there were problems with custom TextInput wrappers, as they
* could creat a hierarchy of three or more composite text input views with
* very similar event props.
*
* Typical hierarchy would be:
* - User composite TextInput
* - UI library composite TextInput
* - RN composite TextInput
* - RN host TextInput
*
* Previous implementation of fireEvent only checked `editable` prop for
* RN TextInputs, both host & composite but did not check on the UI library or
* user composite TextInput level, hence invoking the event handlers that
* should be blocked by `editable={false}` prop.
*/
test('should fire only non-touch-related events on non-editable wrapped TextInput', () => {
const onFocus = jest.fn();
const onChangeText = jest.fn();
const onSubmitEditing = jest.fn();
const onLayout = jest.fn();

const view = render(
<WrappedTextInput
editable={false}
testID="subject"
onFocus={onFocus}
onChangeText={onChangeText}
onSubmitEditing={onSubmitEditing}
onLayout={onLayout}
/>
);

const subject = view.getByTestId('subject');
fireEvent(subject, 'focus');
fireEvent.changeText(subject, 'Text');
fireEvent(subject, 'submitEditing', { nativeEvent: { text: 'Text' } });
fireEvent(subject, 'layout', layoutEvent);

expect(onFocus).not.toHaveBeenCalled();
expect(onChangeText).not.toHaveBeenCalled();
expect(onSubmitEditing).not.toHaveBeenCalled();
expect(onLayout).toHaveBeenCalledWith(layoutEvent);
});

/**
* Ditto testing for even deeper hierarchy of TextInput wrappers.
*/
test('should fire only non-touch-related events on non-editable double wrapped TextInput', () => {
const onFocus = jest.fn();
const onChangeText = jest.fn();
const onSubmitEditing = jest.fn();
const onLayout = jest.fn();

const view = render(
<DoubleWrappedTextInput
editable={false}
testID="subject"
onFocus={onFocus}
onChangeText={onChangeText}
onSubmitEditing={onSubmitEditing}
onLayout={onLayout}
/>
);

const subject = view.getByTestId('subject');
fireEvent(subject, 'focus');
fireEvent.changeText(subject, 'Text');
fireEvent(subject, 'submitEditing', { nativeEvent: { text: 'Text' } });
fireEvent(subject, 'layout', layoutEvent);

expect(onFocus).not.toHaveBeenCalled();
expect(onChangeText).not.toHaveBeenCalled();
expect(onSubmitEditing).not.toHaveBeenCalled();
expect(onLayout).toHaveBeenCalledWith(layoutEvent);
});
132 changes: 46 additions & 86 deletions src/__tests__/fireEvent.test.tsx
Original file line number Diff line number Diff line change
@@ -216,153 +216,113 @@ test('should not fire on disabled Pressable', () => {
expect(handlePress).not.toHaveBeenCalled();
});

test('should not fire on non-editable TextInput', () => {
const testID = 'my-text-input';
const onChangeTextMock = jest.fn();
const NEW_TEXT = 'New text';

const { getByTestId } = render(
<TextInput
editable={false}
testID={testID}
onChangeText={onChangeTextMock}
/>
);

fireEvent.changeText(getByTestId(testID), NEW_TEXT);
expect(onChangeTextMock).not.toHaveBeenCalled();
});

test('should not fire on non-editable TextInput with nested Text', () => {
const placeholder = 'Test placeholder';
const onChangeTextMock = jest.fn();
const NEW_TEXT = 'New text';

const { getByPlaceholderText } = render(
<View>
<TextInput
editable={false}
placeholder={placeholder}
onChangeText={onChangeTextMock}
>
<Text>Test text</Text>
</TextInput>
</View>
);

fireEvent.changeText(getByPlaceholderText(placeholder), NEW_TEXT);
expect(onChangeTextMock).not.toHaveBeenCalled();
});

test('should not fire on none pointerEvents View', () => {
const handlePress = jest.fn();

test('should not fire inside View with pointerEvents="none"', () => {
const onPress = jest.fn();
const screen = render(
<View pointerEvents="none">
<Pressable onPress={handlePress}>
<Pressable onPress={onPress}>
<Text>Trigger</Text>
</Pressable>
</View>
);

fireEvent.press(screen.getByText('Trigger'));
expect(handlePress).not.toHaveBeenCalled();
fireEvent(screen.getByText('Trigger'), 'onPress');
expect(onPress).not.toHaveBeenCalled();
});

test('should not fire on box-only pointerEvents View', () => {
const handlePress = jest.fn();

test('should not fire inside View with pointerEvents="box-only"', () => {
const onPress = jest.fn();
const screen = render(
<View pointerEvents="box-only">
<Pressable onPress={handlePress}>
<Pressable onPress={onPress}>
<Text>Trigger</Text>
</Pressable>
</View>
);

fireEvent.press(screen.getByText('Trigger'));
expect(handlePress).not.toHaveBeenCalled();
fireEvent(screen.getByText('Trigger'), 'onPress');
expect(onPress).not.toHaveBeenCalled();
});

test('should fire on box-none pointerEvents View', () => {
const handlePress = jest.fn();

test('should fire inside View with pointerEvents="box-none"', () => {
const onPress = jest.fn();
const screen = render(
<View pointerEvents="box-none">
<Pressable onPress={handlePress}>
<Pressable onPress={onPress}>
<Text>Trigger</Text>
</Pressable>
</View>
);

fireEvent.press(screen.getByText('Trigger'));
expect(handlePress).toHaveBeenCalled();
fireEvent(screen.getByText('Trigger'), 'onPress');
expect(onPress).toHaveBeenCalledTimes(2);
});

test('should fire on auto pointerEvents View', () => {
const handlePress = jest.fn();

test('should fire inside View with pointerEvents="auto"', () => {
const onPress = jest.fn();
const screen = render(
<View pointerEvents="auto">
<Pressable onPress={handlePress}>
<Pressable onPress={onPress}>
<Text>Trigger</Text>
</Pressable>
</View>
);

fireEvent.press(screen.getByText('Trigger'));
expect(handlePress).toHaveBeenCalled();
fireEvent(screen.getByText('Trigger'), 'onPress');
expect(onPress).toHaveBeenCalledTimes(2);
});

test('should not fire on box-only pointerEvents View with nested elements', () => {
const handlePress = jest.fn();

test('should not fire deeply inside View with pointerEvents="box-only"', () => {
const onPress = jest.fn();
const screen = render(
<View pointerEvents="box-only">
<View>
<Pressable onPress={handlePress}>
<Pressable onPress={onPress}>
<Text>Trigger</Text>
</Pressable>
</View>
</View>
);

fireEvent.press(screen.getByText('Trigger'));
expect(handlePress).not.toHaveBeenCalled();
fireEvent(screen.getByText('Trigger'), 'onPress');
expect(onPress).not.toHaveBeenCalled();
});

test('should fire non-pointer events on box-none pointerEvents View', () => {
const handleTouchStart = jest.fn();

test('should fire non-pointer events inside View with pointerEvents="box-none"', () => {
const onTouchStart = jest.fn();
const screen = render(
<View
pointerEvents="box-none"
onTouchStart={handleTouchStart}
testID="touch-start-view"
>
<Pressable onPress={() => {}}>
<Text>Trigger</Text>
</Pressable>
</View>
<View testID="view" pointerEvents="box-none" onTouchStart={onTouchStart} />
);

fireEvent(screen.getByTestId('touch-start-view'), 'touchStart');
expect(handleTouchStart).toHaveBeenCalled();
fireEvent(screen.getByTestId('view'), 'touchStart');
expect(onTouchStart).toHaveBeenCalled();
});

test('should fire non-touch events on box-none pointerEvents View', () => {
const handleLayout = jest.fn();
test('should fire non-touch events inside View with pointerEvents="box-none"', () => {
const onLayout = jest.fn();
const screen = render(
<View testID="view" pointerEvents="box-none" onLayout={onLayout} />
);

fireEvent(screen.getByTestId('view'), 'layout');
expect(onLayout).toHaveBeenCalled();
});

// This test if pointerEvents="box-only" on composite `Pressable` is blocking
// the 'press' event on host View rendered by pressable.
test('should fire on Pressable with pointerEvents="box-only', () => {
const onPress = jest.fn();
const screen = render(
<View pointerEvents="box-none" onLayout={handleLayout} testID="layout-view">
<Pressable onPress={() => {}}>
<Text>Trigger</Text>
</Pressable>
</View>
<Pressable testID="pressable" pointerEvents="box-only" onPress={onPress} />
);

fireEvent(screen.getByTestId('layout-view'), 'layout');
expect(handleLayout).toHaveBeenCalled();
fireEvent.press(screen.getByTestId('pressable'));
expect(onPress).toHaveBeenCalled();
});

test('should pass event up on disabled TouchableOpacity', () => {
Loading