microsoft-authentication-library-for-js icon indicating copy to clipboard operation
microsoft-authentication-library-for-js copied to clipboard

msal-react: only publishing as ESM breaks jest tests

Open jansepke opened this issue 1 year ago • 22 comments

Core Library

MSAL.js (@azure/msal-browser)

Core Library Version

3.1.0

Wrapper Library

MSAL React (@azure/msal-react)

Wrapper Library Version

2.0.3

Public or Confidential Client?

Public

Description

The latest major release of msal-react is only published as ESM. Currently jest is not able to test code that contains ESM dependencies. Before msal-react V2 we tested our components together with msal-react and only mocked some functions of it. Now with msal-react V2 we have to mock the complete library as jest is unable to load any code from it.

I can see that you are still building other msal libraries as commonjs+ESM (e.g. msal-browser) could you also add a commonjs build to msal-react so it continous to work with jest.

Thank you!

Error Message

No response

Msal Logs

No response

MSAL Configuration

{}

Relevant Code Snippets

-

Reproduction Steps

Expected Behavior

Identity Provider

Azure AD / MSA

Browsers Affected (Select all that apply)

None (Server)

Regression

msal-react 1.5.11

Source

External (Customer)

jansepke avatar Sep 18 '23 09:09 jansepke

it would be nice to have such a change also announced in the release notes. e.g. here https://github.com/AzureAD/microsoft-authentication-library-for-js/releases/tag/msal-react-v2.0.0

Mexx77 avatar Sep 18 '23 10:09 Mexx77

This issue requires attention from the MSAL.js team and has not seen activity in 5 days. @hectormmg please follow up.

This issue requires attention from the MSAL.js team and has not seen activity in 5 days. @hectormmg please follow up.

This issue is affecting my projects as well. Any sense as to if/when it will be possible to resolve it? Thank you!!

jmosney avatar Oct 06 '23 23:10 jmosney

Marking as a feature, feel free to open a pull request if you need this resolved sooner rather than later.

tnorling avatar Oct 09 '23 18:10 tnorling

Does anyone have insights into why this problem is impacting my UAT pipeline while it doesn't seem to have the same effect on my local machine? Also, is there a possible solution or workaround for this issue within a React project?

"Error [ERR_REQUIRE_ESM]: require() of ES Module /app/node_modules/@azure/msal-react/dist/index.js from /app/dist/server/assets/all.page.9a5890d9.js not supported."

karlamarice avatar Oct 11 '23 12:10 karlamarice

hi, same issue on my project! @jansepke can you suggest me how i can mock the entire msal-react library in order to unblock the tests, please?

ciu8 avatar Oct 13 '23 09:10 ciu8

I'm using create-react-app and I was able to resolve the Jest issue by adding the following into my package.json:

  "jest": {
    "transformIgnorePatterns": [
      "node_modules/(?!(@azure/msal-react)).*\\.js$"
    ]
  },

If you're not using create-react-app you should be able to add this into your jest.config.js file.

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

jmosney avatar Oct 13 '23 18:10 jmosney

hi, same issue on my project! @jansepke can you suggest me how i can mock the entire msal-react library in order to unblock the tests, please?

you can mock it like this: jest.mock('@azure/msal-react', () => ({ useMsal: jest.fn(), }));

EDIT: since I see a few downvotes I would like to explain that my suggestion is not a solution to the issue. I shows how @jansepke and I currently work around it by mocking the entire msal library until the issue is fixed. Keeping this workaround is not desirable.

Mexx77 avatar Oct 13 '23 19:10 Mexx77

The fix won't work with Next.js which overwrites jest config.

  1. compile with es-build to a vendor folder and check it in
npx esbuild @azure/msal-react --bundle --platform=node --sourcemap=inline --outfile=vendor/@azure/msal-react.js
  1. update your jest config - map @azure/msal-react to your newly vendored module
const nextJest = require("next/jest");

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: "./",
});

// Add any custom config to be passed to Jest
const customJestConfig = {
  setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
  testEnvironment: "jest-environment-jsdom",
  moduleNameMapper: {
    "@azure/msal-react": "<rootDir>/vendor/@azuremsal-react",
  },
};

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig);
  1. mock
jest.mock('@azure/msal-react', () => ({ useMsal: jest.fn(), }));

jamie-laux-waracle avatar Dec 14 '23 13:12 jamie-laux-waracle

I have been trying different things, still i am getting same issue export { MsalConsumer, MsalContext } from './MsalContext.js'; ^^^^^^

SyntaxError: Unexpected token 'export'

   7 |   RedirectRequest,
   8 | } from '@azure/msal-browser'
>  9 | import {MsalProvider} from '@azure/msal-react'

SyntaxError: Unexpected token 'export'

ranshine avatar Dec 26 '23 12:12 ranshine

Still an issue for me

  • @azure/msal-browser": "^3.7.1"
  • @azure/msal-react": "^2.0.10"

workaround: mocking the entire msal-react/msal-browser libraries.

Extremely annoying.

birpet4 avatar Jan 25 '24 17:01 birpet4

Issue in jest https://github.com/jestjs/jest/issues/14805

julian-alarcon avatar Mar 15 '24 12:03 julian-alarcon

Any updates on this? Looks like this issue blames Jest...and Jest blames this library.

...or does anyone have a good workaround?

jbouder avatar Apr 17 '24 20:04 jbouder

It’s a jest problem at heart. Moving to ESM is the right move long-term, jest just doesn’t support it well yet. As a workaround you can add it to your transformIgnorePatterns (eg transformIgnorePatterns: [“node_modules(?!(@azure/msal-react))“]) so that it is transformed.

patrickfatrick avatar Apr 17 '24 22:04 patrickfatrick

It’s a jest problem at heart. Moving to ESM is the right move long-term, jest just doesn’t support it well yet. As a workaround you can add it to your transformIgnorePatterns (eg transformIgnorePatterns: [“node_modules(?!(@azure/msal-react))“]) so that it is transformed.

Still getting the error with that added. Is there maybe some other jest or tsconfig values that maybe I need to be aware of?

Or maybe you can point me to a repo where this is already setup?

TIA!

jbouder avatar Apr 18 '24 00:04 jbouder

I'm using:

  • jest 29.7.0
  • ts-jest 29.1.1
  • typescript 5.0.2
  • @azure/msal-browser 3.13.0
  • @azure/msal-react 2.0.15

In my case I had to do two things:

  • Change the ts-jest preset (I was using ts-jest's default) to js-with-ts
    • The preset also requires changes allowJs to be true in my Typescript settings.
    • From the docs:

      TypeScript and JavaScript files (.ts, .tsx, .js, .jsx) will be transformed by ts-jest to CommonJS syntax. You'll need to set allowJs to true in your tsconfig.json file.

  • Add the transformIgnorePatterns entry like in other comments.
// jest.config.ts
const config: Config = {
  // ...
  preset: 'ts-jest/presets/js-with-ts',
  // ...
  transformIgnorePatterns: ['<rootDir>/node_modules/(?!@azure/msal-react)'],
  // ...
}
// tsconfig.json
{
  "compilerOptions": {
    // ...
    "allowJs": true /* Required by ts-jest/presets/js-with-ts */
  },
  // ...
}

I used this issue as reference: https://github.com/kulshekhar/ts-jest/issues/970

g-otn avatar Apr 26 '24 22:04 g-otn

What ended up working for me was writing the test to mock MSAL. This resolved my isssue

import React from "react";
import { render } from "@testing-library/react";
import App from "./App";

jest.mock("@azure/msal-react", () => ({
  MsalAuthenticationTemplate: ({ children }: { children: React.ReactNode }) =>
    children,
  useMsal: () => ({
    instance: {},
    inProgress: false,
  }),
}));

describe("App", () => {
  it("renders without crashing", () => {
    render(<App />);
  });
});

alexwasik avatar May 14 '24 20:05 alexwasik

adding global.crypto = require('crypto'); to the setupTests.js file, fixes the failing tests

amritham93 avatar Jun 05 '24 18:06 amritham93

I'm using create-react-app structure for now and solved the issue for was to include this mock bellow in the setupTests.ts or jest.setup.ts. As with create-react-app we need to create an instance of the PublicClientApplication in the index.tsx and after call the initialize function, then we need to mock this instance.

jest.mock('./index', () => {
  const mockMsalInstance = {
    getActiveAccount: jest.fn(),
    acquireTokenSilent: jest.fn(),
    initialize: jest.fn().mockResolvedValue(undefined),
    getAllAccounts: jest.fn().mockReturnValue([]),
    setActiveAccount: jest.fn(),
    addEventCallback: jest.fn(),
  };

  return {
    msalInstance: mockMsalInstance,
  };
});

raulvictorrosa avatar Jun 26 '24 10:06 raulvictorrosa

I'm using create-react-app structure for now and solved the issue for was to include this mock bellow in the setupTests.ts or jest.setup.ts. As with create-react-app we need to create an instance of the PublicClientApplication in the index.tsx and after call the initialize function, then we need to mock this instance.

jest.mock('./index', () => {
  const mockMsalInstance = {
    getActiveAccount: jest.fn(),
    acquireTokenSilent: jest.fn(),
    initialize: jest.fn().mockResolvedValue(undefined),
    getAllAccounts: jest.fn().mockReturnValue([]),
    setActiveAccount: jest.fn(),
    addEventCallback: jest.fn(),
  };

  return {
    msalInstance: mockMsalInstance,
  };
});

this work for me like a charm with my package.json config

"jest": {
    "transform": {
      "^.+\\.(js|jsx|ts|tsx)$": "babel-jest"
    },
    "moduleNameMapper": {
      "^@azure/msal-react": "<rootDir>/node_modules/@azure/msal-react/dist/index.js"
    },
    "setupFilesAfterEnv": [
      "<rootDir>/setupTests.js"
    ],
}

netzulo avatar Jul 04 '24 13:07 netzulo

I'm using create-react-app structure for now and solved the issue for was to include this mock bellow in the setupTests.ts or jest.setup.ts. As with create-react-app we need to create an instance of the PublicClientApplication in the index.tsx and after call the initialize function, then we need to mock this instance.

jest.mock('./index', () => {
  const mockMsalInstance = {
    getActiveAccount: jest.fn(),
    acquireTokenSilent: jest.fn(),
    initialize: jest.fn().mockResolvedValue(undefined),
    getAllAccounts: jest.fn().mockReturnValue([]),
    setActiveAccount: jest.fn(),
    addEventCallback: jest.fn(),
  };

  return {
    msalInstance: mockMsalInstance,
  };
});

this work for me like a charm with my package.json config

"jest": {
    "transform": {
      "^.+\\.(js|jsx|ts|tsx)$": "babel-jest"
    },
    "moduleNameMapper": {
      "^@azure/msal-react": "<rootDir>/node_modules/@azure/msal-react/dist/index.js"
    },
    "setupFilesAfterEnv": [
      "<rootDir>/setupTests.js"
    ],
}

You also probably don't need the line "^@azure/msal-react": "<rootDir>/node_modules/@azure/msal-react/dist/index.js"

This is my configurations, I'm using the fileTransform.js because I'm using CRA and the identity-obj-proxy is a package to be installed to be mapping css and less files.:

// jest.config.ts
import type { JestConfigWithTsJest } from 'ts-jest';
import { defaults as tsjPreset } from 'ts-jest/presets';

const config: JestConfigWithTsJest = {
  roots: ['<rootDir>/src'],
  setupFilesAfterEnv: ['<rootDir>/src/jest.setup.ts'],
  testEnvironment: 'jsdom',
  transform: {
    ...tsjPreset.transform,
    '^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)':
      '<rootDir>/config/jest/fileTransform.js',
  },
  moduleNameMapper: {
    '\\.(css|less)$': 'identity-obj-proxy',
  },
};

export default config;

raulvictorrosa avatar Jul 04 '24 15:07 raulvictorrosa