Много проблем с jest-expo -- "Ошибка типа: невозможно прочитать свойство 'fetch' неопределенного"

Я искал много тем по этой проблеме на сайте, но ни один из них не имеет ответов или ответов, которые мне подходят. У меня есть набор модульных тестов jest, который я пытаюсь запустить, но продолжаю получать сообщения об ошибках Test suite failed to run -- TypeError: Cannot read property 'fetch' of undefined.

Я уже пробовал все исправления, предложенные во всех других потоках проблем stackoverflow и github, связанных с этой проблемой.

Может кто-нибудь помочь мне?

Вот один из двух моих неудачных тестов: App.spec.js

import React from 'react';
import renderer from 'react-test-renderer';
import { mount } from 'enzyme';
import { App } from '../../App';
import { FlooringType } from '../../Condition/FlooringType';
import { SavedPhotosLauncher } from '../../Components/SavedPhotosLauncher';
import { CameraLauncher } from '../../Components/CameraLauncher';
import { CommentsLauncher } from '../../Components/CommentsLauncher';
import { FloorLevel } from '../../Components/FloorLevel';
import { CeilingType } from '../../Components/CeilingType';
import { RoomLayout } from '../../Components/RoomLayout';
import "babel-polyfill";
import fetch from 'isomorphic-fetch'

global.fetch = fetch;

describe('KnockKitchenDetail view suite', () => {
    beforeEach(async () => {
        await device.reloadReactNative();
    });

    it('should render Kitchen view correctly', () => {
        const tree = renderer.create(
            <App />
        ).toJSON()

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

// Room Layout header
    it('should show the Room Layout header text', () => {
        // Info text visible
        const wrapper = mount(<RoomLayout />);

        expect(wrapper.find({testID:'roomLayoutText'}).text()).toBe('Room Layout');
    });

    it('should show info text for what to do in Room Layout container', () => {
        // Info text visible
        const wrapper = mount(<RoomLayout />);

        expect(wrapper.find({testID:'infoText'}).text()).toBe('Take photos from opposite corners of the room');
    });

// Saved Photos and Camera Launcher Icon Buttons
    it('tapping saved photos button should launch saved photos workflow',async () => {
        // Select the saved photos button
        const launchSavedPhotosViewer = jest.fn();
        const wrapper = mount(<SavedPhotosLauncher />);
        wrapper.setProps({ photoNumber: 2})
        wrapper.find({testID: '{photoNumber}_of_photos'}).simulate('press');
        expect(launchSavedPhotosViewer).toHaveBeenCalledTimes(1);
    });

    it('should launch the camera when any camera icon is selected', () => {
        // Finds, selects, and launches native camera
        const launchNativeCamera = jest.fn();
        const wrapper = mount(<CameraLauncher />);
        wrapper.find({testID: 'cameraIconLauncherBtn'}).simulate('press');
        expect(launchNativeCamera).toHaveBeenCalled();
    });

// Add Comments and Comment Modal Launcher Icon Buttons
    it('should launch the comment modal when any comment icon is selected', () => {
        // Find and select a comment icon button
        const launchCommentModal = jest.fn();
        const wrapper = mount(<CommentsLauncher />);
        wrapper.find({testID: 'commentsIconButton'}).simulate('press');
        expect(launchCommentModal).toHaveBeenCalled();
    });

    it('should launch the comment modal when add comments button is pressed', () => {
        // Find and press the add comments button
        const launchAddCommentModal = jest.fn();
        const wrapper = mount(<AddCommentsLauncher />);
        wrapper.find({testID: 'addCommentsRoomLayoutButton'}).simulate('press');
        expect(launchAddCommentModal).toHaveBeenCalled();
    });

// Floor Level Section 
    it('should allow user to select a floor level', () => {
        // selects the floor level
        const selectFloorLevel = jest.fn();
        const wrapper = mount(<FloorLevel onPress = {selectFloorLevel} />);
        wrapper.find({testID: 'floorLevelLower'}).simulate('press')
        expect(floorLevel).toHaveBeenCalledWith('Lower')
    });

    it('should show changed state of the floor level', () => {
        // checks that the floor level state has changed
        const wrapper = mount(<App />);
        const text = wrapper.find({testID: 'floorLevelLower'}).text();
        expect(text).toBeFalsy();
        expect(wrapper.state('floorLevel')).toBe(null);

        wrapper.find({testID: 'floorLevelLower'}).simulate('press');
        expect(wrapper.find({testID:'floorLevelLower'}).text()).toBe('Lower');
        expect(wrapper.state('floorLevel')).toBe('Lower');
    });

// Walls / Paint Header Text
    it('should show the walls and paint header text', () => {
        // Info text visible
        const wrapper = mount(<App />);

        expect(wrapper.find({testID:'wallsPaintHeaderText'}).text()).toBe('Walls / Paint');
    });

// Ceiling Section
    it('should show the ceiling header text', () => {
        // Info text visible
        const wrapper = mount(<App />);

        expect(wrapper.find({testID:'ceilingHeaderText'}).text()).toBe('Ceiling');
    });

    it('should allow user to select a type of ceiling', () => {
        // selects the type of ceiling
        const selectCeilingType = jest.fn();
        const wrapper = mount(<CeilingType onPress = {selectCeilingType} />);
        wrapper.find({testID: 'ceilingTypeSpackled'}).simulate('press')
        expect(selectCeilingType).toHaveBeenCalledWith('Spackled')
    });

    it('should show changed state of the selected ceiling type', () => {
        // checks the ceiling type state change
        const wrapper = mount(<App />);
        const text = wrapper.find({testID: 'ceilingTypeSpackled'}).text();
        expect(text).toBeFalsy();
        expect(wrapper.state('ceilingType')).toBe(null);

        wrapper.find({testID: 'ceilingTypeSpackled'}).simulate('press');
        expect(wrapper.find({testID:'ceilingTypeSpackled'}).text()).toBe('Spackled');
        expect(wrapper.state('ceilingType')).toBe('Spackled');
    });

    it('should show changed state of the selected condition', () => {
        // checks the condition state change
        const wrapper = mount(<App />);
        const text = wrapper.find({testID: 'roomItemCondition'}).text();
        expect(text).toBeFalsy();
        expect(wrapper.state('roomItemCondition')).toBe(null);

        wrapper.find({testID: 'conditionGood'}).simulate('press');
        expect(wrapper.find({testID:'conditionGood'}).text()).toBe('Good');
        expect(wrapper.state('roomItemCondition')).toBe('Good');
    });

// Flooring Type Section
    it('should show the Flooring header text', () => {
        // Info text visible
        const wrapper = mount(<App />);

        expect(wrapper.find({testID:'flooringHeaderText'}).text()).toBe('Flooring');
    });

    it('should allow user to select a type of flooring', () => {
        // selects the type of flooring
        const selectFloorType = jest.fn();
        const wrapper = mount(<FlooringType onPress = {selectFloorType} />);
        wrapper.find({testID: 'flooringTypeTile'}).simulate('press')
        expect(selectFloorType).toHaveBeenCalledWith('Tile')
    });

    it('should show changed state of the selected flooring type', () => {
        // checks the flooring type state change
        const wrapper = mount(<App />);
        const text = wrapper.find({testID: 'flooringTypeTile'}).text();
        expect(text).toBeFalsy();
        expect(wrapper.state('tile')).toBe(null);

        wrapper.find({testID: 'flooringTypeTile'}).simulate('press');
        expect(wrapper.find({testID:'selectedFlooringType'}).text()).toBe('Tile');
        expect(wrapper.state('flooringType')).toBe('Tile');
    });
});

Вот второй тест: RoomLayout.spec.test

import React, { Component } from 'react';
import { render } from 'react-native-testing-library';
import App from '../../App';
import fetch from 'whatwg-fetch';

global.fetch = fetch;

describe('NewMessageForm', () => {
  describe('clicking send', () => {
    const kitchenHeader = 'Kitchen';

    let getByTestId;

    beforeEach(() => {
      ({ getByTestId } = render(<App />));

      // fireEvent.changeText(getByTestId('messageText'), messageText);
      // fireEvent.press(getByTestId('sendButton'));
    });

    it('clears the message field', () => {
      expect(getByTestId(kitchenHeader).props.value).toEqual('Kitchen');
    });
  });
});

Вот мой package.json

{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "test": "node_modules/.bin/jest test/**/*.spec.js",
    "eject": "expo eject"
  },
  "dependencies": {
    "@expo/vector-icons": "^9.0.0",
    "apsl-react-native-button": "^3.1.1",
    "expo": "^32.0.0",
    "react": "16.5.0",
    "react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
    "react-native-camera": "git+https://[email protected]/react-native-community/react-native-camera.git",
    "react-native-camera-roll-picker": "^1.2.3",
    "react-native-elements": "^1.1.0",
    "react-native-fontawesome": "^6.0.1",
    "react-native-is-iphonex": "^1.0.1",
    "react-native-vector-icons": "^6.2.0",
    "react-navigation": "^3.3.2"
  },
  "devDependencies": {
    "@babel/core": "^7.3.4",
    "@babel/preset-env": "^7.3.4",
    "@babel/preset-react": "^7.0.0",
    "babel-jest": "^24.1.0",
    "babel-preset-expo": "^5.0.0",
    "babel-preset-react-native": "^4.0.1",
    "detox": "^10.0.10",
    "detox-expo-helpers": "^0.6.0",
    "enzyme": "^3.9.0",
    "expo-detox-hook": "^1.0.10",
    "jest": "^24.1.0",
    "jest-expo": "^32.0.0",
    "metro-react-native-babel-preset": "^0.52.0",
    "prop-types": "^15.7.2",
    "react-addons-test-utils": "^15.6.2",
    "react-dom": "^16.8.3",
    "react-native-dotenv": "^0.2.0",
    "react-native-testing-library": "^1.5.0",
    "react-test-renderer": "^16.8.3"
  },
  "jest": {
    "preset": "jest-expo"
  },
  "private": true,
  "detox": {
    "test-runner": "jest",
    "configurations": {
      "ios.sim": {
        "binaryPath": "bin/Exponent.app",
        "type": "ios.simulator",
        "name": "iPhone X"
      }
    }
  }
}

Вот мой node_modules/jest-expo/src/setup.js файл (изменение require('whatwg-fetch') на require('fetch-everything) не решило проблему, кстати.

/**
 * Adds Expo-related mocks to the Jest environment. Jest runs this setup module
 * after it runs the React Native setup module.
 */
'use strict';

const { Response, Request, Headers, fetch } = require('whatwg-fetch');
global.Response = Response;
global.Request = Request;
global.Headers = Headers;
global.fetch = fetch;

const mockNativeModules = require('react-native/Libraries/BatchedBridge/NativeModules');

const createMockConstants = require('./createMockConstants');

// window isn't defined as of react-native 0.45+ it seems
if (typeof window !== 'object') {
  global.window = global;
  global.window.navigator = {};
}

const mockImageLoader = {
  configurable: true,
  enumerable: true,
  get: () => ({
    prefetchImage: jest.fn(),
    getSize: jest.fn((uri, success) => process.nextTick(() => success(320, 240))),
  }),
};
Object.defineProperty(mockNativeModules, 'ImageLoader', mockImageLoader);
Object.defineProperty(mockNativeModules, 'ImageViewManager', mockImageLoader);

const mockPlatformConstants = {
  configurable: true,
  enumerable: true,
  get: () => ({
    forceTouchAvailable: true,
  }),
};
Object.defineProperty(mockNativeModules, 'PlatformConstants', mockPlatformConstants);

const publicExpoModules = require('./expoModules');
const internalExpoModules = require('./internalExpoModules');
const expoModules = {
  ...publicExpoModules,
  ...internalExpoModules,
};

function mock(property, customMock) {
  const propertyType = property.type;
  let mockValue;
  if (customMock !== undefined) {
    mockValue = customMock;
  } else if (propertyType === 'function') {
    if (property.functionType === 'promise') {
      mockValue = jest.fn(() => Promise.resolve());
    } else {
      mockValue = jest.fn();
    }
  } else if (propertyType === 'number') {
    mockValue = 1;
  } else if (propertyType === 'string') {
    mockValue = 'mock';
  } else if (propertyType === 'array') {
    mockValue = [];
  } else if (propertyType === 'mock') {
    mockValue = mockByMockDefinition(property.mockDefinition);
  } else {
    mockValue = {};
  }
  return mockValue;
}

function mockProperties(moduleProperties, customMocks) {
  const mockedProperties = {};
  for (let propertyName of Object.keys(moduleProperties)) {
    const property = moduleProperties[propertyName];
    const customMock =
      customMocks && customMocks.hasOwnProperty(propertyName)
        ? customMocks[propertyName]
        : property.mock;
    mockedProperties[propertyName] = mock(property, customMock);
  }
  return mockedProperties;
}

function mockByMockDefinition(definition) {
  const mock = {};
  for (const key of Object.keys(definition)) {
    mock[key] = mockProperties(definition[key]);
  }
  return mock;
}

for (let moduleName of Object.keys(expoModules)) {
  const moduleProperties = expoModules[moduleName];
  const mockedProperties = mockProperties(moduleProperties);

  Object.defineProperty(mockNativeModules, moduleName, {
    configurable: true,
    enumerable: true,
    get: () => mockedProperties,
  });
}

mockNativeModules.ExpoNativeModuleProxy.viewManagersNames.forEach(viewManagerName => {
  Object.defineProperty(mockNativeModules.UIManager, `ViewManagerAdapter_${viewManagerName}`, {
    get: () => ({
      NativeProps: {},
      directEventTypes: [],
    }),
  });
});

// Needed for `react-native-gesture-handler` as of 10/29/2018
// Otherwise the following line fails with "cannot read property directEventTypes of undefined"
// https://github.com/kmagiera/react-native-gesture-handler/blob/master/GestureHandler.js#L46
Object.defineProperty(mockNativeModules.UIManager, 'RCTView', {
  get: () => ({
    NativeProps: {},
    directEventTypes: [],
  }),
});

const modulesConstants = mockNativeModules.ExpoNativeModuleProxy.modulesConstants;
mockNativeModules.ExpoNativeModuleProxy.modulesConstants = {
  ...modulesConstants,
  ExponentConstants: {
    ...modulesConstants.ExponentConstants,
    ...createMockConstants(),
  },
};

jest.mock('expo-file-system', () => ({
  FileSystem: {
    downloadAsync: jest.fn(() => Promise.resolve({ md5: 'md5', uri: 'uri' })),
    getInfoAsync: jest.fn(() => Promise.resolve({ exists: true, md5: 'md5', uri: 'uri' })),
    readAsStringAsync: jest.fn(),
    writeAsStringAsync: jest.fn(),
    deleteAsync: jest.fn(),
    moveAsync: jest.fn(),
    copyAsync: jest.fn(),
    makeDirectoryAsync: jest.fn(),
    readDirectoryAsync: jest.fn(),
    createDownloadResumable: jest.fn(),
  },
}));

jest.mock('react-native/Libraries/Image/AssetRegistry', () => ({
  registerAsset: jest.fn(() => 1),
  getAssetByID: jest.fn(() => ({
    fileSystemLocation: '/full/path/to/directory',
    httpServerLocation: '/assets/full/path/to/directory',
    scales: [1],
    fileHashes: ['md5'],
    name: 'name',
    exists: true,
    type: 'type',
    hash: 'md5',
    uri: 'uri',
    width: 1,
    height: 1,
  })),
}));

jest.mock('react-native-gesture-handler', () => {
  const View = require('react-native/Libraries/Components/View/View');
  return {
    Swipeable: View,
    DrawerLayout: View,
    State: {},
    ScrollView: View,
    Slider: View,
    Switch: View,
    TextInput: View,
    ToolbarAndroid: View,
    ViewPagerAndroid: View,
    DrawerLayoutAndroid: View,
    WebView: View,
    NativeViewGestureHandler: View,
    TapGestureHandler: View,
    FlingGestureHandler: View,
    ForceTouchGestureHandler: View,
    LongPressGestureHandler: View,
    PanGestureHandler: View,
    PinchGestureHandler: View,
    RotationGestureHandler: View,
    /* Buttons */
    RawButton: View,
    BaseButton: View,
    RectButton: View,
    BorderlessButton: View,
    /* Other */
    FlatList: View,
    gestureHandlerRootHOC: jest.fn(),
    Directions: {},
  };
});

jest.doMock('react-native/Libraries/BatchedBridge/NativeModules', () => mockNativeModules);

jest.mock('expo-react-native-adapter', () => {
  const ExpoReactNativeAdapter = require.requireActual('expo-react-native-adapter');
  const { NativeModulesProxy } = ExpoReactNativeAdapter;

  // After the NativeModules mock is set up, we can mock NativeModuleProxy's functions that call
  // into the native proxy module. We're not really interested in checking whether the underlying
  // method is called, just that the proxy method is called, since we have unit tests for the
  // adapter and believe it works correctly.
  //
  // NOTE: The adapter validates the number of arguments, which we don't do in the mocked functions.
  // This means the mock functions will not throw validation errors the way they would in an app.
  for (const moduleName of Object.keys(NativeModulesProxy)) {
    const nativeModule = NativeModulesProxy[moduleName];
    for (const propertyName of Object.keys(nativeModule)) {
      if (typeof nativeModule[propertyName] === 'function') {
        nativeModule[propertyName] = jest.fn(async () => {});
      }
    }
  }

  return ExpoReactNativeAdapter;
});

Любые обновления? У меня возникла эта проблема.

Elio Briceño 15.12.2020 13:25
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
1 267
0

Другие вопросы по теме