aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.42k stars 2.12k forks source link

Jest tests failing - "Ensure `@react-native-async-storage/async-storage` is installed and linked." #12829

Closed mattekelly closed 8 months ago

mattekelly commented 8 months ago

Before opening, please confirm:

JavaScript Framework

React Native

Amplify APIs

Authentication

Amplify Version

v6

Amplify Categories

auth

Backend

Other

Environment information

``` # Put output below this line System: OS: macOS 14.1.1 CPU: (8) arm64 Apple M1 Pro Memory: 84.69 MB / 16.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 18.17.0 - ~/.nvm/versions/node/v18.17.0/bin/node npm: 10.2.3 - ~/.nvm/versions/node/v18.17.0/bin/npm Watchman: 2023.05.08.00 - /opt/homebrew/bin/watchman Browsers: Brave Browser: 120.1.61.114 Safari: 17.1 npmPackages: @apollo/client: 3.8.8 => 3.8.8 @apollo/client/cache: undefined () @apollo/client/core: undefined () @apollo/client/dev: undefined () @apollo/client/errors: undefined () @apollo/client/link/batch: undefined () @apollo/client/link/batch-http: undefined () @apollo/client/link/context: undefined () @apollo/client/link/core: undefined () @apollo/client/link/error: undefined () @apollo/client/link/http: undefined () @apollo/client/link/persisted-queries: undefined () @apollo/client/link/remove-typename: undefined () @apollo/client/link/retry: undefined () @apollo/client/link/schema: undefined () @apollo/client/link/subscriptions: undefined () @apollo/client/link/utils: undefined () @apollo/client/link/ws: undefined () @apollo/client/react: undefined () @apollo/client/react/components: undefined () @apollo/client/react/context: undefined () @apollo/client/react/hoc: undefined () @apollo/client/react/hooks: undefined () @apollo/client/react/parser: undefined () @apollo/client/react/ssr: undefined () @apollo/client/testing: undefined () @apollo/client/testing/core: undefined () @apollo/client/utilities: undefined () @apollo/client/utilities/globals: undefined () @aws-amplify/react-native: ^1.0.10 => 1.0.10 @aws-amplify/rtn-web-browser: ^1.0.10 => 1.0.10 @aws-sdk/client-cognito-identity: ^3.485.0 => 3.485.0 @babel/core: ^7.20.0 => 7.23.7 @babel/preset-env: ^7.20.0 => 7.23.8 @babel/runtime: ^7.20.0 => 7.23.8 @glassbox/react-native-storyboard: ^6.696.1 => 6.698.0 @miblanchard/react-native-slider: ^2.3.1 => 2.3.1 @notifee/react-native: ^5.5.0 => 5.7.0 @project-arbuckle/body-condition-history-schema: ^1.0.0 => v1.0.1 @react-native-async-storage/async-storage: ^1.21.0 => 1.21.0 @react-native-community/checkbox: 0.5.16 => 0.5.16 @react-native-community/eslint-config: ^3.2.0 => 3.2.0 @react-native-community/netinfo: ^11.2.1 => 11.2.1 @react-native-firebase/analytics: 18.7.3 => 18.7.3 @react-native-firebase/app: 18.7.3 => 18.7.3 @react-native-firebase/crashlytics: 18.7.3 => 18.7.3 @react-native-firebase/dynamic-links: 18.7.3 => 18.7.3 @react-native-firebase/messaging: 18.7.3 => 18.7.3 @react-native-firebase/remote-config: 18.7.3 => 18.7.3 @react-native-picker/picker: github:react-native-picker/picker => 2.6.1 @react-navigation/bottom-tabs: 6.5.11 => 6.5.11 @react-navigation/drawer: 6.6.6 => 6.6.6 @react-navigation/native: ^6.0.8 => 6.1.9 @react-navigation/native-stack: 6.9.17 => 6.9.17 @react-navigation/stack: 6.3.20 => 6.3.20 @reduxjs/toolkit: 2.0.1 => 2.0.1 @reduxjs/toolkit-query: 1.0.0 @reduxjs/toolkit-query-react: 1.0.0 @reduxjs/toolkit-react: 1.0.0 @testing-library/jest-native: ^5.4.2 => 5.4.3 @testing-library/react-native: ^12.4.1 => 12.4.3 @tsconfig/react-native: ^2.0.2 => 2.0.3 @types/jest: ^29.2.1 => 29.5.11 @types/luxon: ^3.3.7 => 3.4.0 @types/node: ^18.19.3 => 18.19.5 @types/react: ^18.0.24 => 18.2.47 @types/react-native: ^0.71.13 => 0.71.13 (0.70.19) @types/react-native-vector-icons: ^6.4.18 => 6.4.18 @types/react-redux: 7.1.33 => 7.1.33 @types/react-test-renderer: ^18.0.0 => 18.0.7 @types/redux-mock-store: 1.0.6 => 1.0.6 @types/semver: ^7.3.11 => 7.5.6 @typescript-eslint/eslint-plugin: ^5.3.0 => 5.62.0 @typescript-eslint/parser: ^5.3.0 => 5.62.0 (6.18.1) ApptentiveExample: 6.0.0 GlassboxReactExampleApp: 0.0.1 HelloWorld: 0.0.1 apollo-link-rest: ^0.9.0 => 0.9.0 apptentive-react-native: ^6.1.3 => 6.1.3 aws-amplify: ^6.0.10 => 6.0.10 aws-amplify/adapter-core: undefined () aws-amplify/analytics: undefined () aws-amplify/analytics/kinesis: undefined () aws-amplify/analytics/kinesis-firehose: undefined () aws-amplify/analytics/personalize: undefined () aws-amplify/analytics/pinpoint: undefined () aws-amplify/api: undefined () aws-amplify/api/server: undefined () aws-amplify/auth: undefined () aws-amplify/auth/cognito: undefined () aws-amplify/auth/cognito/server: undefined () aws-amplify/auth/enable-oauth-listener: undefined () aws-amplify/auth/server: undefined () aws-amplify/datastore: undefined () aws-amplify/in-app-messaging: undefined () aws-amplify/in-app-messaging/pinpoint: undefined () aws-amplify/push-notifications: undefined () aws-amplify/push-notifications/pinpoint: undefined () aws-amplify/storage: undefined () aws-amplify/storage/s3: undefined () aws-amplify/storage/s3/server: undefined () aws-amplify/storage/server: undefined () aws-amplify/utils: undefined () babel-jest: ^29.2.1 => 29.7.0 base-64: ^1.0.0 => 1.0.0 buffer: ^6.0.3 => 6.0.3 (4.9.2, 5.7.1) eslint: ^8.19.0 => 8.56.0 eslint-config-prettier: ^7.2.0 => 7.2.0 (8.10.0) eslint-plugin-import: ^2.25.2 => 2.29.1 eslint-plugin-jsx-a11y: ^6.4.1 => 6.8.0 eslint-plugin-prettier: ^3.4.1 => 3.4.1 (4.2.1) eslint-plugin-react: ^7.26.1 => 7.33.2 eslint-plugin-react-hooks: ^4.6.0 => 4.6.0 expo: ^49.0.21 => 49.0.21 expo-barcode-scanner: 12.7.0 => 12.7.0 expo-camera: ~13.6.0 => 13.6.0 expo-file-system: ~15.6.0 => 15.6.0 (15.4.5) expo-sharing: 11.7.0 => 11.7.0 graphql: 14.7.0 => 14.7.0 (15.8.0) jest: ^29.2.1 => 29.7.0 jest-junit: ^16.0.0 => 16.0.0 jest-junit-reporter: ^1.1.0 => 1.1.0 jest-serializer: ^28.0.0 => 28.0.0 (27.5.1) lodash: ^4.17.21 => 4.17.21 luxon: 3.4.4 => 3.4.4 metro-react-native-babel-preset: 0.73.10 => 0.73.10 (0.76.8) patch-package: ^6.4.7 => 6.5.1 prettier: ^2.4.1 => 2.8.8 react: 18.2.0 => 18.2.0 react-native: 0.71.14 => 0.71.14 react-native-barcode-mask: ^1.2.4 => 1.2.4 react-native-ble-plx: 3.1.2 => 3.1.2 react-native-config: 1.5.1 => 1.5.1 react-native-date-picker: 4.3.4 => 4.3.4 react-native-gesture-handler: ^2.9.0 => 2.14.0 react-native-get-random-values: ^1.10.0 => 1.10.0 react-native-inappbrowser-reborn: ^3.7.0 => 3.7.0 react-native-localize: 3.0.4 => 3.0.4 react-native-modal: 13.0.1 => 13.0.1 react-native-navigation-bar-color: 2.0.2 => 2.0.2 react-native-paper: ^4.11.1 => 4.12.8 react-native-permissions: 3.10.1 => 3.10.1 react-native-reanimated: ^2.14.1 => 2.17.0 react-native-restart: 0.0.27 => 0.0.27 react-native-safe-area-context: 4.8.0 => 4.8.0 react-native-screens: 3.29.0 => 3.29.0 react-native-splash-screen: 3.3.0 => 3.3.0 react-native-svg: 14.1.0 => 14.1.0 react-native-svg-transformer: 1.1.0 => 1.1.0 react-native-url-polyfill: 2.0.0 => 2.0.0 react-native-vector-icons: ^10.0.2 => 10.0.3 react-native-version-number: ^0.3.6 => 0.3.6 react-native-webview: 11.23.1 => 11.23.1 react-native-youtube-iframe: ^2.3.0 => 2.3.0 react-redux: 9.0.4 => 9.0.4 react-test-renderer: 18.2.0 => 18.2.0 reactotron-react-native: ^5.0.4 => 5.0.4 reactotron-redux: ^3.1.4 => 3.1.4 redux-mock-store: ^1.5.4 => 1.5.4 redux-persist: ^6.0.0 => 6.0.0 redux-persist/integration/react: undefined () semver: 7.5.4 => 7.5.4 (6.3.1, 5.7.2, 7.5.3, 7.3.2) ts-jest: ^29.1.1 => 29.1.1 typescript: 4.8.4 => 4.8.4 (4.9.5, 5.3.3) unimported: ^1.29.2 => 1.31.1 victory-native: 36.7.0 => 36.7.0 webdriverio: ^7.19.7 => 7.34.0 npmGlobalPackages: corepack: 0.18.0 npm: 10.2.3 ```

Describe the bug

After upgrading to Amplify V6, a vast number of our jest tests are failing due to the following error. We are already using "@react-native-async-storage/async-storage": "^1.21.0". This appears to be originating from the loadAsyncStorage.ts file.

Ensure `@react-native-async-storage/async-storage` is installed and linked.

Expected behavior

Jest tests successful run as they did prior to upgrading to V6.

Reproduction steps

  1. Attempt to execute a jest unit test in a project utilizing @react-native-async-store and aws-amplify

Code Snippet

// Put your code below this line.

Log output

``` // Put your logs below this line ā— Test suite failed to run Ensure `@react-native-async-storage/async-storage` is installed and linked. > 1 | import { signInWithRedirect, signOut, fetchAuthSession, getCurrentUser } from 'aws-amplify/auth'; | ^ 2 | import { Hub } from 'aws-amplify/utils'; 3 | import { Amplify } from 'aws-amplify'; 4 | import { AuthenticationServiceInterface } from './AuthenticationServiceInterface'; at loadAsyncStorage (node_modules/@aws-amplify/react-native/src/moduleLoaders/loadAsyncStorage.ts:22:9) at new DefaultStorage (node_modules/@aws-amplify/core/src/storage/DefaultStorage.native.ts:15:1) at Object. (node_modules/@aws-amplify/core/dist/cjs/storage/index.js:20:26) at Object. (node_modules/@aws-amplify/core/dist/cjs/index.js:67:17) at Object. (node_modules/@aws-amplify/auth/src/providers/cognito/apis/signUp.ts:8:7) at Object. (node_modules/@aws-amplify/auth/src/providers/cognito/index.ts:8:5) at Object. (node_modules/@aws-amplify/auth/src/index.ts:9:8) at Object. (node_modules/aws-amplify/dist/cjs/auth/index.js:12:22) ```

aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

HuiSF commented 8 months ago

I think the jest module loader cannot resolve the @react-native-async-storage/async-storage package in its module system.

Looking at the stack trace, if your test does not depend on the @aws-amplify/react-native, you can probably add the following to prevent this from happening.

jest.mock('`@aws-amplify/react-native')
mattekelly commented 8 months ago

@HuiSF thanks for the suggestion. I just tried that, as the majority of the tests do not depend upon amplify. That caused a different error:

TypeError: Cannot destructure property 'encode' of '(0 , react_native_1.loadBase64)(...)' as it is undefined.
HuiSF commented 8 months ago

This particular requires to mock the loadBase64 function exported from the @aws-amplify/react-native package šŸ¤”

I noticed your test suites load .native implementation, have you configured the jest to mimic a react-native environment?

mattekelly commented 8 months ago

@HuiSF mocking loadBase64 kicks off a seemingly endless list of other things that need to be mocked.

We are using the 'react-native' preset for jest so yes, it is set to mimic a react-native environment.

mattekelly commented 8 months ago

I ended up mocking all of the following:

jest.mock('@aws-amplify/react-native', () => ({
  loadBase64: jest.fn().mockImplementation(() => ({
    encode: jest.fn(),
  })),
  loadGetRandomValues: jest.fn(),
  loadUrlPolyfill: jest.fn(),
  loadAsyncStorage: jest.fn(),
  loadAppState: jest.fn(),
}));

Now I am stuck here:

  ā— Test suite failed to run

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

    > 1 | import { signInWithRedirect, signOut, fetchAuthSession, getCurrentUser } from 'aws-amplify/auth';
        | ^
      2 | import { Hub } from 'aws-amplify/utils';
      3 | import { Amplify } from 'aws-amplify';
      4 | import { AuthenticationServiceInterface } from './AuthenticationServiceInterface';

      at new SessionListener (node_modules/@aws-amplify/core/src/utils/sessionListener/SessionListener.native.ts:16:38)
      at Object.<anonymous> (node_modules/@aws-amplify/core/dist/cjs/utils/sessionListener/index.js:23:27)
      at Object.<anonymous> (node_modules/@aws-amplify/core/src/providers/pinpoint/apis/record.ts:10:26)
      at Object.<anonymous> (node_modules/@aws-amplify/core/dist/cjs/providers/pinpoint/apis/index.js:16:16)
      at Object.<anonymous> (node_modules/@aws-amplify/core/src/providers/pinpoint/index.ts:9:22)
      at Object.<anonymous> (node_modules/@aws-amplify/core/src/ServiceWorker/ServiceWorker.ts:12:1)
      at Object.<anonymous> (node_modules/@aws-amplify/core/dist/cjs/ServiceWorker/index.js:9:23)
      at Object.<anonymous> (node_modules/@aws-amplify/core/dist/cjs/index.js:117:23)
      at Object.<anonymous> (node_modules/@aws-amplify/auth/src/providers/cognito/apis/signUp.ts:8:7)
      at Object.<anonymous> (node_modules/@aws-amplify/auth/src/providers/cognito/index.ts:8:5)
      at Object.<anonymous> (node_modules/@aws-amplify/auth/src/index.ts:9:8)
      at Object.<anonymous> (node_modules/aws-amplify/dist/cjs/auth/index.js:12:22)
      at Object.require (src/services/authentication/AuthenticationService.ts:1:1)
HuiSF commented 8 months ago

For this particular error, would need this

jest.mock('@aws-amplify/react-native', () => ({
  loadBase64: jest.fn().mockImplementation(() => ({
    encode: jest.fn(),
  })),
  loadGetRandomValues: jest.fn(),
  loadUrlPolyfill: jest.fn(),
  loadAsyncStorage: jest.fn(),
  loadAppState: jest.fn(() => ({
    addEventListener: jest.fn(),
  })),
}));

This is a bit tricky like you said, as jest is going to evaluate everything on the module loading paths.

Do your unit tests actually depend on the actual functionalities of Amplify libraries?

I would mock the Amplify APIs in this case if I only needed to test the business logic in my own code, e.g verify the params gets passed to Amplify API. In this case, aws-amplify/auth, aws-amplify/utils and aws-amplify would get a mock.

mattekelly commented 8 months ago

Thank you - I could not figure out how/where to mock addEventListener. I copied in your mock, line for line, and all of our tests began passing. I will keep in mind the potential need to mock Amplify APIs, should we encounter similar errors in the future.

cwomack commented 8 months ago

Awesome help and diagnosis with this @HuiSF! @mattekelly, any further tests failures or help needed on this?

mattekelly commented 8 months ago

@cwomack no, thank you. I do still have an open question on my other issue 12816.

cwomack commented 8 months ago

Sounds good, we'll get to that one then and close this out. Thanks, @mattekelly!

OOCAZ commented 7 months ago

Hey all, I am a little late to the party but I am experiencing the same issue mentioned above, and then I try to use the fixes listed from HuiSF and I get a weird error that is not leaving me alone...

C:\myUser\myproject\node_modules\uuid\dist\esm-browser\index.js:1
({"Object.":function(module,exports,require,dirname,filename,jest){export { default as v1 } from './v1.js'; ^^^^^^

SyntaxError: Unexpected token 'export'.

Appears to be springing from the Amplify generateClient import on the file I am testing, I am not using Amplify in any way in my test file. The only difference I may have is I am using Expo and not Vanilla React-Native. But I have tried using the preset of react-native and jest-expo, and both of them are giving me this same error. If I remove the mock suggested by HuiSF I am left with the original error above. Please advise, and thank you for your time.

victoralmeidadev commented 5 months ago

Hey guys.

I did all the above steps and also added the directory settings in my jest.config.js file.

...
transformIgnorePatterns: [
'...|aws-amplify/.*|aws-amplify|@aws-amplify/react-native|...'
],
...
moduleNameMapper: {
    'aws-amplify': '<rootDir>/node_modules/aws-amplify',
    'aws-amplify/auth': '<rootDir>/node_modules/aws-amplify/auth',
    '@aws-amplify/react-native':
      '<rootDir>/node_modules/@aws-amplify/react-native',
     ...
},
...

This worked in my project that uses Expo SDK 50 + Expo Dev Client.

Hope it works. If you notice any problems with this configuration, let me know so I can fix it in my project.