Reputation: 67892
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?
Upvotes: 84
Views: 84036
Reputation: 395
This works for me without extra files
// yourTest.spec.js
let windowSpy: jest.SpyInstance
describe('string', () => {
beforeEach(() => {
windowSpy = jest.spyOn(globalThis, 'window', 'get')
windowSpy.mockImplementation(() => ({
navigator: {userAgent: PUT_YOUR_USER_AGENT_HERE},
}))
})
// optional
afterEach(() => {
windowSpy.mockClear()
})
...your tests here
})
Upvotes: 0
Reputation: 466
Try this it worked for me without lint issues
Object.defineProperty(global.navigator, 'userAgent', { get: () => 'iPhone' });
Upvotes: 1
Reputation: 141
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.
Upvotes: 14
Reputation: 1210
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
Upvotes: 1
Reputation: 363
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'; });
Upvotes: 1
Reputation: 6657
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';
});
Upvotes: 3
Reputation: 76
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
Upvotes: 4
Reputation: 15752
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');
Upvotes: 24
Reputation: 2193
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.
Upvotes: 9
Reputation: 20396
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'
Upvotes: 5
Reputation: 1
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
Upvotes: -1
Reputation: 11337
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'; });
Upvotes: 25
Reputation: 991
navigator.userAgent
is a read-only string property, so its not possible to edit it
Upvotes: 0
Reputation: 25941
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.
Upvotes: 2
Reputation: 117028
Try:
navigator.__defineGetter__('userAgent', function(){
return 'foo' // customized user agent
});
navigator.userAgent; // 'foo'
Tried it in FF2 and FF3.
Upvotes: 123
Reputation: 58999
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?
Upvotes: -3