Jasmine.js Testing - spy on window.navigator.userAgent
Asked Answered
L

3

14

I need to find the way to change userAgent value. I tried to spyOn the window.navigator.userAgent. But that's not helping.

JS:

@Injectable()
export class DetectBrowserService {
  browserIE: boolean;
  constructor() {
    this.browserIE = this.detectExplorer();
  }

  public detectExplorer() {
    const brows = window.navigator.userAgent;
    const msie = brows.indexOf('MSIE ');
    if (msie > 0) {
      // IE 10 or older => return version number
      return true;
    }
  }
}

Spec:

it('should test window.navigator.userAgent', () => {
  const wind = jasmine.createSpy('window.navigator.userAgent');
  wind.and.returnValue('1111');
  detectBrowserService = TestBed.get(DetectBrowserService);
  console.log(window.navigator.userAgent);
});

I was expecting 1111, but got the real info about my browser.

Laxity answered 18/10, 2017 at 8:49 Comment(1)
I recommend to wrap native api calls into tight functions (like in adequatelygood.com/Writing-Testable-JavaScript.html), adn spy those functions instead of native apis. Rely on tight functions makes your code more portable (server side rendering, multi browser problems etc.) and testable. I always had problems spying native window api's with jasmine.Shallot
C
25

I got a simple solution using Jasmine apis itself.

spyOnProperty(window.navigator, 'userAgent').and.returnValue('Mozilla');

modify the spy in each test as per your requirement.

Not sure from which Jasmine version this API is present, but v3.4 supports this API

Once you spy any global property, it is a good practice to clear that spy afterEach test.

Eg.

describe('Test', function() {
  const NAVIGATOR = window.navigator;

  beforeEach(function() {
    spyOnProperty(window.navigator, 'userAgent').and.returnValue('Mozilla');
  })

  afterEach(function() {
    window.navigator = NAVIGATOR;
  });
}
Crocus answered 17/6, 2019 at 5:55 Comment(4)
This should have been marked at the 'Correct Answer'! I have been searching the past day for an answer that worked. Embarrassing is the fact that this is so simple, I should have come up with it earlier. Thanks @Subroto, this did the trick for me.Hoodwink
why is resetting navigator necessary? spyOnProperty should be automatically reset after each test case. Taking a step back, even if it doesn't get reset, spyOnProperty will update window.navigator.userAgent and the change will be carried over with the original navigator. You should be reseting window.navigator.userAgent getter instead.Phyto
Resetting any property is good to have so that tests don't run with some unexpected behavior. If two tests (outer level) require the same navigator property but with different use-cases, resetting the property will make sure that something unexpected doesn't happen.Crocus
Why is the afterEach needed? I thought jasmine will remove spies after each spec and set the normal value back?Seizing
O
19

userAgent is a read-only/constant property on window.navigator. And jasmine.createSpy is generally used to create spies on methods and NOT properties.

Now, I tried directly doing window.navigator.userAgent = '1111'; as window.navigator would simply be accessible in my tests. But I was getting an error saying:

[ts] Cannot assign to 'userAgent' because it is a constant or a read-only property. (property) NavigatorID.userAgent: string

enter image description here

So the only option was to use the good old __defineGetter__. So that's what I did here:

it('should test window.navigator.userAgent', () => {
  window.navigator['__defineGetter__']('userAgent', function(){
    return '1111' // Return whatever you want here
  });
  detectBrowserService = TestBed.get(DetectBrowserService);
  console.log(window.navigator.userAgent);
});

And it works: enter image description here

Hope this helps!

Objectivism answered 18/10, 2017 at 13:4 Comment(2)
For cleanness sake: any way to undo this after your test?Quartern
This should be using spyOnProperty and not overriding the window implementation. See https://mcmap.net/q/791918/-jasmine-js-testing-spy-on-window-navigator-useragent for the correct answer.Impart
H
3

I realize this is old, but to expand on SiddAjmera's answer, here's what I did to test a safari-specific behavior. At the end of the test, it also resets the userAgent so other tests aren't affected (as Pieter De Bie requested in a comment).

it('should redirect if the user is on safari', () => {
  const originalUserAgent = navigator.userAgent;

  fixture = TestBed.createComponent(ComponentToTest);
  component = fixture.componentInstance;

  navigator['__defineGetter__']('userAgent', () => {
    return 'safari';
  });
  fixture.detectChanges();

  component.methodToTest();

  expect(component['window'].location.href).toBe('redirecturl.com');

  // unset the userAgent after test
  navigator['__defineGetter__']('userAgent', () => {
    return originalUserAgent;
  });
});
Harbor answered 31/10, 2018 at 17:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.