callstack / react-native-testing-library

🦉 Simple and complete React Native testing utilities that encourage good testing practices.
https://callstack.github.io/react-native-testing-library/
MIT License
3.08k stars 273 forks source link

Test react-native switch #518

Closed maximilianoforlenza closed 4 years ago

maximilianoforlenza commented 4 years ago

I'm using the switch component from react-native and I'm trying to test the toggle change. I'm using fireEvent.press but is not working. It returns No handler function found for event: "press"

Could anyone help me?

Versions:

   @testing-library/react-native: 7.0.1 
    react: 16.11.0 
    react-native: 0.62.2 
    react-test-renderer: 16.11.0 

This is my code:

 <Switch
                    testID="switch"
                    onValueChange={onToggleSwitch}
                    value={value}
                    thumbColor={value ? AppColors.Orange : AppColors.Gray2}
                />
 describe('when the switch is pressed', () => {
        beforeEach(() => {
            const { getByTestId } = getComponent();
            const switchComponent = getByTestId('switch');
            fireEvent.press(switchComponent);
        });

        it('should fire `props.onToggleSwitch`', () => {
            expect(props.onToggleSwitch).toHaveBeenCalledTimes(1);
        });
    });

Thanks!

thymikee commented 4 years ago

There's no "onPress" on Switch. There's "onValueChange" so please use that as a value for fireEvent() fn

maximilianoforlenza commented 4 years ago

@thymikee thanks for your reply. I'm trying to use onValueChange instead of press but it throw an error _reactNative.fireEvent.onValueChange is not a function

thymikee commented 4 years ago

Please read the documentation on how to use fireEvent in a generic form.

NicholasIoanJones commented 3 years ago

Please read the documentation on how to use fireEvent in a generic form.

Could you be more specific please? I've tried many forms based on the examples in the documentation but without success Using the example: fireEvent(getByPlaceholderText('my placeholder'), 'blur'); I tried fireEvent(switchComponent, 'valueChange', true); and fireEvent(switchComponent, 'onValueChange', true);

but get No handler function found for event: "valueChange" No handler function found for event: "onValueChange" Also tried many other combinations without success

NicholasIoanJones commented 3 years ago

I solved this with fresh eyes this morning and wanted to share in case it helps somebody else.

The main problem was my mistake - I was getting the wrong component from the render

For clarity on using the generic form, my working code looks like this:

        const switchComponent = component.getByTestId(`form-switch-${switchProps.basicProps.itemDef.id}`);
        fireEvent(switchComponent, 'valueChange', true);
malik-coachhub commented 2 years ago

I still Have the same problem when try to test Switch Component. Tried as suggested and get the same errors as here. any workaround or a clear solution?

vinipachecov commented 1 year ago

Anyone still trying this in 2023:

import { SwitchChangeEvent } from 'react-native'
describe('switch', () => {
   it('test switch change', () => {
     const switchComponent = getByTestId('my-switch-test-id')

      act(() => {
      // import SwitchChangeEvent interface  from react-native package to help you autocomplete the values
        fireEvent(switchComponent, 'onChange', { nativeEvent: { value: true } } as SwitchChangeEvent) 
      })
   })
 })
henriquecostadev commented 1 year ago

Hey @vinipachecov for me worked changing 'onChange' for 'onValueChange'.

rodrigodiasf1984 commented 6 months ago

I'm facing the same problem: This is my component:

<Switch testID="name-switch-active" value={isNameReplaced} onValueChange={value => setIsNameReplaced(value)} trackColor={{ false: colors['shape_gray_2'], true: colors['primary'], }} thumbColor="white" ios_backgroundColor="transparent" disabled={isLoading} />

this is my test suite: ` import React, { useState as useStateMock } from 'react'; import { act, fireEvent, render } from '@testing-library/react-native'; import { useNavigation } from '@react-navigation/native'; import { SwitchChangeEvent } from 'react-native'; import ConfirmScanMobbeelModal from '..'; import { useNavigationMocks } from '../../../../mocks/navigation-mocks'; import * as useUploadPhoto from '../../../hooks/useUploadPhoto';

(useNavigation as jest.Mock).mockReturnValue(useNavigationMocks);
const setIsLoading = jest.fn();
const setIsConfirmScanMobeelModalVisible = jest.fn();
const setIsNameReplaced = jest.fn();

jest.mock('react', () => ({
  ...jest.requireActual('react'),
  useState: jest.fn(),
}));

jest.mock('../../../hooks/useUploadPhoto');

const useUploadPhotoReturn = {
  handleUploadPhoto: jest.fn(),
  isLoading: false,
  setIsLoading: setIsLoading,
};

jest.spyOn(useUploadPhoto, 'default').mockReturnValue(useUploadPhotoReturn);

describe('ConfirmScanMobbeelModal', () => {
  beforeEach(async () => {
    (useStateMock as jest.Mock).mockImplementation(init => [
      init,
      setIsLoading,
    ]);
    (useStateMock as jest.Mock).mockImplementation(init => [
      init,
      setIsConfirmScanMobeelModalVisible,
    ]);
  });

it('should enders correctly', () => {
  const { getByTestId } = render(
    <ConfirmScanMobbeelModal
      isConfirmScanMobbeelModalVisible
      setIsConfirmScanMobeelModalVisible={setIsConfirmScanMobeelModalVisible}
    />
  );
  const modal = getByTestId('confirm-scan-mobbeel-modal');
  expect(modal).toBeDefined();
});

it('should close the modal when the button overlay is pressed', () => {
  const { getByTestId } = render(
    <ConfirmScanMobbeelModal
      isConfirmScanMobbeelModalVisible
      setIsConfirmScanMobeelModalVisible={setIsConfirmScanMobeelModalVisible}
    />,
  );
  const buttonOverlay = getByTestId('confirm-scan-mobbeel-modal-overlay');
  fireEvent.press(buttonOverlay);
  expect(setIsConfirmScanMobeelModalVisible).toHaveBeenCalled();
});

it('it should active the name switch', async () => {
  const { getByTestId } = render(
    <ConfirmScanMobbeelModal
      isConfirmScanMobbeelModalVisible
      setIsConfirmScanMobeelModalVisible={setIsConfirmScanMobeelModalVisible}
    />,
  );
  const switchComponent = getByTestId('name-switch-active');
  act(() => {
    fireEvent(switchComponent, 'onValueChange', {
      nativeEvent: { value: true },
    } as SwitchChangeEvent);
  });

  expect(setIsNameReplaced).toHaveBeenCalled();
});

});`

this is the result when I'm running the tests:

src/components/ConfirmScanMobbeelModal/tests/ConfirmScanMobbeelModal.test.tsx ConfirmScanMobbeelModal ✓ should enders correctly (261 ms) ✓ should close the modal when the button overlay is pressed (19 ms) ✕ it should active the name switch (40 ms)

● ConfirmScanMobbeelModal › it should active the name switch

expect(jest.fn()).toHaveBeenCalled()

Expected number of calls: >= 1
Received number of calls:    0

  75 |     });
  76 |
> 77 |     expect(setIsNameReplaced).toHaveBeenCalled();
     |                               ^
  78 |   });
  79 | });
  80 |

  at Object.toHaveBeenCalled (src/components/ConfirmScanMobbeelModal/__tests__/ConfirmScanMobbeelModal.test.tsx:77:31)
  at asyncGeneratorStep (node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:24)
  at _next (node_modules/@babel/runtime/helpers/asyncToGenerator.js:22:9)
  at node_modules/@babel/runtime/helpers/asyncToGenerator.js:27:7
  at tryCallTwo (node_modules/promise/lib/core.js:45:5)
  at doResolve (node_modules/promise/lib/core.js:200:13)
  at new Promise (node_modules/promise/lib/core.js:66:3)
  at Object.<anonymous> (node_modules/@babel/runtime/helpers/asyncToGenerator.js:19:12)

Any idea how to solve it?

mdjastrzebski commented 6 months ago

@rodrigodiasf1984 this issue is literally 4 years old. If you think you've found a bug in RNTL please create a new issue for that. Please make sure to attach a minimal repro for your issue, so that we can investigate it locally.