GeekyAnts / NativeBase

Mobile-first, accessible components for React Native & Web to build consistent UI across Android, iOS and Web.
https://nativebase.io/
MIT License
20.23k stars 2.39k forks source link

Jest TypeError: Cannot read properties of undefined (reading 'width') #5820

Open toninlopes opened 4 weeks ago

toninlopes commented 4 weeks ago

Description

Testing Snapshot NativeBase components failed

CodeSandbox/Snack link

https://github.com/toninlopes/NativeBaseSample

Steps to reproduce

  1. On terminal, run yarn test

NativeBase Version

3.4.28

Platform

Other Platform

No response

Additional Information

I have set up a standard React Native + Native Base project. See the package.json.

{
  "name": "NativeBaseSample",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "lint": "eslint .",
    "start": "react-native start",
    "test": "jest"
  },
  "dependencies": {
    "native-base": "^3.4.28",
    "react": "18.3.1",
    "react-native": "0.76.0",
    "react-native-safe-area-context": "3.3.2",
    "react-native-svg": "12.1.1"
  },
  "devDependencies": {
    "@babel/core": "^7.25.2",
    "@babel/preset-env": "^7.25.3",
    "@babel/runtime": "^7.25.0",
    "@react-native-community/cli": "15.0.0-alpha.2",
    "@react-native-community/cli-platform-android": "15.0.0-alpha.2",
    "@react-native-community/cli-platform-ios": "15.0.0-alpha.2",
    "@react-native/babel-preset": "0.76.0",
    "@react-native/eslint-config": "0.76.0",
    "@react-native/metro-config": "0.76.0",
    "@react-native/typescript-config": "0.76.0",
    "@testing-library/react-native": "^12.8.0",
    "@types/react": "^18.2.6",
    "@types/react-test-renderer": "^18.0.0",
    "babel-jest": "^29.6.3",
    "eslint": "^8.19.0",
    "jest": "^29.6.3",
    "prettier": "2.8.8",
    "react-test-renderer": "18.3.1",
    "typescript": "5.0.4"
  },
  "engines": {
    "node": ">=18"
  }
}

Here is a simple snapshot test with @testing-library/react-native and react-test-renderer.

import 'react-native';
import React from 'react';
import {describe, it, expect} from '@jest/globals';
import {render, screen} from '@testing-library/react-native';
import renderer from 'react-test-renderer';
import { Button } from 'native-base';
import { Text } from 'react-native';
import { NativeBaseProvider } from 'native-base';
import { Metrics, SafeAreaProvider } from 'react-native-safe-area-context';

const MOCK_INITIAL_METRICS: Metrics = {
  frame: { width: 320, height: 640, x: 0, y: 0 },
  insets: { left: 0, right: 0, bottom: 0, top: 0 },
};

const Wrapper = ({children}: {children: React.ReactNode}) =>
  <SafeAreaProvider initialMetrics={MOCK_INITIAL_METRICS}>
    <NativeBaseProvider>
      {children}
    </NativeBaseProvider>
  </SafeAreaProvider>;

describe('Snapshot Testing', () => {

  it('Render NativeBase Button with @testing-library/react-native', () => {
    render(
      <Wrapper>
        <Button>Test</Button>
      </Wrapper>
    );
    expect(screen.toJSON()).toMatchSnapshot();
  });

  it('Render NativeBase Button with react-test-renderer', () => {
    const component = renderer.create(
      <Wrapper>
        <Button>Test</Button>
      </Wrapper>
    ).toJSON();
    expect(component).toMatchSnapshot();
  });

  it('Render React Native Text with @testing-library/react-native', () => {
    render(
      <Text>Test</Text>
    );
    expect(screen.toJSON()).toMatchSnapshot();
  });

  it('Render React Native Text with react-test-renderer', () => {
    const component = renderer.create(
      <Text>Test</Text>
    ).toJSON();
    expect(component).toMatchSnapshot();
  });

});

Tests 1 and 2 use the <NativeBaseProvider /> as wrapper and both libraries gets the same error.

console.error
    The above error occurred in the <ForwardRef> component:

        at debug (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/utils/styled.js:18:5)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/components/primitives/Pressable/Pressable.js:72:3)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/components/primitives/Button/Button.js:38:3)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/@react-aria/ssr/dist/SSRProvider.main.js:75:154)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/components/composites/Toast/Toast.js:139:3)
        at useState (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/@react-native-aria/overlays/lib/commonjs/Portal.js:17:44)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/core/hybrid-overlay/HybridProvider.js:23:3)
        at disableCSSMediaQueries (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/utils/useResponsiveQuery/ResponsiveQueryProvider.js:21:14)
        at RNCSafeAreaProvider
        at Component (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/react-native/jest/mockNativeComponent.js:17:18)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/react-native-safe-area-context/lib/commonjs/SafeAreaContext.js:35:3)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/utils/createContext.js:17:7)
        at colorModeManager (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/core/NativeBaseProvider.js:56:5)
        at RNCSafeAreaProvider
        at Component (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/react-native/jest/mockNativeComponent.js:17:18)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/react-native-safe-area-context/lib/commonjs/SafeAreaContext.js:35:3)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/__tests__/App.test.tsx:16:19)

    Consider adding an error boundary to your tree to customize error handling behavior.
    Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.

      25 |
      26 |   it('Render NativeBase Button with @testing-library/react-native', () => {
    > 27 |     render(
         |           ^
      28 |       <Wrapper>
      29 |         <Button>Test</Button>
      30 |       </Wrapper>

      at logCapturedError (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8678:23)
      at logCapturedError (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8711:5)
      at call (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5175:12)
      at callCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5196:9)
      at commitUpdateQueue (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13271:13)
      at commitLayoutEffectOnFiber (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14369:9)
      at commitLayoutMountEffects_complete (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14355:7)
      at commitLayoutEffects_begin (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14340:3)
      at commitLayoutEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:16101:5)
      at commitRootImpl (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15972:5)
      at commitRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15493:3)
      at callback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2597:22)
      at callback (node_modules/react/cjs/react.development.js:2667:24)
      at flushActQueue (node_modules/react/cjs/react.development.js:2521:11)
      at actImplementation (node_modules/@testing-library/react-native/build/act.js:31:25)
      at renderWithAct (node_modules/@testing-library/react-native/build/render-act.js:14:24)
      at renderInternal (node_modules/@testing-library/react-native/build/render.js:54:48)
      at renderInternal (node_modules/@testing-library/react-native/build/render.js:29:10)
      at Object.<anonymous> (__tests__/App.test.tsx:27:11)

  console.error
    The above error occurred in the <ForwardRef> component:

        at debug (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/utils/styled.js:18:5)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/components/primitives/Pressable/Pressable.js:72:3)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/components/primitives/Button/Button.js:38:3)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/@react-aria/ssr/dist/SSRProvider.main.js:75:154)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/components/composites/Toast/Toast.js:139:3)
        at useState (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/@react-native-aria/overlays/lib/commonjs/Portal.js:17:44)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/core/hybrid-overlay/HybridProvider.js:23:3)
        at disableCSSMediaQueries (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/utils/useResponsiveQuery/ResponsiveQueryProvider.js:21:14)
        at RNCSafeAreaProvider
        at Component (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/react-native/jest/mockNativeComponent.js:17:18)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/react-native-safe-area-context/lib/commonjs/SafeAreaContext.js:35:3)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/utils/createContext.js:17:7)
        at colorModeManager (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/native-base/lib/commonjs/core/NativeBaseProvider.js:56:5)
        at RNCSafeAreaProvider
        at Component (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/react-native/jest/mockNativeComponent.js:17:18)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/node_modules/react-native-safe-area-context/lib/commonjs/SafeAreaContext.js:35:3)
        at children (/Users/aclopesjr/git_repos/ReactNative/NativeBaseSample/__tests__/App.test.tsx:16:19)

    Consider adding an error boundary to your tree to customize error handling behavior.
    Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.

      34 |
      35 |   it('Render NativeBase Button with react-test-renderer', () => {
    > 36 |     const component = renderer.create(
         |                                ^
      37 |       <Wrapper>
      38 |         <Button>Test</Button>
      39 |       </Wrapper>

      at logCapturedError (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8678:23)
      at logCapturedError (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8711:5)
      at call (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5175:12)
      at callCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5196:9)
      at commitUpdateQueue (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13271:13)
      at commitLayoutEffectOnFiber (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14369:9)
      at commitLayoutMountEffects_complete (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14355:7)
      at commitLayoutEffects_begin (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14340:3)
      at commitLayoutEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:16101:5)
      at commitRootImpl (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15972:5)
      at commitRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15493:3)
      at callback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2597:22)
      at flushSyncCallbacks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2576:5)
      at flushSyncCallbacksOnlyInLegacyMode (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14949:7)
      at scheduleUpdateOnFiber (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:17809:5)
      at Object.updateContainer [as create] (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:18549:3)
      at Object.create (__tests__/App.test.tsx:36:32)

 FAIL  __tests__/App.test.tsx
  Snapshot Testing
    ✕ Render NativeBase Button with @testing-library/react-native (283 ms)
    ✕ Render NativeBase Button with react-test-renderer (15 ms)
    ✓ Render React Native Text with @testing-library/react-native (3 ms)
    ✓ Render React Native Text with react-test-renderer

  ● Snapshot Testing › Render NativeBase Button with @testing-library/react-native

    TypeError: Cannot read properties of undefined (reading 'width')

      25 |
      26 |   it('Render NativeBase Button with @testing-library/react-native', () => {
    > 27 |     render(
         |           ^
      28 |       <Wrapper>
      29 |         <Button>Test</Button>
      30 |       </Wrapper>

      at useResponsiveQuery (node_modules/native-base/lib/commonjs/utils/useResponsiveQuery/useResponsiveQuery.js:19:62)
      at useStyledSystemPropsResolver (node_modules/native-base/lib/commonjs/hooks/useStyledSystemPropsResolver.js:38:49)
      at node_modules/native-base/lib/commonjs/utils/styled.js:21:71
      at Component (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5608:18)
      at renderWithHooks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9169:20)
      at updateForwardRef (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11400:16)
      at beginWork$1 (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15850:12)
      at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15784:5)
      at workLoopSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15756:7)
      at renderRootSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15461:20)
      at callback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2597:22)
      at callback (node_modules/react/cjs/react.development.js:2667:24)
      at flushActQueue (node_modules/react/cjs/react.development.js:2521:11)
      at actImplementation (node_modules/@testing-library/react-native/build/act.js:31:25)
      at renderWithAct (node_modules/@testing-library/react-native/build/render-act.js:14:24)
      at renderInternal (node_modules/@testing-library/react-native/build/render.js:54:48)
      at renderInternal (node_modules/@testing-library/react-native/build/render.js:29:10)
      at Object.<anonymous> (__tests__/App.test.tsx:27:11)

  ● Snapshot Testing › Render NativeBase Button with react-test-renderer

    TypeError: Cannot read properties of undefined (reading 'width')

      34 |
      35 |   it('Render NativeBase Button with react-test-renderer', () => {
    > 36 |     const component = renderer.create(
         |                                ^
      37 |       <Wrapper>
      38 |         <Button>Test</Button>
      39 |       </Wrapper>

      at useResponsiveQuery (node_modules/native-base/lib/commonjs/utils/useResponsiveQuery/useResponsiveQuery.js:19:62)
      at useStyledSystemPropsResolver (node_modules/native-base/lib/commonjs/hooks/useStyledSystemPropsResolver.js:38:49)
      at node_modules/native-base/lib/commonjs/utils/styled.js:21:71
      at Component (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5608:18)
      at renderWithHooks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9169:20)
      at updateForwardRef (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11400:16)
      at beginWork$1 (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15850:12)
      at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15784:5)
      at workLoopSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15756:7)
      at renderRootSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15461:20)
      at callback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2597:22)
      at flushSyncCallbacks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2576:5)
      at flushSyncCallbacksOnlyInLegacyMode (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14949:7)
      at scheduleUpdateOnFiber (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:17809:5)
      at Object.updateContainer [as create] (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:18549:3)
      at Object.create (__tests__/App.test.tsx:36:32)

Test Suites: 1 failed, 1 total
Tests:       2 failed, 2 passed, 4 total
Snapshots:   2 passed, 2 total
Time:        2.949 s, estimated 3 s
Ran all test suites related to changed files.
toninlopes commented 4 weeks ago

The jest.config.js file has the property setupFiles. So, for my surprise, when I remove that property, the test passed. And that made me think that the issue would be missing mock piece of code. So, I started to review the mocks and I found out the one was causing the issue.

Before

jest.mock('react-native/Libraries/Utilities/Dimensions', () => ({
  get: jest.fn(),
  set: jest.fn(),
  addEventListener: jest.fn(),
}));

After

jest.mock('react-native/Libraries/Utilities/Dimensions', () => ({
  get: jest.fn().mockImplementation(() => {
    return { width: 375, height: 812, scale: 0, fontScale: 0 };
  }),
  set: jest.fn(),
  addEventListener: jest.fn().mockImplementation(() => {
    return { remove: jest.fn() };
  }),
}));