How to mock window.location.href with Jest + Vuejs?

vue.jsUnit TestingJestjsJasmineVue Test-Utils

vue.js Problem Overview


Currently, I am implementing unit test for my project and there is a file that contained window.location.href.

I want to mock this to test and here is my sample code:

it("method A should work correctly", () => {
      const url = "http://dummy.com";
      Object.defineProperty(window.location, "href", {
        value: url,
        writable: true
      });
      const data = {
        id: "123",
        name: null
      };
      window.location.href = url;
      wrapper.vm.methodA(data);
      expect(window.location.href).toEqual(url);
    });

But I get this error:

TypeError: Cannot redefine property: href
        at Function.defineProperty (<anonymous>)

I had tried some solutions but not resolve it. I need some hints to help me get out of this trouble. Plz help.

vue.js Solutions


Solution 1 - vue.js

You can try:

global.window = Object.create(window);
const url = "http://dummy.com";
Object.defineProperty(window, 'location', {
  value: {
    href: url
  }
});
expect(window.location.href).toEqual(url);  

Have a look at the Jest Issue for that problem:
Jest Issue

Solution 2 - vue.js

2020 Update


Basic

The URL object has a lot of the same functionality as the Location object. In other words, it includes properties such as pathname, search, hostname, etc. So for most cases, you can do the following:

delete window.location
window.location = new URL('https://www.example.com')

Advanced

You can also mock Location methods that you might need, which don't exist on the URL interface:

const location = new URL('https://www.example.com')
location.assign = jest.fn()
location.replace = jest.fn()
location.reload = jest.fn()

delete window.location
window.location = location

Solution 3 - vue.js

I have resolved this issue by adding writable: true and move it to beforeEach

Here is my sample code:

global.window = Object.create(window);
const url = "http://dummy.com";
Object.defineProperty(window, "location", {
    value: {
       href: url
    },
    writable: true
});

Solution 4 - vue.js

Solution for 2019 from GitHub: > > delete global.window.location; > global.window = Object.create(window); > global.window.location = { > port: '123', > protocol: 'http:', > hostname: 'localhost', > };

Solution 5 - vue.js

The best is probably to create a new URL instance, so that it parses your string like location.href does, and so it updates all the properties of location like .hash, .search, .protocol etc.

it("method A should work correctly", () => {
  const url = "http://dummy.com/";
  Object.defineProperty(window, "location", {
    value: new URL(url)
  } );
  
  window.location.href = url;
  expect(window.location.href).toEqual(url);
  
  window.location.href += "#bar"
  expect(window.location.hash).toEqual("#bar");
});

https://repl.it/repls/VoluminousHauntingFunctions

Solution 6 - vue.js

Many of the examples provided doesn't mock the properties of the original Location object.

What I do is just replace Location object (window.location) by URL, because URL contains the same properties as Location object like "href", "search", "hash", "host".

Setters and Getters also work exactly like the Location object.

Example:

const realLocation = window.location;

describe('My test', () => {

    afterEach(() => {
        window.location = realLocation;
    });

    test('My test func', () => {

        // @ts-ignore
        delete window.location;

        // @ts-ignore
        window.location = new URL('http://google.com');

        console.log(window.location.href);

        // ...
    });
});

Solution 7 - vue.js

Working example with @testing-library/react in 2020 for window.location.assign:

  afterEach(cleanup)
  beforeEach(() => {
    Object.defineProperty(window, 'location', {
      writable: true,
      value: { assign: jest.fn() }
    })
  })

Solution 8 - vue.js

Extending @jabacchetta's solution to avoid this setting bleeding into other tests:

describe("Example", () => {
  let location;

  beforeEach(() => {
    const url = "https://example.com";
    location = window.location;
    const mockLocation = new URL(url);
    mockLocation.replace = jest.fn();
    delete window.location;
    window.location = mockLocation;
  });

  afterEach(() => {
    window.location = location;
  });
});

Solution 9 - vue.js

You can try a helper:

const setURL = url => global.jsdom.reconfigure({url});

describe('Test current location', () => {
  test('with GET parameter', () => {
    setURL('https://test.com?foo=bar');
    // ...your test here
  });
});

Solution 10 - vue.js

JSDOM Version

Another method, using JSDOM, which will provide window.location.href and all of the other properties of window.location, (e.g. window.location.search to get query string parameters).

import { JSDOM } from 'jsdom';

...

const { window } = new JSDOM('', {
	url: 'https://localhost/?testParam=true'
});
delete global.window;
global.window = Object.create(window);

Solution 11 - vue.js

you can try jest-location-mock.

npm install --save-dev jest-location-mock

update jest configs at jest.config.js file or jest prop inside package.json:

setupFilesAfterEnv: [ "./config/jest-setup.js" ]

create jest-setup.js

import "jest-location-mock";

usage:

it("should call assign with a relative url", () => {
    window.location.assign("/relative-url");
    expect(window.location).not.toBeAt("/");
    expect(window.location).toBeAt("/relative-url");
});

Solution 12 - vue.js

Can rewrite window.location by delete this global in every test.

delete global.window.location;
const href = 'http://localhost:3000';
global.window.location = { href };

Solution 13 - vue.js

Based on examples above and in other threads, here is a concrete example using jest that might help someone:

describe('Location tests', () => {
    const originalLocation = window.location;

    const mockWindowLocation = (newLocation) => {
        delete window.location;
        window.location = newLocation;
    };

    const setLocation = (path) =>
        mockWindowLocation(
            new URL(`https://example.com${path}`)
        );

    afterEach(() => {
        // Restore window.location to not destroy other tests
        mockWindowLocation(originalLocation);
    });

    it('should mock window.location successfully', () => {
        setLocation('/private-path');

        expect(window.location.href).toEqual(
            `https://example.com/private-path`
        );
    });
});

Solution 14 - vue.js

This is valid for Jest + TypeScript + Next.js (in case you use useRoute().push

const oldWindowLocation = window.location;

beforeAll(() => {
  delete window.location;
  window.location = { ...oldWindowLocation, assign: jest.fn() };
});

afterAll(() => {
  window.location = oldWindowLocation;
});

Solution 15 - vue.js

Probably irrelevant. But for those seeking a solution for window.open('url', attribute) I applied this, with help of some comments above:

window = Object.create(window);
const url = 'https://www.9gag.com';
Object.defineProperty(window, 'open', { value: url });

expect(window.open).toEqual(url);

Solution 16 - vue.js

How to reassign window.location in your code base; the simplest working setup we found for our Jest tests:

const realLocation = window.location;

beforeEach(() => {
  delete window.location;
});

afterEach(() => {
  window.location = realLocation;
});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTran Son HoangView Question on Stackoverflow
Solution 1 - vue.jsmavaView Answer on Stackoverflow
Solution 2 - vue.jsjabacchettaView Answer on Stackoverflow
Solution 3 - vue.jsTran Son HoangView Answer on Stackoverflow
Solution 4 - vue.jsserv-incView Answer on Stackoverflow
Solution 5 - vue.jsKaiidoView Answer on Stackoverflow
Solution 6 - vue.jsJuan LagoView Answer on Stackoverflow
Solution 7 - vue.jsDevin ClarkView Answer on Stackoverflow
Solution 8 - vue.jsthisismydesignView Answer on Stackoverflow
Solution 9 - vue.jslequanView Answer on Stackoverflow
Solution 10 - vue.jsEd LucasView Answer on Stackoverflow
Solution 11 - vue.jsMuhammed MoussaView Answer on Stackoverflow
Solution 12 - vue.jsskanecodeView Answer on Stackoverflow
Solution 13 - vue.jsBazzeView Answer on Stackoverflow
Solution 14 - vue.jsEstevão LucasView Answer on Stackoverflow
Solution 15 - vue.jsImran KhanView Answer on Stackoverflow
Solution 16 - vue.jsTyler Caine RhodesView Answer on Stackoverflow