react-native-actions-sheet icon indicating copy to clipboard operation
react-native-actions-sheet copied to clipboard

Cannot test this library with testing-library/react-native

Open dkulakov opened this issue 2 years ago • 25 comments

First off all thanks for this great library!

After I've switched to this library some of my tests are failing, specifically test cases which interact with ActionSheet. I need to render ActionSheet content in my tests, after some research I found this peace of code, these changes were added not so far in the commit what I guess to cause a problem. I'm not sure if it's possible to mock this value outside the library.

see more details

dkulakov avatar Sep 24 '22 00:09 dkulakov

Hi @dkulakov, what functionality are you trying to test exactly? I would recommend not mocking this library and just asserting that the show and hide functions are called. If you really want to, you could also add a snapshot. Here is an example:

import React from 'react';
import { fireEvent, render } from 'utils/test-utils/custom-render';
import LoginRegisterInfoSheet from '../login-register-info-sheet';
import { hideActionSheet, SheetManagerSheets } from 'interfaces/sheet-manager';

describe('LoginRegisterInfoSheet', () => {
  test('should render correctly', () => {
    const tree = render({
      ui: <LoginRegisterInfoSheet />,
    });

    expect(tree).toMatchSnapshot();
  });

  test('should call hideActionSheet() when pressed', () => {
    const tree = render({
      ui: <LoginRegisterInfoSheet />,
    });

    const closeActionSheetButton = tree.getByTestId('loginRegisterInfoSheetCloseButton');

    fireEvent(closeActionSheetButton, 'press');

    expect(hideActionSheet).toBeCalledWith({ id: SheetManagerSheets.loginRegisterInfo });
  });
});

The custom render import aligns with docs: https://testing-library.com/docs/react-native-testing-library/setup

Hi @johnny-mcfadden-dailypay , thanks for your reply!

Exactly, I don't want to mock this library, but the problem is that the value dimensions.height will be always 0 in the test environment, so it means you can't test your custom content inside the ActionSheet component.

dkulakov avatar Sep 27 '22 11:09 dkulakov

@dkulakov if you trigger the sheet open and wait for it async, does it still have 0 dimensions in height? In the example above, the close action sheet button i'm getting:

const closeActionSheetButton = tree.getByTestId('loginRegisterInfoSheetCloseButton');

is within the action sheet, as "custom content" as you've referred to. But I could grab any content inside the action sheet by test id and interact with it.

@johnny-mcfadden-dailypay as I can see dimensions.height changes inside onLayout event, but test environment renders components in nodejs environment which doesn't have any dimensions, that's why dimensions.height always will be 0.

What version do you use? The bug appears after 0.8.3

dkulakov avatar Sep 27 '22 12:09 dkulakov

Not seeing this issue. Even my snapshots have all the child obj nodes. I'm using the latest: 0.8.7. Are you ensuring you open the action sheet first before trying to assert elements? (sorry maybe a silly question)

@johnny-mcfadden-dailypay I've created reproducible example. Can you check it? It fails with the latest version, but if you install version 0.8.3, test works well.

Thanks in advance!

dkulakov avatar Sep 27 '22 19:09 dkulakov

@dkulakov in your example it doesn't look like you've called the action sheet open before trying to assert the combo change option was called, could you try that?

@johnny-mcfadden-dailypay here is I call the action sheet open

dkulakov avatar Sep 28 '22 11:09 dkulakov

Hey guys, I have a related problem and I thought this would be the best place to ask. I have a problem of finding the elements inside the ActionSheet in my test. Heres my component code:

const MyComponent = () => {

  const actionSheetRef = useRef<ActionSheetRef>(null);

  useEffect(() => {
    actionSheetRef.current?.show();
  }, []);

  return (
    <ActionSheet
      ref={actionSheetRef}
      ...
    >
      <View testID='viewTestId'>
      ...
      </View>
    </ActionSheet>
  )
}

If I render MyComponent in the test, it is unable to find 'viewTestId' with 'getById()' inside the ActionSheet.

It seems that 'findByText()' works though, but it does give me a reference error: ReferenceError: You are trying to import a file after the Jest environment has been torn down.

Heres the test:

describe('MyComponent', () => {

  it('Renders', () => {
    const component = render(
      <MyComponent onBack={jest.fn()} />
    );
      expect(component.findByText('Muokkaa tietoja')).toBeTruthy();
    })
});

The render() is a custom method:

const customRender = (ui : any) => {
  return render(ui, { wrapper: renderWithProviders });
};

Any suggestions? Thank you

Taloqq avatar Sep 29 '22 06:09 Taloqq

@Taloqq try to add waitFor like in this example

your test should like like, but don't forget to add async await waitFor(() => { component.findByText('Muokkaa tietoja'); });

dkulakov avatar Sep 29 '22 12:09 dkulakov

@dkulakov It still gives me the same referenceerror. Im able to fix the reference error with jest.useFakeTimers('legacy') but it still says A worker process has failed to exit gracefully and has been force exited. Anyhow, is it possible to use getByTestId() with this library?

Taloqq avatar Oct 02 '22 11:10 Taloqq

@Taloqq I'm using version 0.8.3 and my tests work fine, but on the latest version it doesn't, so try to downgrade your version to 0.8.3 and check your tests again.

dkulakov avatar Oct 02 '22 12:10 dkulakov

@dkulakov Hey I'm having a new issue. I have a pressable/button inside the sheet component, and when Im trying to press it, it fails with Cannot read properties of undefined (reading 'onPress') Have you had this issue? It doesnt matter if I find it by text or testid. await waitFor(() => fireEvent.press(component.findByTestId('taloButton'))); With getByTestId, it is unable to find it. Also if I just test expect(component.findByText('Talo')).toBeTruthy();, it is successful.

Taloqq avatar Oct 06 '22 09:10 Taloqq

@Taloqq Look how I do it in my example image

dkulakov avatar Oct 06 '22 09:10 dkulakov

@dkulakov

await waitFor(() => component.getByText('Talo'));
await act(async () => {
  await fireEvent.press(component.getByText('Talo'));
});

Gives Unable to find an element with text: Talo And If i replace getByText with findByText, it gives Timed out in waitFor.

Taloqq avatar Oct 06 '22 09:10 Taloqq

@Taloqq Can you share your whole test case?

dkulakov avatar Oct 06 '22 09:10 dkulakov

@dkulakov

describe('NewAddressView', () => {

  it('Navigates to page 2 when address and location type is set', async () => {
    const component = render(
      <NewAddressView onBack={jest.fn()} />
    );
    fireEvent.press(component.getByTestId('locationConfirm'));
    await waitFor(() => fireEvent.press(component.getByTestId('nextPageButton')));
    await waitFor(() => component.getByText('Talo'));
    await act(async () => {
      await fireEvent.press(component.getByText('Talo'));
    });
  });
});

And heres how im calling it:

const askLocationType = async () => {
    const locationTypeResponse = await SheetManager.show('LocationTypeSheet');
    if (locationTypeResponse) {
     ...
    }
  };

Taloqq avatar Oct 06 '22 09:10 Taloqq

@Taloqq Is it also inside ActionSheet? If so, does it work? await waitFor(() => fireEvent.press(component.getByTestId('nextPageButton')));

dkulakov avatar Oct 06 '22 09:10 dkulakov

@dkulakov No, const askLocationType = async () => { } is not inside the actionSheet. Its in my AddressPage1 component. The action sheet fires when 'nextPageButton' is pressed

Taloqq avatar Oct 06 '22 09:10 Taloqq

@Taloqq Try to remove waitFor for fireEvent.press(component.getByTestId('nextPageButton'))

dkulakov avatar Oct 06 '22 09:10 dkulakov

@dkulakov It has no effect :/

Taloqq avatar Oct 06 '22 09:10 Taloqq

@Taloqq Can you reproduce this issue using my demo project?

dkulakov avatar Oct 06 '22 12:10 dkulakov


jest.mock('react-native-actions-sheet', () => {
	const { View } = require('react-native')

	const ActionSheet = props => {
		return <View testID="actionSheet">{props.children}</View>
	}

	return {
		__esModule: true,
		default: ActionSheet,
		SheetManager: {
			hideAll: jest.fn(),
			hide: jest.fn(),
			show: jest.fn(),
		},
	}
})

I mocked the library, can you try this?

Kaung-Htet-Hein avatar Oct 14 '22 03:10 Kaung-Htet-Hein

@Kaung-Htet-Hein thanks, I tried, but it doesn't help.

import { registerSheet } from 'react-native-actions-sheet';

this method renders sheets in a portal, so if we mock this lib, then a content never appears in the test.

dkulakov avatar Oct 14 '22 09:10 dkulakov

@Kaung-Htet-Hein well, I made it work in this way

jest.mock('react-native-actions-sheet', () => {
  const funcs = jest.requireActual('react-native-actions-sheet');

  return {
    __esModule: true,
    ...funcs,
    SheetManager: {
      show: funcs.SheetManager.show,
      hide: jest.fn(),
      // hide: funcs.SheetManager.hide, // <-- It doesn't work if uncomment this line
    },
    default: ({ children }) => children,
  };
});

dkulakov avatar Oct 14 '22 09:10 dkulakov