AzureAD / microsoft-authentication-library-for-js

Microsoft Authentication Library (MSAL) for JS
http://aka.ms/aadv2
MIT License
3.52k stars 2.62k forks source link

msal-react: only publishing as ESM breaks jest tests #6487

Open jansepke opened 9 months ago

jansepke commented 9 months ago

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)

Mexx77 commented 9 months ago

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

microsoft-github-policy-service[bot] commented 9 months ago

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

microsoft-github-policy-service[bot] commented 9 months ago

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

jmosney commented 9 months ago

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

tnorling commented 9 months ago

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

karlamarice commented 9 months ago

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."

ciu8 commented 8 months ago

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?

jmosney commented 8 months ago

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

Mexx77 commented 8 months ago

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.

jamie-laux-waracle commented 6 months ago

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(), }));
ranshine commented 6 months ago

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'
birpet4 commented 5 months ago

Still an issue for me

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

Extremely annoying.

julian-alarcon commented 3 months ago

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

jbouder commented 2 months ago

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

...or does anyone have a good workaround?

patrickfatrick commented 2 months ago

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.

jbouder commented 2 months ago

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!

g-otn commented 2 months ago

I'm using:

In my case I had to do two things:

// 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

alexwasik commented 1 month ago

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 />);
  });
});
amritham93 commented 1 month ago

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

raulvictorrosa commented 1 week ago

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,
  };
});
netzulo commented 4 days ago

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"
    ],
}
raulvictorrosa commented 3 days ago

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;