react-navigation / react-navigation

Routing and navigation for your React Native apps
https://reactnavigation.org
23.56k stars 5.03k forks source link

TypeError: Cannot convert undefined or null to object when testing web with jest-expo in a Expo managed workflow app #8604

Open Albert-Gao opened 4 years ago

Albert-Gao commented 4 years ago

expo-cli version: 3.22.3

Created a managed TS project:

package.json

{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject",
    "test": "node_modules/.bin/jest",
    "test:debug": "node --inspect-brk node_modules/jest/bin/jest.js --runInBand"
  },
  "dependencies": {
    "@react-native-community/masked-view": "0.1.10",
    "@react-navigation/native": "^5.7.0",
    "@react-navigation/stack": "^5.7.0",
    "expo": "~38.0.8",
    "expo-status-bar": "^1.0.2",
    "react": "~16.11.0",
    "react-dom": "~16.11.0",
    "react-native": "https://github.com/expo/react-native/archive/sdk-38.0.2.tar.gz",
    "react-native-gesture-handler": "~1.6.0",
    "react-native-reanimated": "~1.9.0",
    "react-native-safe-area-context": "~3.0.7",
    "react-native-screens": "~2.9.0",
    "react-native-web": "~0.11.7"
  },
  "devDependencies": {
    "@babel/core": "^7.8.6",
    "@types/react": "~16.9.41",
    "@types/react-native": "~0.62.13",
    "jest-expo": "^38.0.2",
    "react-native-testing-library": "^2.1.1",
    "typescript": "~3.9.5"
  },
  "private": true,
  "jest": {
    "projects": [
      {
        "preset": "jest-expo/ios",
        "setupFiles": [
          "./setup.js"
        ],
        "transformIgnorePatterns": [
          "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*)"
        ]
      },
      {
        "preset": "jest-expo/android",
        "setupFiles": [
          "./setup.js"
        ],
        "transformIgnorePatterns": [
          "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*)"
        ]
      },
      {
        "preset": "jest-expo/web",
        "setupFiles": [
          "./setup.js"
        ],
        "transformIgnorePatterns": [
          "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*)"
        ]
      }
    ]
  }
}

setup.js for jest

from https://reactnavigation.org/docs/testing/

import "react-native-gesture-handler/jestSetup";

jest.mock("react-native-reanimated", () => {
  const Reanimated = require("react-native-reanimated/mock");

  // The mock for `call` immediately calls the callback which is incorrect
  // So we override it with a no-op
  Reanimated.default.call = () => {};

  return Reanimated;
});

// Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing
jest.mock("react-native/Libraries/Animated/src/NativeAnimatedHelper");

App.tsx

import { StatusBar } from "expo-status-bar";
import React from "react";
import { TextInput, StyleSheet, Text, View } from "react-native";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";

const Stack = createStackNavigator();

const Screen = () => (
  <View>
    <Text>Open up App.tsx to start working on your app!</Text>
    <StatusBar style="auto" />
    <TextInput />
  </View>
);

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="first" component={Screen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

App.test.tsx

import * as React from "react";
import App from "./App";
import { render, act } from "react-native-testing-library";
import { flushMicrotasksQueue } from "./testUtils";

// Silence the warning https://github.com/facebook/react-native/issues/11094#issuecomment-263240420
jest.mock("react-native/Libraries/Animated/src/NativeAnimatedHelper");

it("should work", async () => {
  const tree = render(<App />);

  // fix the Warning: An update to ForwardRef(NavigationContainer) inside a test was not wrapped in act(...).
  await act(async () => {
    await flushMicrotasksQueue();
  });

  expect(tree).toBeTruthy();
});

Outcome:

Android and iOS are passing while Web is failing.

image

 FAIL   Web  ./App.test.tsx
  ● Console

    console.error
      Error: Uncaught [TypeError: Cannot convert undefined or null to object]
          at reportException (/Users/AlbertGao/codes/temp/trya/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:62:24)
          at innerInvokeEventListeners (/Users/AlbertGao/codes/temp/trya/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:332:9)
          at invokeEventListeners (/Users/AlbertGao/codes/temp/trya/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:267:3)
          at HTMLUnknownElementImpl._dispatch (/Users/AlbertGao/codes/temp/trya/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:214:9)
          at HTMLUnknownElementImpl.dispatchEvent (/Users/AlbertGao/codes/temp/trya/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:87:17)
          at HTMLUnknownElement.dispatchEvent (/Users/AlbertGao/codes/temp/trya/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:144:23)
          at Object.invokeGuardedCallbackDev (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11793:16)
          at invokeGuardedCallback (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11846:31)
          at commitRootImpl (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15069:9)
          at unstable_runWithPriority (/Users/AlbertGao/codes/temp/trya/node_modules/scheduler/cjs/scheduler.development.js:818:12) TypeError: Cannot convert undefined or null to object
          at slice (<anonymous>)
          at getDOMStyleInfo (/Users/AlbertGao/codes/temp/trya/node_modules/react-native-web/dist/cjs/exports/StyleSheet/createStyleResolver.js:385:41)
          at Object.resolveWithNode (/Users/AlbertGao/codes/temp/trya/node_modules/react-native-web/dist/cjs/exports/StyleSheet/createStyleResolver.js:202:28)
          at styleResolver (/Users/AlbertGao/codes/temp/trya/node_modules/react-native-web/dist/cjs/modules/NativeMethodsMixin/index.js:91:39)
          at createDOMProps (/Users/AlbertGao/codes/temp/trya/node_modules/react-native-web/dist/cjs/modules/createDOMProps/index.js:150:24)
          at View.setNativeProps (/Users/AlbertGao/codes/temp/trya/node_modules/react-native-web/dist/cjs/modules/NativeMethodsMixin/index.js:90:22)
          at Card.setPointerEventsEnabled (/Users/AlbertGao/codes/temp/trya/node_modules/@react-navigation/stack/lib/commonjs/views/Stack/Card.tsx:238:5)
          at Card.animate (/Users/AlbertGao/codes/temp/trya/node_modules/@react-navigation/stack/lib/commonjs/views/Stack/Card.tsx:187:5)
          at Card.componentDidMount (/Users/AlbertGao/codes/temp/trya/node_modules/@react-navigation/stack/lib/commonjs/views/Stack/Card.tsx:99:5)
          at commitLifeCycles (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12202:22)
          at commitLayoutEffects (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15331:7)
          at HTMLUnknownElement.callCallback (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11744:14)
          at innerInvokeEventListeners (/Users/AlbertGao/codes/temp/trya/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:316:27)
          at invokeEventListeners (/Users/AlbertGao/codes/temp/trya/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:267:3)
          at HTMLUnknownElementImpl._dispatch (/Users/AlbertGao/codes/temp/trya/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:214:9)
          at HTMLUnknownElementImpl.dispatchEvent (/Users/AlbertGao/codes/temp/trya/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:87:17)
          at HTMLUnknownElement.dispatchEvent (/Users/AlbertGao/codes/temp/trya/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:144:23)
          at Object.invokeGuardedCallbackDev (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11793:16)
          at invokeGuardedCallback (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11846:31)
          at commitRootImpl (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15069:9)
          at unstable_runWithPriority (/Users/AlbertGao/codes/temp/trya/node_modules/scheduler/cjs/scheduler.development.js:818:12)
          at runWithPriority (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2013:10)
          at commitRoot (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14918:3)
          at finishSyncRender (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14425:3)
          at performSyncWorkOnRoot (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14403:9)
          at /Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2063:24
          at unstable_runWithPriority (/Users/AlbertGao/codes/temp/trya/node_modules/scheduler/cjs/scheduler.development.js:818:12)
          at runWithPriority (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2013:10)
          at flushSyncCallbackQueueImpl (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2058:7)
          at flushSyncCallbackQueue (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2046:3)
          at batchedUpdates (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14446:7)
          at act (/Users/AlbertGao/codes/temp/trya/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:17248:14)
          at renderWithAct (/Users/AlbertGao/codes/temp/trya/node_modules/react-native-testing-library/build/render.js:67:3)
          at render (/Users/AlbertGao/codes/temp/trya/node_modules/react-native-testing-library/build/render.js:46:20)
          at _callee2$ (/Users/AlbertGao/codes/temp/trya/App.test.tsx:10:16)
          at tryCatch (/Users/AlbertGao/codes/temp/trya/node_modules/regenerator-runtime/runtime.js:45:40)
          at Generator.invoke [as _invoke] (/Users/AlbertGao/codes/temp/trya/node_modules/regenerator-runtime/runtime.js:274:22)
          at Generator.prototype.<computed> [as next] (/Users/AlbertGao/codes/temp/trya/node_modules/regenerator-runtime/runtime.js:97:21)
          at tryCatch (/Users/AlbertGao/codes/temp/trya/node_modules/regenerator-runtime/runtime.js:45:40)
          at invoke (/Users/AlbertGao/codes/temp/trya/node_modules/regenerator-runtime/runtime.js:135:20)
          at /Users/AlbertGao/codes/temp/trya/node_modules/regenerator-runtime/runtime.js:170:11
          at new Promise (<anonymous>)
          at callInvokeWithMethodAndArg (/Users/AlbertGao/codes/temp/trya/node_modules/regenerator-runtime/runtime.js:169:16)
          at AsyncIterator.enqueue (/Users/AlbertGao/codes/temp/trya/node_modules/regenerator-runtime/runtime.js:192:13)
          at AsyncIterator.prototype.<computed> [as next] (/Users/AlbertGao/codes/temp/trya/node_modules/regenerator-runtime/runtime.js:97:21)
          at Object.<anonymous>.exports.async (/Users/AlbertGao/codes/temp/trya/node_modules/regenerator-runtime/runtime.js:219:14)
          at Object._callee2 (/Users/AlbertGao/codes/temp/trya/App.test.tsx:9:19)
          at Object.asyncJestTest (/Users/AlbertGao/codes/temp/trya/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:100:37)
          at /Users/AlbertGao/codes/temp/trya/node_modules/jest-jasmine2/build/queueRunner.js:45:12
          at new Promise (<anonymous>)
          at mapper (/Users/AlbertGao/codes/temp/trya/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
          at /Users/AlbertGao/codes/temp/trya/node_modules/jest-jasmine2/build/queueRunner.js:75:41
          at processTicksAndRejections (internal/process/task_queues.js:97:5)

      at VirtualConsole.<anonymous> (node_modules/jsdom/lib/jsdom/virtual-console.js:29:45)
      at reportException (node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:28)
      at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:332:9)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:267:3)
      at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:214:9)
      at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:87:17)
      at HTMLUnknownElement.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:144:23)

    console.error
      The above error occurred in the <Card> component:
          in Card (created by CardContainer)
          in CardContainer (created by Context.Consumer)
          in div (created by View)
          in View (created by WebScreen)
          in WebScreen (created by AnimatedComponent)
          in AnimatedComponent (created by MaybeScreen)
          in MaybeScreen (created by Context.Consumer)
          in div (created by View)
          in View (created by MaybeScreenContainer)
          in MaybeScreenContainer (created by Context.Consumer)
          in CardStack (created by KeyboardManager)
          in KeyboardManager (created by Context.Consumer)
          in div (created by View)
          in View (created by NativeSafeAreaView)
          in NativeSafeAreaView (created by SafeAreaProvider)
          in SafeAreaProvider (created by Context.Consumer)
          in SafeAreaProviderCompat (created by StackView)
          in div (created by View)
          in View (created by StackView)
          in StackView (created by StackNavigator)
          in StackNavigator (created by App)
          in EnsureSingleNavigator (created by ForwardRef(BaseNavigationContainer))
          in ForwardRef(BaseNavigationContainer) (created by ForwardRef(NavigationContainer))
          in ThemeProvider (created by ForwardRef(NavigationContainer))
          in ForwardRef(NavigationContainer) (created by App)
          in App

      Consider adding an error boundary to your tree to customize error handling behavior.
      Visit https://fb.me/react-error-boundaries to learn more about error boundaries.

      at logCapturedError (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11944:13)
      at logError (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11981:5)
      at update.callback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13331:5)
      at callCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:3728:12)
      at commitUpdateEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:3766:7)
      at commitUpdateQueue (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:3754:3)
      at commitLifeCycles (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12261:11)
      at commitLayoutEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15331:7)

  ● should work

    TypeError: Cannot convert undefined or null to object
        at slice (<anonymous>)

       8 |
       9 | it("should work", async () => {
    > 10 |   const tree = render(<App />);
         |                ^
      11 |
      12 |   // fix the Warning: An update to ForwardRef(NavigationContainer) inside a test was not wrapped in act(...).
      13 |   await act(async () => {

      at getDOMStyleInfo (node_modules/react-native-web/dist/cjs/exports/StyleSheet/createStyleResolver.js:385:41)
      at Object.resolveWithNode (node_modules/react-native-web/dist/cjs/exports/StyleSheet/createStyleResolver.js:202:28)
      at styleResolver (node_modules/react-native-web/dist/cjs/modules/NativeMethodsMixin/index.js:91:39)
      at createDOMProps (node_modules/react-native-web/dist/cjs/modules/createDOMProps/index.js:150:24)
      at View.setNativeProps (node_modules/react-native-web/dist/cjs/modules/NativeMethodsMixin/index.js:90:22)
      at Card.setPointerEventsEnabled (node_modules/@react-navigation/stack/lib/commonjs/views/Stack/Card.tsx:238:5)
      at Card.animate (node_modules/@react-navigation/stack/lib/commonjs/views/Stack/Card.tsx:187:5)
      at Card.componentDidMount (node_modules/@react-navigation/stack/lib/commonjs/views/Stack/Card.tsx:99:5)
      at commitLifeCycles (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12202:22)
      at commitLayoutEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15331:7)
      at HTMLUnknownElement.callCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11744:14)
      at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:316:27)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:267:3)
      at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:214:9)
      at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:87:17)
      at HTMLUnknownElement.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:144:23)
      at Object.invokeGuardedCallbackDev (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11793:16)
      at invokeGuardedCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11846:31)
      at commitRootImpl (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15069:9)
      at unstable_runWithPriority (node_modules/scheduler/cjs/scheduler.development.js:818:12)
      at runWithPriority (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2013:10)
      at commitRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14918:3)
      at finishSyncRender (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14425:3)
      at performSyncWorkOnRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14403:9)
      at node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2063:24
      at unstable_runWithPriority (node_modules/scheduler/cjs/scheduler.development.js:818:12)
      at runWithPriority (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2013:10)
      at flushSyncCallbackQueueImpl (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2058:7)
      at flushSyncCallbackQueue (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2046:3)
      at batchedUpdates (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14446:7)
      at act (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:17248:14)
      at renderWithAct (node_modules/react-native-testing-library/build/render.js:67:3)
      at render (node_modules/react-native-testing-library/build/render.js:46:20)
      at _callee2$ (App.test.tsx:10:16)
      at tryCatch (node_modules/regenerator-runtime/runtime.js:45:40)
      at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:274:22)
      at Generator.prototype.<computed> [as next] (node_modules/regenerator-runtime/runtime.js:97:21)
      at tryCatch (node_modules/regenerator-runtime/runtime.js:45:40)
      at invoke (node_modules/regenerator-runtime/runtime.js:135:20)
      at node_modules/regenerator-runtime/runtime.js:170:11
      at callInvokeWithMethodAndArg (node_modules/regenerator-runtime/runtime.js:169:16)
      at AsyncIterator.enqueue (node_modules/regenerator-runtime/runtime.js:192:13)
      at AsyncIterator.prototype.<computed> [as next] (node_modules/regenerator-runtime/runtime.js:97:21)
      at Object.<anonymous>.exports.async (node_modules/regenerator-runtime/runtime.js:219:14)
      at Object._callee2 (App.test.tsx:9:19)

Only the web is failing, what am I missing here? Thanks

Albert-Gao commented 4 years ago

Have a reproduced repo here: https://github.com/Albert-Gao/react-navigation-test-re-produce

image

Albert-Gao commented 4 years ago

I tried added https://github.com/react-navigation/react-navigation/blob/main/jest/setup.js as one of the jest setup, won't help

Albert-Gao commented 4 years ago

Or is it because I miss configuring the jest-expo in package.json?

Is this a proper setup for testing react-navigation with jest-expo? @EvanBacon

  "jest": {
    "projects": [
      {
        "preset": "jest-expo/ios",
        "setupFiles": [
          "./setup.js"
        ],
        "transformIgnorePatterns": [
          "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*)"
        ]
      },
      {
        "preset": "jest-expo/android",
        "setupFiles": [
          "./setup.js"
        ],
        "transformIgnorePatterns": [
          "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*)"
        ]
      },
      {
        "preset": "jest-expo/web",
        "setupFiles": [
          "./setup.js"
        ],
        "transformIgnorePatterns": [
          "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*)"
        ]
      }
    ]
  }