'expect(jest.fn()).toHaveBeenCalled()

I want to test that specific method/s are called when the user clicks 'sign in with microsoft' button.

Unfortunately, I'm receiving the below error in the image:

[enter image description here][1]

What I want is to mock the one specific method/instance that cause the sign up with microsoft pop up to occur on button click.

Below is my test file:

Login.test.js:

import * as React from 'react';
import {
  render, screen, waitFor
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MuiThemeProvider } from '@material-ui/core';
import { SnackbarProvider } from 'notistack';
import { I18nextProvider } from 'react-i18next';
import Login from '../../pages/Login';
import theme from '../../styles/theme';
import i18n from '../../i18n/index';

const mockLoginWithMicrosoft = jest.fn();

jest.mock('../../config/Firebase', () => ({
  __esModule: true,
  default: {
    getCurrentUserId: jest.fn(),
    loginWithMicrosoft: () => mockLoginWithMicrosoft,
  }
}));

afterEach(() => {
  jest.resetAllMocks();
  jest.restoreAllMocks();
});

test('Firebase signInWithPopUp triggered on login button ', async () => {
  render(
    <MuiThemeProvider theme={theme}>
      <SnackbarProvider>
        <I18nextProvider i18n={i18n}>
          <Login />
        </I18nextProvider>
      </SnackbarProvider>
    </MuiThemeProvider>

  );

  expect(<Login />).toBeTruthy();
  userEvent.click(screen.getByRole('button', { name: /sign in with microsoft/i }));
  await waitFor(() => expect(mockLoginWithMicrosoft).toHaveBeenCalled());
});

And here is my firebase setup:

Firebase/index.js:

class Firebase {
  constructor() {
    app.initializeApp(config);
    app.analytics();
    this.perf = app.performance();
    this.auth = app.auth();
    this.db = app.firestore();

    this.microsoftProvider = new app.auth.OAuthProvider('microsoft.com');
    this.microsoftProvider.setCustomParameters({
      tenant: '4b9d21d4-5ce6-4db6-bce6-cfcd1920afbc',
    });
    this.microsoftProvider.addScope('GroupMember.Read.All');
  }

  async loginWithMicrosoft() {
    try {
      const result = await this.auth.signInWithPopup(this.microsoftProvider);
      const {
        accessToken,
      } = result.credential;
      await this.getUserRoles(accessToken);
      this.refreshRoles(true);
      return {
        message: 'success',
      };
    } catch (error) {
      return {
        message: 'failure',
      };
    }
  }

some code

As you see above, I expect loginWithMicrosoft to be called, when clicking login button as shown below in login file:

Login.js:

import { useSnackbar } from 'notistack';
import { Redirect } from 'react-router-dom';
import Button from '@material-ui/core/Button';
import { useTranslation } from 'react-i18next';
import { DASHBOARD as DASHBOARD_PATH } from '../../navigation/CONSTANTS';
import firebase from '../../config/Firebase';

const Login = (props) => {
  const { history } = props;
  const classes = useStyles(props);
  const { enqueueSnackbar } = useSnackbar();
  const { t } = useTranslation();

  const [loadState, setLoadState] = useState(false);

  if (firebase.getCurrentUserId()) {

    return (<Redirect to={DASHBOARD_PATH} />);
  }

  async function login() {
    try {
      setLoadState(true);
      await firebase.loginWithMicrosoft();
      history.replace(DASHBOARD_PATH);
    } catch (error) {
      enqueueSnackbar(t(translationKeys.snackbar.loginFailed), { variant: 'error' });
      setLoadState(false);
    }
  }

  return (
    <div>
 some code
<Button
            data-testid="submitbtn"
            disabled={loadState}
            type="submit"
            fullWidth
            variant="contained"
            color="primary"
            startIcon={<img src={MicrosoftIcon} alt="" />}
            className={classes.submit}
            onClick={() => { login(); }}
          >
            {t(translationKeys.button.loginWindows)}
          </Button>
     </Grid>
        <Footer />
      </Grid>
    </div>
  );
};

  );
};

Any help would be appreciated.



Solution 1:[1]

Okay so someone helped solve this problem,

As it was explained to me,

  1. LoginWithMicrosoft doesn't need to be an arrow function since jest.fn already returns a function

  2. The other problem is jest hoisting the mock call above the fn declaration

My code now looks like this:


import MockFirebase from '../../config/Firebase';

jest.mock('../../config/Firebase', () => ({
  __esModule: true,
  default: {
    getCurrentUserId: jest.fn(),
    loginWithMicrosoft: jest.fn(),
  }
}));

test('Firebase signInWithPopUp triggered on login button ', async () => {
  render(
    <MuiThemeProvider theme={theme}>
      <SnackbarProvider>
        <I18nextProvider i18n={i18n}>
          <Login />
        </I18nextProvider>
      </SnackbarProvider>
    </MuiThemeProvider>

  );

  userEvent.click(screen.getByRole('button', { name: /sign in with microsoft/i }));
  expect(MockFirebase.loginWithMicrosoft).toHaveBeenCalled();
});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Samora Mabuya