我正在寻找一种以编程方式动态更改 navigator.userAgent 的方法。在我尝试获得自动化 javascript 单元测试器的失败中,我放弃并尝试开始使用 fireunit。立即,我就碰上了使用实际浏览器进行 JavaScript 测试的一堵墙。
具体来说,我需要更改 navigator.userAgent 来模拟数百个 userAgent 字符串,以确保对给定函数的正确检测和覆盖。 navigator.userAgent 是只读的,所以我似乎卡住了!如何模拟 navigator.userAgent? User Agent Switcher(插件)可以切换FF的useragent,但是我可以在javascript中完成吗?
尝试:
navigator.__defineGetter__('userAgent', function(){
return 'foo' // customized user agent
});
navigator.userAgent; // 'foo'
在FF2和FF3中尝试过。
添加到 Crescent Fresh 的解决方案,重新定义
navigator.userAgent
getter 似乎在 Safari 5.0.5 中不起作用(在 Windows 7 和 Mac OS X 10.6.7 上)。
需要创建一个新的对象,继承自
navigator
对象,并定义一个新的userAgent
getter,以隐藏userAgent
中原来的navigator
getter:
var __originalNavigator = navigator;
navigator = new Object();
navigator.__proto__ = __originalNavigator;
navigator.__defineGetter__('userAgent', function () { return 'Custom'; });
以下解决方案适用于 Chrome、Firefox、Safari、IE9+ 以及 iframe:
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
});
}
}
}
示例:
setUserAgent(window, 'new user agent');
setUserAgent(document.querySelector('iframe').contentWindow, 'new user agent');
对于那些需要在单元测试中更改 userAgent 值的人来说,Tyler Long 的解决方案有效,但如果您想恢复初始 userAgent 或多次更改它,您可能需要将该属性设置为可配置:
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);
否则你可能会遇到
TypeError: Cannot redefine property
错误。
在 Chrome Headless 上适用于我。
使用 Object.defineProperty 应该会添加更多浏览器:
if (navigator.__defineGetter__) {
navigator.__defineGetter__("userAgent", function () {
return "ua";
});
} else if (Object.defineProperty) {
Object.defineProperty(navigator, "userAgent", {
get: function () {
return "ua";
}
});
}
此代码应该可以在 Firefox 1.5+、Chrome 6+、Opera 10.5+ 和 IE9+ 中运行(并经过测试)。不幸的是,任何平台上的 Safari 都不允许更改 userAgent。
编辑:Safari 不允许更改 userAgent,但可以替换整个 navigator 对象,如上面另一解决方案中所指出的。
Crescent Fresh的回答是正确的。但有一个问题:
__defineGetter__
已弃用:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineGetter
已弃用 此功能已从 Web 标准中删除。 尽管某些浏览器可能仍然支持它,但它正在开发中 被丢弃。不要在旧项目或新项目中使用它。页面或网络应用程序 使用它随时可能会损坏。
您应该使用
defineProperty
来代替:
Object.defineProperty(navigator, "userAgent", {
get: function () {
return "foo"; // customized user agent
}
});
navigator.userAgent; // 'foo'
要更新此线程,defineGetter 在 Jasmine 中不再工作,因为它已被弃用。然而我发现这允许我修改 jasmine 中 navigator.userAgent 的 getter:
navigator = {
get userAgent() {
return 'agent';
}
}
console.log(navigator.userAgent); // returns 'agent'
在 jasmine 中完成测试后,请记住重置导航器对象
对于那些尝试在 TypeScript 中做同样事情的人,解决方案如下:
(<any>navigator)['__defineGetter__']('userAgent', function(){
return 'foo';
});
navigator.userAgent; // 'foo'
或者语言也一样:
(<any>navigator)['__defineGetter__']('language', function(){
return 'de-DE';
});
我想我会采用依赖注入方法。而不是:
function myFunction() {
var userAgent = navigator.userAgent;
// do stuff with userAgent
}
也许可以做这样的事情:
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);
}
然后你可以通过以下方式“模拟”
getUserAgent()
:
function getUserAgentReal() { // formerly not 'Real'
// ...
}
function getUserAgent() { // formerly 'Mock'
// ...
}
这个设计仍然不是完全自动化的(你必须手动重命名 getter 来执行测试),它给像操作
navigator.userAgent
这样简单的事情增加了很多复杂性,我不知道你是如何做到的d 实际上识别出 myFunction
中的任何错误,但我只是想我会把它扔在那里,给你一些如何处理这个问题的想法。
也许这里提出的“依赖注入”的想法可以以某种方式与 FireUnit 集成。
以上答案不适用于 PhantomJS + TypeScript。下面的代码对我有用:
var __originalNavigator = navigator;
(window as any).navigator = new Object();
navigator["__proto__"] = __originalNavigator["__proto__"];
navigator["__defineGetter__"]('userAgent', function () { return 'Custom'; });
本主题较晚,但对于 Karma + Jasmin 和 Typescript 并且想要设置 userAgent 属性,这可以做到:
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
});
本文有帮助:https://www.codeproject.com/Tips/1036762/Mocking-userAgent-with-JavaScript
试试这个,它对我有用,没有棉绒问题
Object.defineProperty(global.navigator, 'userAgent', { get: () => 'iPhone' });
navigator.userAgent
是只读字符串属性,因此无法编辑它
这对我有用,不需要额外的文件
// 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
})
通过defineGetter更改Firefox和Opera上的navigator.userAgent
navigator.__defineGetter__('userAgent', function(){
return( "iPhone 5" );
});
alert( navigator.userAgent ); //iPhone 5
通过对象实例更改 IE 和 Opera 上的 navigator.userAgent
var navigator = new Object;
navigator.userAgent = 'iPhone 5';
alert( navigator.userAgent ); //iPhone5
好的事情是,如果你使用 IE 浏览器控件,你可以通过 execScript 双重欺骗 HTTP 请求和 JavaScript navigator.userAgent
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
不,我怀疑你能在 javascript 中做到这一点。但是使用 Firefox 的用户代理切换器,您可以测试您想要的任何用户代理,那么为什么不直接使用它呢?