Издевается над useragent в javascript?
-
19-09-2019 - |
Вопрос
Я ищу способ программно изменить navigator.UserAgent "на лету".В моей неудачной попытке получить автоматический модульный тестер javascript я сдался и попытался начать использовать fireunit.Я сразу же наткнулся на одну из проблем использования реального браузера для тестирования javascript.
В частности, мне нужно изменить navigator.UserAgent для имитации нескольких сотен строк UserAgent, чтобы обеспечить надлежащее обнаружение и покрытие для данной функции.navigator.UserAgent доступен только для чтения, так что я, кажется, застрял!Как я могу издеваться над navigator.UserAgent?User Agent Switcher (плагин) может переключать useragent FF, но могу ли я сделать это в 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
добытчик для скрытия оригинала userAgent
добытчик в navigator
:
var __originalNavigator = navigator;
navigator = new Object();
navigator.__proto__ = __originalNavigator;
navigator.__defineGetter__('userAgent', function () { return 'Custom'; });
Следующее решение работает в Chrome, Firefox, Safari, IE9 +, а также с 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
});
}
}
}
Примеры:
setUserAgent(window, 'new user agent');
setUserAgent(document.querySelector('iframe').contentWindow, 'new user agent');
Использование 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+, Хром 6+, Opera 10.5+ и IE9+.К сожалению, Safari ни на одной платформе не позволяет изменять пользовательский агент.
Редактировать:Safari не позволяет изменять пользовательский агент, но можно заменить весь навигатор объект, как указано в другом решении выше.
Ответ Crescent Fresh верен.Но есть одна проблема: __defineGetter__
является устаревшим:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/Определитель
Устаревший Эта функция была удалена из веб-стандартов.Хотя некоторые браузеры все еще могут поддерживать его, он находится в процессе удаления.Не используйте его в старых или новых проектах.Страницы или веб-приложения его использование может прерваться в любое время.
Вы должны использовать defineProperty
вместо этого:
Object.defineProperty(navigator, "userAgent", {
get: function () {
return "foo"; // customized user agent
}
});
navigator.userAgent; // 'foo'
Чтобы обновить этот поток, defineGetter больше не работает в Jasmine, поскольку он устарел.Однако я обнаружил, что это позволяет мне изменить получатель для navigator.UserAgent в jasmine:
navigator = {
get userAgent() {
return 'agent';
}
}
console.log(navigator.userAgent); // returns 'agent'
Просто не забудьте сбросить объект navigator, как только вы закончите тестирование в 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'
// ...
}
Этот дизайн все еще не полностью автоматизирован (вам нужно вручную переименовать средство получения для выполнения вашего тестирования), и это добавляет сложности к чему-то столь простому, как работа с navigator.userAgent
, и я не уверен , как бы вы на самом деле выявили какие - либо ошибки в myFunction
, но я просто решил выложить это там, чтобы дать вам несколько идей, как с этим можно справиться.
Возможно, идея "внедрения зависимостей", представленная здесь, может быть каким-то образом интегрирована с FireUnit.
Для тех, кому здесь нужно изменить значение UserAgent в модульных тестах, решение Тайлера Лонга работает, но если вы хотите восстановить исходный 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.
navigator.userAgent
является строковым свойством, доступным только для чтения, поэтому его невозможно отредактировать
Приведенные выше ответы не работали для 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
Измените navigator.UserAgent в Firefox и Opera через defineGetter
navigator.__defineGetter__('userAgent', function(){
return( "iPhone 5" );
});
alert( navigator.userAgent ); //iPhone 5
Измените navigator.UserAgent в IE и Opera через экземпляр объекта
var navigator = new Object;
navigator.userAgent = 'iPhone 5';
alert( navigator.userAgent ); //iPhone5
Хорошо то, что если вы работаете с веб-браузером IE control, вы можете дважды подделать как HTTP-запрос, так и JavaScript navigator.UserAgent через 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
Нет, я сомневаюсь, что вы можете сделать это с помощью javascript.Но с помощью переключателя пользовательского агента Firefox вы можете протестировать любой пользовательский агент, который вам нужен, так почему бы просто не использовать это?