Skip to content

Manipulation with window location is no longer possible #3492

Closed as not planned

Description

@avlapeto

Basic info:

  • Node.js version: 18.12.0
  • jsdom version: 21.0.0

Minimal reproduction case

We have a page that performs a redirect to an external page based on query string parameters:

...
<script>
const redirectUrl = 'https://github.com/'
window.location.href = redirectUrl;
// window.location.assign(redirectUrl);
</script>
...

and integration test that expects that location.href will be changed (or location.assing will be invoked with some params).

    const windowLocation = {};
    const { window } = new JSDOM(response.text, {
      beforeParse(window) {
        delete window.location;
        window.location = windowLocation;
      },
      runScripts: 'dangerously',
    });
   expect(windowLocation.href).toBe('https://github.com/');

Previously (jsdom 20.0.3) we changed the original location object to a syntetic one and ran an expectation on it. But since window.location became non-configurable, it's no longer possible. 
Any advice about options to spy on location object?

Right now, we are getting:

Error: Not implemented: navigation (except hash changes).

Thanks in advance!

Activity

  1. jenseng commented on May 6, 2023

    @jenseng
    Contributor

    So this won't work for all cases, but if you can control which "window" is used by your code under test, you can inject a more test-friendly version (as in this react-router PR to upgrade jsdom).

    e.g. given some app code we want to test:

    function goSomewhere({ redirectUrl }) {
      window.location.assign(redirectUrl);
    }

    If we change it like so:

    function goSomewhere({ redirectUrl, window: w = window }) {
      w.location.assign(redirectUrl);
    }

    Existing callers callers will still default to the real window. But in our test code we can then do something like this:

    const testWindow = { ...window, location: { ...window.location, assign: jest.fn() } };
    goSomewhere({ redirectUrl: "https://github.com", window: testWindow });
    expect(testWindow.location.assign).toHaveBeenCalledWith("https://github.com");
  2. avlapeto commented on May 9, 2023

    @avlapeto
    Author

    @jenseng
    Thanks much for your suggestion! Based on it I improved my tests to work with the newest version of jest. Unfortunately it's not possible in our case to extract function declaration to a js file and easily test it.

    My solution is:

    ...
    <script>
    // ... Business logic start
    // ... Business logic end
    
    const redirectUrl = 'https://github.com/'
    window.redirect = window.redirect || function (redirectUrl, win) {
        win.location.href = redirectUrl;
      }
    redirect(redirectUrl, window);
    </script>
    ...

    And test it in a way:

    const { window } = new JSDOM(response.text, {
      beforeParse(window) {
        window.redirect = jest.fn();
      },
      runScripts: 'dangerously',
    });
    expect(window.redirect).toHaveBeenCalledWith(redirectParam, window);;

    It's note actually check that redirect is happening, but it test business logic before redirect should happen.

    From my point of view this issue could be closed, since with propper decomposition you could test window object.

  3. uladzimirdev commented on May 10, 2023

    @uladzimirdev

    great solution @jenseng, unfortunately if you do not use window directly, it can be tricky to override window. Solution which worked in my case I found here.

    /**
     * Changes the URL used by the global JSDOM instance in the current window.
     *
     * NOTE: This does not change the origin which would result in a security exception.
     */
    function changeJSDOMURL(url) {
      const newURL = new URL(url);
      const href = `${window.origin}${newURL.pathname}${newURL.search}${newURL.hash}`;
      history.replaceState(history.state, null, href);
    }
  4. donaldpipowitch commented on May 11, 2023

    @donaldpipowitch

    My strategy now: we moved all location.href assignments to a setLocationHref call that is imported from a module, so we can easily mock it in Jest. Not nice, but works.

  5. tkrotoff commented on Dec 7, 2023

    @tkrotoff

    FYI The commit that did this change: 0c8eb3a

  6. fabb commented on Aug 13, 2024

    @fabb

    @ExE-Boss plz fix and make location mockable again

  7. romulof commented on Aug 19, 2024

    @romulof

    Did anyone ever consider creating run time api for mocking?

    Something like:

    window.location[JSDOM.MockSymbol](() => ({
      search: '?/potato=true',
    }));

    This way the properties could still be non-configurable and non-writable, just like in the browser, but with an API for us to modify it.

  8. theKashey commented on Oct 15, 2024

    @theKashey

    removing location: { configurable: false }, is the only solution working for me.

  9. 28 remaining items

  10. domenic commented on Jun 18, 2025

    @domenic
    Member

    @domenic Would you be willing to accept a PR that implements a basic navigation system?

    If you can implement a spec-compliant version of https://html.spec.whatwg.org/#navigation-and-session-history, please do. However, note that it's extremely complicated. I'd suggest first putting together a design doc, instead of a pull request. As a small taste of the issues you'd be raising, you'd need to first separate out Window and WindowProxy, so that navigation changes the Window but leaves the WindowProxy the same. I'm not sure that's possible in Node.js today, but if you have a solution written up, I'd be interested to review it.

  11. Netail commented on Jun 18, 2025

    @Netail

    What about exporting 2 JSDOM versions from the package, one fully browser compliant (a strict version) another fully mockable (a loose version)

    import { JSDOM, MockableJSDOM } from "jsdom";

  12. domenic commented on Jun 19, 2025

    @domenic
    Member

    I support someone else creating a fork of jsdom with their own modifications. I don't have plans to maintain two versions in my own limited time.

  13. added a commit that references this issue on Jun 26, 2025
  14. PenghaiZhang commented on Jun 27, 2025

    @PenghaiZhang

    I guess the JSDOM team should do a survey to know what roles use this library mostly for what purposes. If the majority users use this library for an intention that is NOT what this library is designed for, it would be important to reconsider those users' requests. Or maybe this product is the lowest priority in your organisation and you will not spend time on it, then I can only hope there is proper alternative coming shortly.

  15. PenghaiZhang commented on Jun 27, 2025

    @PenghaiZhang

    jsdom is supposed to be a mocking tool

    This is not correct.

    But this is could be how most users use it in reality.

  16. cpojer commented on Jun 27, 2025

    @cpojer
    Contributor

    While I don't like this decision by the jsdom team either, @PenghaiZhang I think you have a fundamental misunderstanding of open source, and the history here. From my perspective, I agree that these days jsdom is mostly used in a context for emulating a browser environment for testing, but the authors of jsdom did not originally build it for that purpose and since it is open source, they were not in control of us (the Jest team) deciding to use it. Where they want to take the open source project in the future is entirely up to them, and if you don't like it, you can fork it and build the tool you need yourself.

  17. PenghaiZhang commented on Jun 27, 2025

    @PenghaiZhang

    While I don't like this decision by the jsdom team either, @PenghaiZhang I think you have a fundamental misunderstanding of open source, and the history here. From my perspective, I agree that these days jsdom is mostly used in a context for emulating a browser environment for testing, but the authors of jsdom did not originally build it for that purpose and since it is open source, they were not in control of us (the Jest team) deciding to use it. Where they want to take the open source project in the future is entirely up to them, and if you don't like it, you can fork it and build the tool you need yourself.

    Sorry mate but I don't see any misunderstanding. Firstly, like what you said, how this library is used in reality nowadays possibly does not its original intention. Hence, it is reasonable for the authors to say no. But, given the fact that there have been so many people requesting/discussing this kind of feature (not just here, but over in Jest and more) , on one hand the authors should be proud that their product is widely used, on the other hand if they do care their end users' feedback, they should take a certain action rather than simply refusing everything.

    Lastly, I absolutely understand what open source is, hence I said I could only hope someone else would make an alternative. Why do I not do this by myself ? The answer is simple. Because I do not use this library directly. It is used in Jest and if Jest had gone with a different one I would not have been here.

    UPDATE:

    I am now trying to use @jest/environment-jsdom-abstract.

    Update:

    The two purposes are clearly stated:

    Image

    And the ease of testing comes first. 🤷 🤕 🤦

  18. guilhermesimoes commented on Aug 11, 2025

    @guilhermesimoes

    Hey, I work at Sky and this is a very common use-case for us. We have hundreds of tests that do jest.spyOn(window, 'window', 'get') or Object.defineProperty(window, 'location', ...).

    We do this to:

    1. Simulate certain window APIs that only exist on TVs (for example, Samsung TVs have window.tizen.package.getPackageInfo(), LG TVs have window.webOS.deviceInfo(...), etc)
    2. Test certain interactions with the TV app URL

    We'd really like to upgrade to jsdom v26.1.0, to stop including the bloated form-data dependency (nice work on #3850, @domenic!), but this breaking change is making this difficult. I'd really like to avoid patching our test framework or changing production code.

    @domenic please don't take this personally, I don't intend this as an attack 🙏 but just like that individual is very inflexible in his quest to support node 0.4, to the detriment of devs worldwide, I do think you're being a bit inflexible in your quest for "jsdom purity", to the detriment of current users of this package.

    Just look at the amount of issues and PRs that reference this issue, above this comment of mine. This seems like a very common use-case.

  19. locked as resolved and limited conversation to collaborators on Aug 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions