在 javascript 中模仿用户代理?

我正在寻找一种方法,以编程方式更改导航器。用户代理的飞行。在尝试获得一个自动化的 javascript 单元测试器失败后,我放弃了,并试图开始使用 fireunit。很快,我就遇到了使用实际浏览器进行 javascript 测试的难题。

具体来说,我需要更改 Navator.userAgent 来模拟几百个 userAgent 字符串,以确保对给定函数进行适当的检测和覆盖。UserAgent 是只读的,所以我好像卡住了!我怎样才能模仿领航员 userAgent?用户代理切换器(插件)可以切换 FF 的用户代理,但是我可以在 javascript 中这样做吗?

76725 次浏览

不,我怀疑你能在 javascript 中完成。但是有了 Firefox 的用户代理转换器,你可以测试任何你想要的用户代理,所以为什么不直接使用它呢?

navigator.userAgent是只读字符串属性,因此不可能编辑它

试试:

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


navigator.userAgent; // 'foo'

在 FF2和 FF3中试过了。

我想我会采取依赖注入的方式,而不是:

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这样简单的东西上,我不确定你如何实际识别 myFunction中的任何错误,但我只是想把它抛出去,给你一些想法,这可能是如何处理的。

也许这里提到的“依赖注入”的概念可以以某种方式与 FireUnit 集成。

添加到 新月食品公司的解决方案,重新定义 navigator.userAgent getter 似乎不适用于 Safari 5.0.5(在 Windows 7和 Mac OS X 10.6.7上)。

需要创建一个继承自 navigator对象的新对象,并定义一个新的 userAgent getter 以在 navigator中隐藏原来的 userAgent getter:

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

使用 Object.defeProperty 应该添加更多的浏览器:

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,但是可以替换整个 领航员对象,正如上面另一个解决方案中指出的那样。

通过 defeGetter 更改 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 导航器.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

以下解决方案适用于 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');

新月食品的回答是正确的。但有一个问题: __defineGetter__不受欢迎:

Https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/object/abc0

反对 此特性已从 Web 标准中删除。 虽然一些浏览器可能仍然支持它,但是它正处于 不要在旧的或新的项目中使用它。页面或 Web 应用程序 使用它可能随时打破。

你应该改用 defineProperty:

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


navigator.userAgent; // 'foo'

为了更新这个线程,defeGetter 在 Jasmine 中不再工作了,因为它已经被废弃了。然而,我发现这允许我在 jasmine 中修改 Navator.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';
});

上面的答案不适用于 PhantomJS + TypeScript,下面的代码适用于我:

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

这个主题的后期,但是对于 Karma + Jasmin 和 Type 脚本,并且想要设置 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

对于那些需要在单元测试中更改 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(global.navigator, 'userAgent', { get: () => 'iPhone' });