the venom
the venom

Reputation: 771

How to mock window.navigator.language using jest

I am trying to mock the window.navigator.language attribute in the browser in my jest unit tests so I can test that the content on my page is using the correct language

I have found people online using this:

Object.defineProperty(window.navigator, 'language', {value: 'es', configurable: true});

I have set it right at the top of my test file and it is working there

however, when I redefine in an individual test (and people set to make sure configurable was set to true) it wont redefine it and is just using the old value, does anyone know a way to definitely change it?

beforeEach(() => {
    jest.clearAllMocks()
    Object.defineProperty(global.navigator, 'language', {value: 'es', configurable: true});
    wrapper = shallow(<Component {...props} />)
})

  it('should do thing 1', () => {
      Object.defineProperty(window.navigator, 'language', {value: 'de', configurable: true});
      expect(wrapper.state('currentLanguage')).toEqual('de')
    })

it('should do thing 2', () => {
  Object.defineProperty(window.navigator, 'language', {value: 'pt', configurable: true});
  expect(wrapper.state('currentLanguage')).toEqual('pt')
})

for these tests it is not changing the language to the new language I have set, always using the one at the top

Upvotes: 47

Views: 47495

Answers (3)

Kyle Treman
Kyle Treman

Reputation: 265

Alternatively

jest config file

setupFiles: ['./test/mock-data/globals.js']

globals.js

const navigator = { language: 'Chalcatongo Mixtec', ...anyOtherPropertiesYouNeed };

Object.defineProperty(window, 'navigator', {
   value: navigator,
   writable: true
});

then you can mutate freely in your individual test setup

Upvotes: 3

Eduardo Acosta
Eduardo Acosta

Reputation: 39

Adding a little bit to Estus Flask's answer, you can also spy on your setup file:

In jest config file activate the setupFiles feature:

setupFiles: ['./test/mock-data/globals.js']

Then inside globals.js spy on the userAgent or any other property:

global.userAgent = jest.spyOn(navigator, 'userAgent', 'get');

Finally in your test mock the return value:

describe('abc', () => {
  global.userAgent.mockReturnValue(
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4)\
           AppleWebKit/600.1.2 (KHTML, like Gecko)\
           Version/13.0.0 Safari/600.1.2'
  );
  test('123', async () => {
    const result = await fnThatCallsOrUseTheUserAgent();
    expect(result).toEqual('Something');
  });
});

Upvotes: 3

Estus Flask
Estus Flask

Reputation: 222760

window.navigator and its properties are read-only, this is the reason why Object.defineProperty is needed to set window.navigator.language. It's supposed to work for changing property value multiple times.

The problem is that the component is already instantiated in beforeEach, window.navigator.language changes don't affect it.

Using Object.defineProperty for mocking properties manually will require to store original descriptor and restore it manually as well. This can be done with jest.spyOn. jest.clearAllMocks() wouldn't help for manual spies/mocks, it may be unneeded for Jest spies.

It likely should be:

let languageGetter;

beforeEach(() => {
  languageGetter = jest.spyOn(window.navigator, 'language', 'get')
})

it('should do thing 1', () => {
  languageGetter.mockReturnValue('de')
  wrapper = shallow(<Component {...props} />)
  expect(wrapper.state('currentLanguage')).toEqual('de')
})
...

Upvotes: 74

Related Questions