Mocking a useragent in javascript?

JavascriptUnit TestingUser Agent

Javascript Problem Overview


I'm looking for a way to programmatically change navigator.userAgent on the fly. In my failed attempt to get an automated javascript unit tester, I gave up and attempted to begin using fireunit. Immediately, I've slammed into one of the walls of using an actual browser for javascript testing.

Specifically, I need to change navigator.userAgent to simulate a few hundred userAgent strings to ensure proper detection and coverage on a given function. navigator.userAgent is readonly, so I seem stuck! How can I mock navigator.userAgent? User Agent Switcher (plugin) can switch FF's useragent, but can I do it within javascript?

Javascript Solutions


Solution 1 - Javascript

Try:

navigator.__defineGetter__('userAgent', function(){
    return 'foo' // customized user agent
});

navigator.userAgent; // 'foo'

Tried it in FF2 and FF3.

Solution 2 - Javascript

Adding on to Crescent Fresh's solution, redefining the navigator.userAgent getter doesn't seem to work in Safari 5.0.5 (on Windows 7 & Mac OS X 10.6.7).

Need to create a new object that inherits from the navigator object and define a new userAgent getter to hide the original userAgent getter in navigator:

var __originalNavigator = navigator;
navigator = new Object();
navigator.__proto__ = __originalNavigator;
navigator.__defineGetter__('userAgent', function () { return 'Custom'; });

Solution 3 - Javascript

The following solution works in Chrome, Firefox, Safari, IE9+ and also with iframes:

function setUserAgent(window, userAgent) {
    if (window.navigator.userAgent != userAgent) {
        var userAgentProp = { get: function () { return userAgent; } };
        try {
            Object.defineProperty(window.navigator, 'userAgent', userAgentProp);
        } catch (e) {
            window.navigator = Object.create(navigator, {
                userAgent: userAgentProp
            });
        }
    }
}

Examples:

setUserAgent(window, 'new user agent');
setUserAgent(document.querySelector('iframe').contentWindow, 'new user agent');

Solution 4 - Javascript

For those here because they need to change the userAgent value in unit tests, Tyler Long's solution works, but if you want to restore the initial userAgent or change it more than once, you will probably need to set the property as configurable:

function setUserAgent(userAgent) {
    Object.defineProperty(navigator, "userAgent", { 
        get: function () { 
            return userAgent; // customized user agent
        },
        configurable: true
    });
}

// Now in your setup phase:
// Keep the initial value
var initialUserAgent = navigator.userAgent;
setUserAgent('foo');

// In your tearDown:
// Restore the initial value
setUserAgent(initialUserAgent);

Otherwise you might run into a TypeError: Cannot redefine property error. Works for me on Chrome Headless.

Solution 5 - Javascript

Using Object.defineProperty should add several more browsers to the mix:

if (navigator.__defineGetter__) {
	navigator.__defineGetter__("userAgent", function () { 
		return "ua"; 
	});
} else if (Object.defineProperty) { 
	Object.defineProperty(navigator, "userAgent", { 
		get: function () { 
			return "ua";
		}
	});
}

This code should work (and was tested) in Firefox 1.5+, Chrome 6+, Opera 10.5+ and IE9+. Unfortunately Safari on any platform doesn't allow changing the userAgent.

Edit: Safari doesn't allow changing the userAgent, but one can replace the whole navigator object, as pointed out in another solution above.

Solution 6 - Javascript

Crescent Fresh's answer is correct. But there is an issue: __defineGetter__ is deprecated:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/__defineGetter__

> Deprecated > This feature has been removed from the Web standards. > Though some browsers may still support it, it is in the process of > being dropped. Do not use it in old or new projects. Pages or Web apps > using it may break at any time.

You should use defineProperty instead:

Object.defineProperty(navigator, "userAgent", { 
    get: function () { 
        return "foo"; // customized user agent
    }
});

navigator.userAgent; // 'foo'

Solution 7 - Javascript

To update this thread, defineGetter does not work anymore in Jasmine as it was deprecated. However I found this allows me to modify the getter for navigator.userAgent in jasmine:

navigator = {
  get userAgent() {
    return 'agent';
  }
}

console.log(navigator.userAgent); // returns 'agent'

Just remember resetting the navigator object once you are done testing in jasmine

Solution 8 - Javascript

For those trying to do the same thing in TypeScript here's the solution:

(<any>navigator)['__defineGetter__']('userAgent', function(){
    return 'foo';
});

navigator.userAgent; // 'foo'

Or same thing for language:

(<any>navigator)['__defineGetter__']('language', function(){
    return 'de-DE';
});

Solution 9 - Javascript

I guess I'd take a dependency injection approach. Instead of:

function myFunction() {
    var userAgent = navigator.userAgent;
    // do stuff with userAgent
}

Maybe do something like:

function myFunction(userAgent) {
    // do stuff with userAgent
}

function getUserAgent() {
    window.userAgentReal = +window.userAgentReal || 0;
    return [ navigator.userAgent ][window.userAgentReal++];
}

function getUserAgentMock() {
    window.nextUserAgentMock = +window.nextUserAgentMock || 0;
    return [
        'test user agent1',
        'test user agent2',
        'test user agent3'
    ][window.nextUserAgentMock++];
}

var userAgent;
while (userAgent = getUserAgent()) {
    myFunction(userAgent);
}

Then you can "mock out" getUserAgent() by doing:

function getUserAgentReal() { // formerly not 'Real'
    // ...
}

function getUserAgent() { // formerly 'Mock'
    // ...
}

This design still isn't completely automated (you have to manually rename the getter to perform your testing), and it adds a bunch of complexity to something as simple as operating on navigator.userAgent, and I'm not sure how you'd actually identify any bugs in myFunction, but I just figured I'd throw it out there to give you some ideas how this might be dealt with.

Maybe the idea of "dependency injection" presented here can somehow be integrated with FireUnit.

Solution 10 - Javascript

Above answers were not working for PhantomJS + TypeScript. Below code worked for me:

var __originalNavigator = navigator;
(window as any).navigator = new Object();
navigator["__proto__"] = __originalNavigator["__proto__"];
navigator["__defineGetter__"]('userAgent', function () { return 'Custom'; });

Solution 11 - Javascript

Late to this topic but for Karma + Jasmin and Typescript and want to set the userAgent property this will do it:

describe('should validate YYYY-MM-dd format only on IE browser', () => {
    // this validator has a specific condition to work only in IE11 and down
    (window as any).navigator.__defineGetter__('userAgent', function () {
      return 'MSIE';
    });

...
// rest of the test

});

This article helped: https://www.codeproject.com/Tips/1036762/Mocking-userAgent-with-JavaScript

Solution 12 - Javascript

Try this it worked for me without lint issues

Object.defineProperty(global.navigator, 'userAgent', { get: () => 'iPhone' });

Solution 13 - Javascript

navigator.userAgent is a read-only string property, so its not possible to edit it

Solution 14 - Javascript

Change navigator.userAgent on Firefox and Opera via defineGetter

navigator.__defineGetter__('userAgent', function(){
	return( "iPhone 5" );
});

alert( navigator.userAgent ); //iPhone 5

Change navigator.userAgent on IE and Opera via object instance

var navigator = new Object; 
navigator.userAgent = 'iPhone 5';

alert( navigator.userAgent ); //iPhone5

Good thing is, if you work on IE webbrowser control, you can double spoof both HTTP request and JavaScript navigator.userAgent via execScript

WebBrowser1.Navigate "http://example.com", , , , "User-Agent: iPhone 5" & vbCrLf

WebBrowser1.Document.parentWindow.execScript ("var navigator=new Object;navigator.userAgent='iPhone 5';")
WebBrowser1.Document.parentWindow.execScript ("alert(navigator.userAgent);") 'iPhone 5

Solution 15 - Javascript

No, i doubt you can do it within javascript. But with Firefox's User Agent Switcher you can test whatever useragent you want, so why not just use that?

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
QuestionStefan KendallView Question on Stackoverflow
Solution 1 - JavascriptCrescent FreshView Answer on Stackoverflow
Solution 2 - JavascriptmaxyfcView Answer on Stackoverflow
Solution 3 - JavascriptJoelView Answer on Stackoverflow
Solution 4 - JavascriptSo6View Answer on Stackoverflow
Solution 5 - JavascriptBundyoView Answer on Stackoverflow
Solution 6 - JavascriptTyler LiuView Answer on Stackoverflow
Solution 7 - JavascriptFernando De VegaView Answer on Stackoverflow
Solution 8 - JavascriptMiroslav JonasView Answer on Stackoverflow
Solution 9 - JavascriptGrant WagnerView Answer on Stackoverflow
Solution 10 - JavascriptblackspacerView Answer on Stackoverflow
Solution 11 - JavascriptAvram VirgilView Answer on Stackoverflow
Solution 12 - JavascriptvnxyzView Answer on Stackoverflow
Solution 13 - JavascriptLil'MonkeyView Answer on Stackoverflow
Solution 14 - JavascriptPriince paul GatesView Answer on Stackoverflow
Solution 15 - JavascriptMariusView Answer on Stackoverflow