Skip to content

After upgrading to React Native 0.73 & Jest 29, bundle splitter seems to cause rendering issues in inner components (in tests) #63

@matt-dalton

Description

@matt-dalton

Sorry, this is quite a tricky bug to narrow down as there are lots of variables involved, but hopefully there's enough here for a hint of the problem

Describe the bug
We have upgraded from React Native 0.68 and jest 26-29. Quite a big jump, but we managed to get most of our 700 tests working after tweaking some of the fake timer logic.

We're having problems in two suites, both of which involve the bundle splitter layer.

If I import a component using bundle splitter, the useEffect in my component never seems to run. I have tried all manner of jest.runOnlyPendingTimers etc, and have also tried real timers, increasing jest timeouts etc and nothing seems to get this to work. If I call React Native test library rerender it then calls useEffect once, but this is obviously a hack.

The test then works fine when I import the component normally.

Code snippet
Component file

const SignUpScreen = () => {
    //This render logic successfully runs in my test
    React.useEffect(() => {
         //but this useEffect never does
         triggerTracking()
    }, [])

    const triggerTracking = ()=>{
         myTrackingFn()
    }

Navigation layer file

const SignUpScreen = register({
    loader: () => require('Screens/SignUpScreen'),
    group: ONBOARDING,
})
// import SignUpScreen from 'Screens/SignUpScreen' // If I comment the above and use this, everything works as expected

Test file

        const component = <Navigator {...props} />

        const renderedComponent = await waitFor(() => render(component))

        // Check screen has rendered
        const image = await renderedComponent.findByTestId(SIGNUP_IMG_TEST_ID)
        expect(image).toBeTruthy()

        // Initial Paywall
        act(() => {
            // Nothing in here causes the useEffect to fire...have tried many many combinations!
            jest.runOnlyPendingTimers()
            //jest.runAllTimers Also doesn't work
            // renderedComponent.rerender(component) //This gets the useEffect to fire once
        })

        // This never triggers, even with real timers, because the useEffect never runs
        await waitFor(() => expect(myTrackingFn).toHaveBeenCalled(), {
            timeout: 40000,
        })

I can fix it using this mock:

const RNBundleSplitter = jest.requireActual('react-native-bundle-splitter')

module.exports = {
    ...RNBundleSplitter,
    register: (props: any) => {
        const Component = props.loader().default

        return Component
    },
}

but would be nice if I could test the real behaviour in my tests.

Expected behavior
The useEffect in the inner component should render in tests

Smartphone (please complete the following information):

  • Desktop OS MacOS 14.4
  • Device: n/a
  • JS engine: Jest (node)
  • Library version 2.2.3

Any idea what this could be?

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions