Mocking & monitoring Keyboard events with jest in react native

So this problem was way more complex that I could imagine to solve at start. Since what you want to test here is dismiss() and show() basically for a keyboard right?

Tests were executed with the following lib: @testing-library/react-native

So the events and listeners by documentation for 0.63 and 0.62 is https://reactnative.dev/docs/keyboard#docsNav

useEffect(() => {
   Keyboard.addListener("keyboardDidShow", _keyboardDidShow);
   Keyboard.addListener("keyboardDidHide", _keyboardDidHide);

   // cleanup function
   return () => {
       Keyboard.removeListener("keyboardDidShow", _keyboardDidShow);
       Keyboard.removeListener("keyboardDidHide", _keyboardDidHide);
   };
}, []);

const _keyboardDidShow = () => {
    alert("Keyboard Shown");
};

const _keyboardDidHide = () => {
    alert("Keyboard Hidden");
};

To get Jest to call the 2 functions _keyboardDidShow and _keyboardDidHide you will need to use Keyboard.emit('keyboardDidShow')

Example:

it('Test Keyboards keyboardDidShow is called', () => {
    const { getByTestId } = render(<Container />);
    act(() => {
        Keyboard.emit('keyboardDidShow', {});
    });
    const box = getByTestId('TEST');
    //Do here your expect clauses to check if something changed in your container
});

Not fully sure if this will ever help anyone. But that's how I solved this puzzle to figure out how to coverage the _keyboardDidShow and Hide

EDIT: For version 0.66 > now you need to do the following:

useEffect(() => {
  const kds = Keyboard.addListener("keyboardDidShow", _keyboardDidShow);
  const kdh = Keyboard.addListener("keyboardDidHide", _keyboardDidHide);

  // cleanup function
  return () => {
    kdh.remove();
    kds.remove();
  };
}, []);

const _keyboardDidShow = () => {
  alert("Keyboard Shown");
};

const _keyboardDidHide = () => {
  alert("Keyboard Hidden");
};

Since they changed the way to make it subscription events from now on

And for the tests change the Keyboard.emit to:

Keyboard._emitter.emit('keyboardDidShow', {});
Keyboard._emitter.emit('keyboardDidHide', {});

And you should have everything you need to make it work in jest :)


I was facing a similar issue with component that subscribed to Keyboard events

const MyComponent = () => {
    useEffect(() => {
        const listener = Keyboard.addListener('keyboardDidHide', () => {})

        return () => {
            listener.remove()
        }
    })

    return <View>...</View>
}

I was able to test Keyboard.addListener with following test and also test that listener.remove is being called on component unmount

import renderer from 'react-test-renderer'

const mockListener = {
    remove: jest.fn(),
}
const originalAddListener = Keyboard.addListener
const mockAddListener = jest.fn().mockReturnValue(mockListener)

describe('<MyComponent />', () => {
    beforeAll(() => {
        Keyboard.addListener = mockAddListener
    })
    beforeEach(() => {
        mockAddListener.mockClear()
        mockListener.remove.mockClear()
    })
    afterAll(() => {
        Keyboard.addListener = originalAddListener
    })
    it('should subscribe to KeyboardDidClose event', () => {
        renderer.create(<MyComponent />)
        expect(Keyboard.addListener).toHaveBeenCalled()
    })

    it('should call listener.remove on unmount', () => {
        const component = renderer.create(
            < MyComponent />,
        )
        component.unmount()
        expect(mockListener.remove).toHaveBeenCalled()
    })
})