jest-location-mock icon indicating copy to clipboard operation
jest-location-mock copied to clipboard

Conflicts with other tests, Possibly React Router v6

Open muhammedmoussa1 opened this issue 3 years ago β€’ 7 comments

it works but changes the window globally which causes crashes at another test, so can be used to modify the window inside every single test spirality without reflection on other files?

muhammedmoussa1 avatar Dec 20 '21 16:12 muhammedmoussa1

Hi @muhammedmoussa1! πŸ‘‹

Conflict

Conflicts shouldn't be common with this mock so that's unexpected! I'm happy to see if there's something I can do to improve the mock here. Could you create a minimal reproduction of the conflict? Using a tool like Codesandbox (you can add the mock import in setupTests.js if you're using the React templates, for example) or including the related files here helps me out. I am curious to know the cause as the mock is nearly identical to the object in a browser.

Using Jest Location Mock In a Single File or Test

Jest Location Mock is intended to be used across an entire project so that the assumptions of the location object don't change from test to test.

That said, it's worth a try to import it only in each test file you want to have a mocked location and then remove the import from the Jest config setup file.

import "jest-location-mock";

// [tests...]

While not recommended, you can also manually execute the replace hook if all else fails. This may unblock you while we figure out the root of the conflict. Below is an example idea of what this would look like, but I wrote it on my phone from memory, so it may need some adjusting.

// Pull in the location mock setup hook
import {replaceLocation} from "jest-location-mock/lib/hooks";
// Extend the expect matchers for use with the mocked location
// - `expect(window.location).toBeAt("/test");`
import "jest-location-mock/lib/extend-expect";

describe("native location", () => {
  test("should use native location", () => {
    // [expectations...]
  });
});

describe("mocked location", () => {
  // Store the native location stub
  let originalLocation;
  beforeAll(() => {
    originalLocation = Object.getOwnPropertyDescriptor(window, "location");
  });
  // Replace the location property with a clean mock for every each in this describe block
  beforeEach(replaceLocation);
  // Reset the location object back to the native stub after all tests complete
  afterAll(() => {
    Object.defineProperty(window, "location", originalLocation);
  });
  test("should use mocked location", () => {
    // [expectations...]
  });
});

evelynhathaway avatar Dec 21 '21 09:12 evelynhathaway

for what it's worth, i just encountered an issue when using it with react-router-dom v6 - using it seemed to break react router's ability to update window.location.href, so i imported this package in the specific test suites where we were interacting with the location state directly and avoided the issue entirely.

no specific errors were reported, so i didn't deem it worth time to dig deeper 🀷

meatwallace avatar Jun 16 '22 04:06 meatwallace

I think others may be having similar issues with React Router v6 per the comments on this answer on StackOverflow.

I still need to dive into how to reproduce this. Reproduction projects welcome if someone else runs into this.

evelynhathaway avatar Jul 01 '23 17:07 evelynhathaway

I tried to reproduce this issue with just React Router DOM v6, Create React App, and jest-location-mock; and I didn't get any errors when using createBrowserRouter(), <BrowserRouter>, or <MemoryRouter>.

If anyone knows how to reproduce the issues, that'd be perfection! πŸ’–

Working Codesandbox

evelynhathaway avatar Jul 01 '23 21:07 evelynhathaway

I tried out the sandbox and see you have some comments saying:

// React Router doesn't update `window.location`

I don't think this is true from my own testing, at least for createBrowserRouter. I modified your sandbox and window.location.pathname does appear to be updated: https://codesandbox.io/s/jest-location-mock-with-react-router-forked-s2zghz?file=/src/App.test.tsx

maxrchung avatar Oct 17 '23 04:10 maxrchung

@maxrchung Nice catch! You're right. I must've had some incorrect or outdated assumptions about how their browser routers worked in tests. I'll look into this more.

evelynhathaway avatar Oct 17 '23 05:10 evelynhathaway

Funnily enough this would be fixed alongside #158. Jest + JSDOM allows for window.history changes, and window.location correct updates without jest-location-mock. But when jest-location-mock is activated, the internal reference that window.history refers to to update window.location does not match the mocked version.

The effect is that jest-location-mock hides all changes to the location performed by native history because it currently does not even register them. We could mock window.history (this gets close to just writing JSDOM in my opinion), or we can figure out how to add on to the implementation that JSDOM has:

  1. Using events maybe? It could be a small spec deviation to use popstate and the like, but would work for most cases
  2. I would love it we could just move the spies around to a better place
  3. Spy/proxy window.history as well, passing through everything, and then perform a check to our internal copy of the original window.location to check if an update to our side needs to be performed once window.history.* is done. This would also fix #158

evelynhathaway avatar Oct 17 '23 06:10 evelynhathaway