braze-inc / braze-web-sdk

Public repo for the Braze Web SDK
https://www.braze.com
Other
71 stars 25 forks source link

[Bug]: Jest errors integrating SDK 4.0.2 #121

Closed concrete-mixer closed 2 years ago

concrete-mixer commented 2 years ago

Braze Web SDK Version

4.0.2

Integration Method

NPM

Browser

Other

Steps To Reproduce

Upgrade @braze/web-sdk package to v4.0.2.

Invoke existing jest test suite via yarn cli: yarn jest.

Expected Behavior

Tests to run without error.

Actual Incorrect Behavior

Tests failed to run when trying to import braze ES module (suite ran fine with SDK version 3.5.0).

Verbose Logs

FAIL  src/plib/form/validate.test.ts
  ● Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/en/ecmascript-modules for how to enable it.
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

   node_modules/@braze/web-sdk/src/index.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){export*from"./Card/index.js";export*from"./ContentCards/index.js";export*from"./Core/index.js";export*from"./Feed/index.js";export*from"./InAppMessage/index.js";export*from"./Push/index.js";export*from"./User/index.js";export{WindowUtils}from"./util/window-utils.js";
                                                                                             ^^^^^^

    SyntaxError: Unexpected token 'export'

    > 1 | import * as braze from '@braze/web-sdk'
        | ^
      2 | import {Model} from '~/api/types'
      3 | import config from '~/config'
      4 |

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1350:14)
      at Object.<anonymous> (src/global/braze.ts:1:1)

Additional Information

Selected package versions:

"@braze/web-sdk": "^4.0.2",
"jest": "^26.4.2",
"ts-jest": "^26.3.0",  // maybe relevant?
"@babel/core": "^7.11.6",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.11.5",
"@babel/preset-env": "^7.11.5",
"@babel/preset-react": "^7.10.4",
"@babel/runtime": "^7.11.2",
"babel-loader": "8.1.0",
"ts-loader": "^8.0.3",
"ts-node": "^10.3.1",
"webpack": "^4.44.1",
"typescript": "4.6.3",

Babel config:

  "babel": {
    "plugins": [
      "@babel/plugin-syntax-dynamic-import",
      "@babel/plugin-transform-runtime"
    ],
    "presets": [
      "@babel/react",
      [
        "@babel/preset-env",
        {
          "targets": {
            "chrome": "67",
            "firefox": "60",
            "edge": "16",
            "ios": "9",
            "safari": "11"
          }
        }
      ]
    ]
  },

Jest config:

module.exports = {
    preset: 'ts-jest',
    testEnvironment: 'jsdom',
    moduleNameMapper: {
        '^.+\\.(svg|gif|png|eot|ttf|woff|woff2|jpg)$': '<rootDir>/test/jestMockFiles.ts',
        '^.+\\.(scss|css)$': '<rootDir>/test/jestMockStyles.ts',
        '^~(.*)$': '<rootDir>/src$1',
    },
    modulePaths: ['<rootDir>'],
    coverageDirectory: '<rootDir>/test/coverage',
    collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/**/*.d.ts', '!**/node_modules/**'],
    setupFiles: ['jest-canvas-mock'],
    setupFilesAfterEnv: ['./test/jestSetup.ts'],
    globals: {
        NODE_ENV: 'test',
    },
    clearMocks: true,
}

I'm not familiar with Jest configuration, but am aware it is a common package. Has anyone else experienced similar problems integrating Braze SDK 4.0.2 with Jest, have they managed to resolve them, and if so how? So far I've tried:

spvismaya commented 2 years ago

Hi @concrete-mixer ! Based on the info you provided, the transformIgnorePatterns config in your jest.config.js file is perhaps set incorrectly.

In order to transform everything inside @braze/web-sdk path as expected, you may have to set the config to be as follows:

"transformIgnorePatterns": [
      "/node_modules/(?!@braze/web-sdk/)"
    ],

Could you please try this out, and let us know if it helps resolve the issue?

For reference: https://jestjs.io/docs/configuration#transformignorepatterns-arraystring

concrete-mixer commented 2 years ago

Hello @spvismaya! Thanks for your reply; I tried your suggested code, but unfortunately the same errors resulted.

spvismaya commented 2 years ago

Hi @concrete-mixer ! I tried to reproduce the issue on my end by creating a sample app with the configs you provided. My apologies - the config should not be injest.config.js. I was able to resolve the issue by adding the transformIgnorePatterns I suggested in package.json file.

Here's my sample package.json file:

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "^13.2.0",
    "@testing-library/user-event": "^13.5.0",
    "@types/jest": "^27.5.1",
    "@types/node": "^16.11.36",
    "@types/react": "^18.0.9",
    "@types/react-dom": "^18.0.4",
    "react": "^18.1.0",
    "react-dom": "^18.1.0",
    "react-scripts": "5.0.1",
    "typescript": "^4.6.3",
    "web-vitals": "^2.1.4",
    "@braze/web-sdk": "^4.0.2",
    "jest": "^26.4.2",
    "ts-jest": "^26.3.0",
    "@babel/core": "^7.11.6",
    "@babel/plugin-syntax-dynamic-import": "^7.8.3",
    "@babel/plugin-transform-runtime": "^7.11.5",
    "@babel/preset-env": "^7.11.5",
    "@babel/preset-react": "^7.10.4",
    "@babel/runtime": "^7.11.2",
    "babel-loader": "8.1.0",
    "ts-loader": "^8.0.3",
    "ts-node": "^10.3.1",
    "webpack": "^4.44.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "jest": {
    "transformIgnorePatterns": ["/node_modules/(?!@braze/web-sdk/)"]
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Please give this a shot - thank you!

concrete-mixer commented 2 years ago

Hi @spvismaya thanks again for getting back. Again the result is the same. It looks like transformIgnorePatterns is being ignored.

I've tried mocking the module by creating __mocks__/@braze/web-sdk/src/index.js with the following code:

const braze = jest.createMockFromModule('@braze/web-sdk')

braze.initialize = function (key, data) {}
braze.automaticallyShowInAppMessages = function () {}
braze.requestPermission = function () {}
braze.openSession = function () {}
braze.changeUser = function () {}

module.exports = braze

Again this is ignored.

I've tried setting moduleNameMapper and mapping '^.+@braze/web-sdk/src/index.js': '/test/jestMockBraze.ts', where jestMockStyles.ts is:

module.exports = {}

and again same result.

I'm wondering if there's something in our babel config that is bypassing jest settings?

Thanks again for your help!

PS I should add that none of our tests are relying on the braze lib directly; it's more an issue with compiling the app source code.

wesleyorbin commented 2 years ago

Hi @concrete-mixer. I'm not sure why transformIgnorePatterns isn't working for you, but I think I've found a workaround.

First, move your braze code into its own file:

// /lib/braze.js
import { initialize, openSession, logCustomEvent } from "@braze/web-sdk";

export const initializeBraze = (apiKey, options, ...) => {
  braze.initialize(...);
  braze.openSession();
  // rest of initialize code
};

export const logEvent = (eventName) => {
  braze.logCustomEvent(eventName);
}

// other braze functions

Then use these functions in your code and mock them in your test instead of using/mocking the SDK directly:

// App.test.tsx

import { initializeBraze } from "/lib/braze.js";

jest.mock("/lib/braze", () => ({
  initializeBraze: jest.fn(),
}));

test('initializes braze', () => {
  expect(initializeBraze).toHaveBeenCalled();
});

This isn't ideal if you use many of our functions and probably will not work if you import any of our classes. If this doesn't work or you prefer not to use this workaround, I would consider filing an issue with jest because transformIgnorePatterns should just work.

concrete-mixer commented 2 years ago

Hi @wesleyorbin & @spvismaya thanks for your help! I tried your suggestion but I'm afraid that didn't work either.

I looked at tranformIgnorePatterns and followed the suggestion in https://github.com/facebook/jest/issues/10256 of using a babel.config.js file (our babel config was previously in package.json). Once again, that solution didn't help. I'm guessing there's some combination of module version and jest/babel config that's causing the directive to be ignored.

In the end I tried jest's moduleNameMapper directive instead:

    moduleNameMapper: {
         '@braze/web-sdk': '<rootDir>/test/jestMockBraze.ts',
    }

where <rootDir>/test/jestMockBraze.ts contains:

module.exports = {
    initialize: (): void => {},
}

... and the tests now pass. I don't know if this is a particularly satisfactory solution but I'll note it here as an option for anyone else who might have a similar problem.

Thanks again for your help!

blimmer commented 2 years ago

You can also use jest mocking vs. moduleNameMapper, which might be more conventional: https://github.com/blimmer/braze-jest-mocking/commit/780b746c34e14b88d03c46160d1ecd8504e35949