React Simulate back button behavior in react testing library and jest

Viewed 892

Hey guys I've got a component that overrides the back button behavior by creating a popstate event, but I haven't found a way to test it's functionality. It should be as easy as creating a spy and checking if the window.confirm is being called, but it's not calling the function when I do window.history.back(), and I don't understand why.

Also if I pull the function outside of the component, it's being rendered as an anonymous function and the remove event listener isn't being called, which makes the popup event display on every page, and there's no way to remove it because it's an anonymous function. I'm able to test the function though and the logic is working just fine (;

How do we fix this? What should I do?

This function stops the initial back button navigation behavior, and creates a popup event to ask if the person wants to navigate to the home page, and if they click okay then they navigate, otherwise they stay on the page. We have this wrapped around a page after they submit a form to prevent them from submitting another form when they click the back button Here's my component:

import React, {useEffect, ReactElement} from 'react';
import { navigate } from '@reach/router';

export interface BackButtonBehaviorProps {
  children: ReactElement;
}

export const BackButtonBehavior = ({children}: BackButtonBehaviorProps) => {
  const onBackButtonEvent = (e: { preventDefault: () => void; }) => {
    e.preventDefault();
    const backButtonIsConfirmed = window.confirm("Your claim has been submitted, would you like to exit before getting additional claim information?");
    if (backButtonIsConfirmed) {
      navigate('/');
    } else {
      window.history.pushState(window.history.state, "success page", window.location.pathname); // When you click back (this refreshes the current instance)
    }
  };

  useEffect(() => {
    window.history.pushState(window.history.state, "", window.location.pathname);
    window.addEventListener('popstate', onBackButtonEvent, true);

    // As you're unmounting the component
    return () => {
      window.removeEventListener('popstate', onBackButtonEvent);  
    };
  }, []);

  return (children);
}; 

If I pull the function outside of the component and export it, it's rendered in the popstate event listeners as anonymous, and will not be deleted when I'm unmounting the component, and there's no way to fix that. Here are the tests that work when I exported it though:

import {cleanup, render} from '@testing-library/react';
import * as router from '@reach/router';
import { mockPersonalReturnObj } from '../Summary/testData';

describe('<App />', () => {
  let navigateSpy: any;
  let backButtonBehavior: any;

  beforeEach(() => {
    const mod = require('./BackButtonBehavior');
    backButtonBehavior = mod.onBackButtonEvent;
    navigateSpy = jest.spyOn(router, 'navigate');
  });

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

  it('should display a popup when the user clicks the back button', async () => {
    jest.spyOn(global, 'confirm' as any).mockReturnValueOnce(true);

    backButtonBehavior({preventDefault: () => {}});

    expect(global.confirm).toHaveBeenCalled();
  });

  it('should navigate to home page when you click ok on the window.confirm popup', async () => {
    jest.spyOn(global, 'confirm' as any).mockReturnValueOnce(true);

    backButtonBehavior({preventDefault: () => {}});

    expect(global.confirm).toHaveBeenCalled();
    expect(await navigateSpy).toHaveBeenCalledWith('/');
  });
});

I haven't found a way to call the global confirm when I do a test and I literally just want to test if the window.confirm event is being called (there's a bunch of ways to check window.confirm through spies, none of them worked). I need a way to simulate the back button click event for this to be called, but I haven't found a way to do this. Here's a test example:

it('should navigate to home page when you click ok on the window.confirm popup', async () => {
  jest.spyOn(global, 'confirm' as any).mockReturnValueOnce(true);

  render(
    <BackButtonBehavior>
      <CurrentPage />
    </BackButtonBehavior>
  );

  window.history.back();

  expect(global.confirm).toHaveBeenCalled();
  expect(await navigateSpy).toHaveBeenCalledWith('/', {});
});

How do we simulate clicking the back button in the browser for react tests?

0 Answers
Related