Настройка Jest для Native-Base на Nextjs с React-Native-Web


TLDR

Вот готовый рабочий пример того, как настроить jest для работы с native-base на nextjs и react-native-web. https://github.com/MiguelNiblock/next-jest-nativebase

Вступление

Native-base — это моя любимая библиотека ui компонентов для react-native. Она отлично работает в вебе, и ее легко настроить с помощью expo или даже react-native-web. Однако тестирование с помощью jest становится экспоненциально более сложным, когда вы добавляете эту библиотеку в свой проект. Настройка тестирования больше не является тривиальной, вам действительно нужно знать, что вы делаете, как вы увидите ниже. В поисках решений в интернете я наткнулся на множество комментариев, в которых люди говорят, что лучше отказаться от использования любимой библиотеки ui, чем от надлежащего тестирования. Я хотел бы написать эту статью с несколькими целями:

  • Помочь другим людям, столкнувшимся с подобной ситуацией в стремлении создать универсальное react-native приложение.
  • Призвать создателей библиотек упростить жизнь своим пользователям, либо улучшив документацию, либо поставляя предварительно скомпилированный код.

Справочная информация

Я установил nativebase на проект nextjs, управляемый nx monorepo, и тест jest не смог отрисовать приложение из-за проблем с safeareacontext.

Я уже прочитал статью о тестировании в документации, но она мне не помогла. https://docs.nativebase.io/testing потому что когда я добавляю mock из setupFile.js, он также жалуется, что не может использовать оператор import вне модуля, это происходит, когда mock файл из safeareacontext импортирует react.

Поскольку nx делает вещи более сложными, я делаю минимальное воспроизведение проблемы тестирования, в надежде, что я могу получить минимальный образец, чтобы работать правильно с некоторой помощью.

шаблоны, предоставляемые nativebase, не имеют конфигурации jest. ни один из них. вы вообще тестируете свою библиотеку?. https://github.com/GeekyAnts/nativebase-templates

nextjs поставляется с базовой настройкой jest, поэтому я буду следовать инструкциям по установке на существующий проект nextjs. есть один, который использует expo адаптер: https://docs.nativebase.io/install-next и другой, который использует native-base next адаптер https://docs.nativebase.io/next-adapter . Я не знаю, в чем разница, но не думаю, что это должно повлиять на тестирование. Я предпочитаю использовать адаптер native-base/next, так как в моем nx monorepo проект nextjs полностью независим от проекта react-native. Если вы используете expo, возможно, сделайте другую версию.

Исследование похожих проблем

Это имеет потенциальное решение путем добавления шаблонов transformignore

https://github.com/GeekyAnts/NativeBase/issues/3045#issuecomment-580956513

здесь говорится о начальных метриках окна

https://github.com/GeekyAnts/NativeBase/issues/3907

здесь также есть проблема «cannot import statement outside a module».

потенциальное решение jest config https://github.com/GeekyAnts/NativeBase/issues/3105#issuecomment-886034272

другой пример добавления transformIgnorepattern для решения проблемы импорта https://github.com/GeekyAnts/NativeBase/issues/3001#issuecomment-718617355

та же проблема, нет решения https://github.com/GeekyAnts/NativeBase/issues/1215

та же проблема https://stackoverflow.com/questions/45197514/unexpected-token-import-error-crna

другой пример трансформации игнорепаттерна, который помог некоторым людям https://github.com/GeekyAnts/NativeBase/issues/977#issuecomment-322541757

https://github.com/GeekyAnts/NativeBase/issues/977#issuecomment-353689768

https://github.com/GeekyAnts/NativeBase/issues/977#issuecomment-354403658

«правильный конфиг для 2022» https://stackoverflow.com/a/71575172

или настройка jest для esmodules

https://jestjs.io/docs/ecmascript-modules

но он не поддерживает jest.mock в es модуле для jest

классный синтаксис для создания шаблона https://github.com/nrwl/nx/issues/812#issuecomment-429420861

https://github.com/nrwl/nx/issues/812#issuecomment-429488470

специфическое решение для nx с allowJs https://github.com/nrwl/nx/issues/812#issuecomment-429467823

с nx и babel preset env https://github.com/nrwl/nx/issues/812#issuecomment-799930598

статья о jest из документации nextjs https://nextjs.org/docs/testing#setting-up-jest-with-babel

Начнем с шаблона nextjs, готового для jest

Итак, я создам приложение nextjs из шаблона «with-jest-babel». https://github.com/vercel/next.js/tree/canary/examples/with-jest-babel

тесты изначально проходят. приложение запускается.

Это простой тест по умолчанию, который я буду запускать в следующих разделах:

describe('Home', () => {
  it('renders a heading', () => {
    render(<Home />)

    const heading = screen.getByRole('heading', {
      name: /welcome to next.js!/i,
    })

    expect(heading).toBeInTheDocument()
  })
})
Вход в полноэкранный режим Выход из полноэкранного режима

Установка нативной базы на существующий nextjs

Теперь я буду следовать инструкциям по установке native-base с адаптером next. Следуйте либо этому https://docs.nativebase.io/next-adapter, либо этому https://docs.nativebase.io/install-next.

yarn add react-native-web native-base react-native-svg react-native-safe-area-context react-native

yarn add @native-base/next-adapter next-compose-plugins next-transpile-modules next-fonts -D

создайте pages/_document.ts.

export { default } from "@native-base/next-adapter/document"

создайте next.config.js с начальной настройкой адаптера. Шаблон не поставлялся с ним, а в инструкции к native-base сказано next.config.json. но это неправильно, это «js».

настройте пример hello world в _app.tsx и index.tsx.


Запуск next после установки native-base

с этими изменениями, начиная с пустого nextjs conf, теперь приложение nextjs не запускается.

ошибка при запуске yarn dev (next dev) :

Server Error

Error: "Document.getInitialProps()" should resolve to an object with a "html" prop set with a valid html string
This error happened while generating the page. Any console logs will be displayed in the terminal window.
Call Stack
documentInitialProps
file:/home/user/MyProjects/next-jest-nativebase/node_modules/next/dist/server/render.js (822:23)
Вход в полноэкранный режим Выход из полноэкранного режима

Честно говоря, я ожидал, что Next хотя бы запустится после установки адаптера next. Теперь мы должны сначала исправить Next, а затем исправить Jest, чтобы он работал с native-base. Невесело, но сдаваться не хочется.

Я нашел несколько решений в интернете и попытался изменить документ из native-base, но уперся в стену, так что в итоге я удалил _document.js вообще, после чего Nextjs смог собрать с текстом hello world из native-base.

Возможно, это следует исправить и сделать правильно, но это уже другая кроличья нора.

Теперь вернемся к тестированию.

Jest после установки native-base

выдает ошибку:

/home/user/MyProjects/next-jest-nativebase/node_modules/react-native/index.js:14
    import typeof AccessibilityInfo from './Libraries/Components/AccessibilityInfo/AccessibilityInfo';
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module
Enter fullscreen mode Выход из полноэкранного режима

Это означает, что он пытается импортировать материал из react-native. Я не уверен, что решение заключается в псевдониме react-native на react-native-web, или что решение заключается в добавлении react-native в transformIgnorePatterns.

Попытки:

1.

transformIgnorePatterns: "/node_modules/?!(react-native)".

SyntaxError: /home/user/MyProjects/next-jest-nativebase/node_modules/react-native/index.js: Unexpected token, expected "{" (14:7)

      12 |
      13 | // Components
    > 14 | import typeof AccessibilityInfo from './Libraries/Components/AccessibilityInfo/AccessibilityInfo';
         |        ^
      15 | import typeof ActivityIndicator from './Libraries/Components/ActivityIndicator/ActivityIndicator';
Вход в полноэкранный режим Выйти из полноэкранного режима

ошибка была похожей, но другой. похоже, что теперь это проблема с транспонированием типов. react-native использует flow для типов, так что, возможно, мне нужен пресет или плагин, который транспонирует flow.

https://babeljs.io/docs/en/babel-preset-flow.html

2.

Решил добавить предложение в документации react-native-web для Jest seup, и добавить jest config preset: 'react-native-web'. Ошибка стала другой, но также говорит о том, что Flow должен быть транспонирован

SyntaxError: /home/user/MyProjects/next-jest-nativebase/node_modules/react-native/Libraries/Utilities/codegenNativeComponent.js: Unexpected token, expected "from" (14:12)

      12 |
      13 | import requireNativeComponent from '../../Libraries/ReactNative/requireNativeComponent';
    > 14 | import type {HostComponent} from '../../Libraries/Renderer/shims/ReactNativeTypes';
         |             ^
      15 | import UIManager from '../ReactNative/UIManager';
Войдите в полноэкранный режим Выйти из полноэкранного режима

3.

Решил добавить пресет «@babel/preset-flow». ошибка теперь такая:

Cannot find module '../Utilities/Platform' from 'node_modules/react-native/Libraries/StyleSheet/processColor.js'

    Require stack:
      node_modules/react-native/Libraries/StyleSheet/processColor.js
      node_modules/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js
      node_modules/react-native/Libraries/ReactNative/getNativeComponentAttributes.js
      node_modules/react-native/Libraries/ReactNative/requireNativeComponent.js
      node_modules/react-native/Libraries/Utilities/codegenNativeComponent.js
      node_modules/react-native-safe-area-context/lib/commonjs/specs/NativeSafeAreaProvider.js
      node_modules/react-native-safe-area-context/lib/commonjs/NativeSafeAreaProvider.js
      node_modules/react-native-safe-area-context/lib/commonjs/SafeAreaContext.js
      node_modules/react-native-safe-area-context/lib/commonjs/index.js
      node_modules/native-base/lib/commonjs/hooks/useSafeArea/index.js
      node_modules/native-base/lib/commonjs/hooks/index.js
      node_modules/native-base/lib/commonjs/components/primitives/Box/index.js
      node_modules/native-base/lib/commonjs/components/composites/AspectRatio/index.js
      node_modules/native-base/lib/commonjs/components/composites/index.js
      node_modules/native-base/lib/commonjs/index.js
      pages/index.tsx
      __tests__/index.test.tsx


    However, Jest was able to find:
        '../Utilities/Platform.android.js'
        '../Utilities/Platform.ios.js'

    You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['js', 'mjs', 'cjs', 'jsx', 'ts', 'tsx', 'json', 'node'].

    See https://jestjs.io/docs/configuration#modulefileextensions-arraystring
Войти в полноэкранный режим Выход из полноэкранного режима

интересно. получается, что Box использует safearea, который использует некоторые утилиты react-native. и предоставляются только родные (мобильные).

  • Это говорит о том, что проблема типа отпала, и теперь проблема в том, что этим react-native libs нет места в Next web build. Так что, возможно, нам придется вручную присвоить псевдоним react-native-web здесь в jest.
  • Или поиздеваться над safearea.

4.

Просто в качестве эксперимента я отключил предустановку ‘react-native-web’, (Чтобы посмотреть, действительно ли она что-то делает), и ошибка была такой:

ReferenceError: __DEV__ is not defined

       5 |
       6 | import React from "react"
    >  7 | import { Box } from "native-base"
         | ^
       8 |
       9 | export default function Home() {
      10 |   return (
Войдите в полноэкранный режим Выход из полноэкранного режима

5.

Я отменил это и добавил псевдоним mapper имени модуля в jest, как предложено в документации react-native-web

https://necolas.github.io/react-native-web/docs/multi-platform/

moduleNameMapper: {
    ...

    "react-native$": "react-native-web",

    ...
  },
Войти в полноэкранный режим Выйти из полноэкранного режима

получил ту же ошибку, что и в попытке #3. Это означает, что мне нужно поиздеваться над safearea.

Есть документация nb: https://docs.nativebase.io/testing, но как мне это реализовать? Я не тестирую провайдера специально, я просто хочу убедиться, что все приложение отображается правильно. Компонент Home вызывает safearea из внутренней зависимости native-base компонента Box -> useSafeArea -> SafeAreaContext.

Авторы предоставляют дополнительную информацию https://github.com/th3rdwave/react-native-safe-area-context#testing.

они предлагают добавить это в установочный файл jest.

import mockSafeAreaContext from 'react-native-safe-area-context/jest/mock';

jest.mock('react-native-safe-area-context', () => mockSafeAreaContext);
Вход в полноэкранный режим Выход из полноэкранного режима

6.

Добавил mock, как указано, и получил эту ошибку:

Cannot find module '../Utilities/Platform' from 'node_modules/react-native/Libraries/StyleSheet/processColor.js'

    Require stack:
      node_modules/react-native/Libraries/StyleSheet/processColor.js
      node_modules/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js
      node_modules/react-native/Libraries/ReactNative/getNativeComponentAttributes.js
      node_modules/react-native/Libraries/ReactNative/requireNativeComponent.js
      node_modules/react-native/Libraries/Utilities/codegenNativeComponent.js
      node_modules/react-native-safe-area-context/lib/commonjs/specs/NativeSafeAreaProvider.js
      node_modules/react-native-safe-area-context/lib/commonjs/NativeSafeAreaProvider.js
      node_modules/react-native-safe-area-context/lib/commonjs/SafeAreaContext.js
      node_modules/react-native-safe-area-context/lib/commonjs/index.js
      node_modules/react-native-safe-area-context/jest/mock.js
      jest.setup.js


    However, Jest was able to find:
        '../Utilities/Platform.android.js'
        '../Utilities/Platform.ios.js'

    You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['js', 'mjs', 'cjs', 'jsx', 'ts', 'tsx', 'json', 'node'].

    See https://jestjs.io/docs/configuration#modulefileextensions-arraystring
Войти в полноэкранный режим Выход из полноэкранного режима

Это та же ошибка, что и раньше (попытка №5), но на этот раз она инициируется из файла настроек, это единственное отличие. проблема в том, что макет использует мобильный код, а в вебе ему нет замены.

Интересно отметить, что псевдоним имени react-native на react-native-web не соблюдается согласно стеку вызовов.

7.

Затем я скорректировал шаблон связующего имени модуля. Из "react-native$": "react-native-web", к "react-native": "react-native-web", (просто убрал «$»).

получил новую ошибку:

TypeError: Cannot read property 'default' of undefined

       8 | import mockSafeAreaContext from "react-native-safe-area-context/jest/mock"
       9 |
    > 10 | jest.mock("react-native-safe-area-context", () => mockSafeAreaContext)
         |                                                   ^
      11 |
Войдите в полноэкранный режим Выход из полноэкранного режима

Эта разница означает, что «$» действительно препятствовал разрешению rnw вместо react-native.

В этой ошибке не так много информации, поэтому сначала я был в некотором замешательстве. Но после просмотра структуры пакета rnw, я думаю, что это просто потому, что в модуле rnw нет такого каталога, эквивалентного этому, node_modules/react-native/Libraries/Utilities/codegenNativeComponent.js , (т.е. это не должно приходить из react-native-web, вообще), как ожидается от react-native.

Возможно, если бы rnw был нативным typescript, мы могли бы получить более красивый стек вызовов и здесь, но он написан на flow, поэтому я полагаю, что именно поэтому нет более подробного сообщения об ошибке.

Я думаю, это проблема с контекстом безопасной области, загружающим нативную версию вместо веб-версии .https://github.com/necolas/react-native-web/issues/2292#issuecomment-1129340357 . necolas (автор react-native-web) сказал:

Ваш бандлер должен использовать веб-реализацию react-native-safe-area-context вместо нативного экспорта. Если у вас по-прежнему возникают проблемы с загрузкой веб-реализации, вам следует открыть проблему в их репозитории.

Проблема с предоставленным имитатором заключается в том, что он по-прежнему загружает реальный компонент, включая нативный код.

const RNSafeAreaContext = jest.requireActual('react-native-safe-area-context');

Вход в полноэкранный режим Выйдите из полноэкранного режима

после осмотра файлов в пакете safe area, в стеке вызовов есть один, NativeSafeAreaProvider, который имеет расширения файловой платформы. но тот, который импортируется в стеке вызовов при ошибке (попытка #6), был по умолчанию простым «.js», тогда как для наших целей это должна быть версия «.web.js», возможно.

8.

Поэтому давайте скажем Jest, что мы хотим приоритетно импортировать .web. версию любого файла.

установим параметр jest moduleFileExtensions в moduleFileExtensions: ["web.tsx", "tsx", "web.ts", "ts", "web.jsx", "jsx", "web.js", "js"],.

Также удалите псевдоним rnw вообще. // "react-native": "react-native-web",.

новая ошибка:

`useTheme` must be used within `NativeBaseConfigProvider`

      4 | describe('Home', () => {
      5 |   it('renders a heading', () => {
    > 6 |     render(<Home />)
        |     ^
      7 |
      8 |     const heading = screen.getByRole('heading', {
      9 |       name: /welcome to next.js!/i,

      at useContext (node_modules/native-base/lib/commonjs/utils/createContext.tsx:22:13)
      at Object.<anonymous> (__tests__/index.test.tsx:6:5)
Вход в полноэкранный режим Выход из полноэкранного режима

похоже, что это просто естественная ошибка использования native-base, так что это должно означать, что он работает.

Сейчас ошибка возникает потому, что я пытаюсь отрисовать элемент native-base, без провайдера. https://github.com/GeekyAnts/NativeBase/issues/4303#issuecomment-975835832.

9.

Теперь я добавлю провайдер в тест.

  it("renders a heading", () => {
    render(
      <NativeBaseProvider>
        <Home />
      </NativeBaseProvider>
    )
Вход в полноэкранный режим Выход из полноэкранного режима

И НАКОНЕЦ-ТО ВСЕ РАБОТАЕТ!!!!!!!! Я ОФИЦИАЛЬНО ПРОВЕЛ УСПЕШНЫЙ ТЕСТ С NATIVE-BASE НА NEXTJS, НАСТРОИВ ВСЕ КИШКИ ИЗ JEST.

Я заслужил пиво. Я делал это весь день в день памяти.

Оцените статью
Procodings.ru
Добавить комментарий