Издеваются над пользовательским агентом в javascript?

Ищу способ программно изменить navigator.userAgent на лету. В моей неудачной попытке получить автоматический тестер модулей javascript я сдался и попытался начать использовать fireunit. Я сразу же врезался в одну из стен использования настоящего браузера для тестирования javascript.

В частности, мне нужно изменить navigator.userAgent, чтобы имитировать несколько сотен строк userAgent, чтобы обеспечить правильное обнаружение и охват данной функции. navigator.userAgent доступен только для чтения, поэтому я, кажется, застрял! Как я могу издеваться над navigator.userAgent? User Agent Switcher (плагин) может переключать пользовательский агент FF, но могу ли я сделать это в javascript?


person Stefan Kendall    schedule 20.08.2009    source источник
comment
Вы смотрели env.js? (groups.google.com/group/envjs)   -  person Aaron Digulla    schedule 20.08.2009


Ответы (15)


Пытаться:

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

navigator.userAgent; // 'foo'

Пробовал в FF2 и FF3.

person Crescent Fresh    schedule 20.08.2009
comment
Можно ли использовать это решение для установки пользовательского агента iframe? - person MANnDAaR; 22.08.2012
comment
Работает для меня. (IE в Windows) - person Tyler Long; 27.01.2015
comment
__defineGetter__ устарел, используйте вместо него defineProperty . Проверьте мой ответ ниже. - person Tyler Long; 30.01.2015
comment
при использовании angular .. stackoverflow.com/questions/24702003/ - person danday74; 04.03.2016
comment
Недавно я создал суть, чтобы сделать доступными только для чтения свойства (вы можете найти пример перезаписи navigator.userAgent). Это уродливо и не рекомендуется, но я использовал его для модульных тестов, чтобы менять пользовательские агенты на лету, так что будьте осторожны. Суть: gist.github.com/moehlone/bed7dd6cb38fc55bd640 - person Philipp Möhler; 17.03.2016
comment
Я попытался поместить это прямо в инструменты разработчика браузера, и он просто вернул агент в виде строки. Сам браузер по-прежнему отображает как исходный браузер. Нужно ли мне обновлять, чтобы он работал? - person mogoli; 14.09.2016
comment
Работает в консоли Chrome. У меня не сработало в PhantomJS. - person theUtherSide; 21.06.2017
comment
Устаревший ответ! - person Olivier de Broqueville; 19.03.2019

Добавление к решению решения Crescent Fresh, переопределение геттера navigator.userAgent не похоже, работает в 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'; });
person maxyfc    schedule 10.05.2011
comment
Я не могу переопределить объект window.navigator, поэтому ваше решение мне не подходит. Тот, что принадлежит «Crescent Fresh». - person cburgmer; 10.12.2012
comment
Это работает и с phantomjs ... нужен новый объект. Я думаю, это дело в вебките. - person Pykler; 14.05.2013
comment
Не работает в Safari версии 12.1.2 (14607.3.9). Ошибка Javascript в строке navigator.__proto__ = __originalNavigator;, там написано: cyclic __proto__ value или что-то подобное. Удаление этой строки заставляет пользовательский агент работать локально, то есть navigator.userAgent вернет то, что вы установили, но для вызовов XMLHttpRequest он будет просто использовать UA по умолчанию. - person Jonny; 28.08.2019

Следующее решение работает в 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');
person Joel Richard    schedule 12.11.2014
comment
Я тестировал Safari 8, и, похоже, это не сработало. Я использую console.log (navigator.userAgent), и консоль регистрирует строку пользовательского агента по умолчанию. - person Aero Wang; 27.02.2015
comment
Однако я думаю, что у меня есть console.log до того, как скрипт изменит пользовательский агент. - person Aero Wang; 27.02.2015
comment
@AeroWindwalker Я только что снова протестировал его в Safari 8, и он работает. - person Joel Richard; 27.02.2015
comment
Да я понял, что сценарий работает. Просто iframe отправил на сервер пользовательский агент по умолчанию до того, как скрипт применит к нему новый пользовательский агент. - person Aero Wang; 28.02.2015
comment
Я также получаю сообщение об ошибке: null не является объектом (оценка 'document.querySelector (' iframe '). ContentWindow') - person Aero Wang; 28.02.2015
comment
Я понял, что использую серверную часть для обнаружения пользовательского агента, в то время как этот сценарий меняет только пользовательский агент во внешнем интерфейсе. Вот почему бэкэнд будет игнорировать мобильный пользовательский агент javascript и отправлять обратно содержимое рабочего стола. - person Aero Wang; 28.02.2015
comment
Проверено на Chrome 41.0.2272.64, тоже не работает. Это работает только в том случае, если у меня есть функция javascript переднего плана, которая обнаруживает строку пользовательского агента. Моя серверная часть всегда получает строку пользовательского агента по умолчанию. - person Aero Wang; 28.02.2015
comment
@AeroWindwalker Не существует решения JavaScript для изменения пользовательского агента, который отправляется в заголовках HTTP. Вы даже не можете переопределить его с помощью setRequestHeader для запросов AJAX. Однако некоторые браузеры позволяют переопределить его настройкой (что, вероятно, не то, что вам нужно). - person Joel Richard; 01.03.2015
comment
@AeroWang Как вы это решили? the iframe sent the default user agent to the server before the script applies a new user agent to it. - person LIGHT; 28.04.2015
comment
@Prakash Вы не можете изменить пользовательский агент, который отправляется на сервер (если вы не измените настройки своего браузера). Это изменение носит временный характер и применяется только к среде JavaScript. - person Joel Richard; 28.04.2015
comment
@prakash Тут нет решения. В итоге я написал свой собственный скрипт node.js для тестирования прокси. - person Aero Wang; 30.04.2015
comment
Это работало со стеком Jasmine / Karma // Phantom. Спасибо! - person theUtherSide; 21.06.2017

Использование 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, но можно заменить весь объект навигатора, как указано в другом решении выше.

person Bundyo    schedule 24.03.2014
comment
Привет, Банди, есть ли способ сделать то же самое в IE8? - person Pratik Pattanayak; 25.02.2015
comment
К сожалению, я ничего не знаю. - person Bundyo; 26.02.2015

Для тех, кто здесь, потому что им нужно изменить значение 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.

person So6    schedule 08.01.2019
comment
Я пришел к такому же выводу после проб и ошибок, хотел бы я сначала прочитать этот ответ. - person MDahlke; 21.03.2019
comment
У меня сейчас это не работает в Windows Google Chrome версии 78.0.3904.108 (официальная сборка) (64-разрядная версия) - person Ryan; 16.12.2019
comment
Спасибо, сработало как шарм const setUserAgent = (userAgent: 'Chrome' | 'Android') => { Object.defineProperty(navigator, 'userAgent', { get: () => userAgent, configurable: true, }); }; - person Aamir Afridi; 09.03.2020

Ответ Crescent Fresh правильный. Но есть проблема: __defineGetter__ устарел:

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

Устарело. Эта функция была удалена из веб-стандартов. Хотя некоторые браузеры могут по-прежнему поддерживать его, в настоящее время он удаляется. Не используйте его в старых или новых проектах. Страницы или веб-приложения, использующие его, могут сломаться в любой момент.

Вместо этого вы должны использовать defineProperty:

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

navigator.userAgent; // 'foo'
person Tyler Long    schedule 30.01.2015
comment
TypeError: попытка изменить получатель неконфигурируемого свойства. - person uchuugaka; 02.06.2015
comment
@uchuugaka, я вообще не тестировал Safari. Простите за это. - person Tyler Long; 02.06.2015
comment
Не стоит беспокоиться. Я сделал. Это самое серьезное о блокировке безопасности. Ничего из этого не работает. - person uchuugaka; 02.06.2015
comment
Я считаю, что это решение больше не работает в Firefox, потому что свойство navigator.useragent доступно только для чтения. - person Olivier de Broqueville; 19.03.2019

Чтобы обновить этот поток, defineGetter больше не работает в Jasmine, поскольку он устарел. Однако я обнаружил, что это позволяет мне изменять получатель для navigator.userAgent в jasmine:

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

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

Просто не забудьте сбросить объект навигатора после завершения тестирования в жасмине.

person Fernando De Vega    schedule 23.06.2015
comment
Мне кажется, что использование __defineGetter__ на NodeJS 6.1 (с использованием Jasmine 2) работает очень хорошо. - person Stephan Bijzitter; 15.06.2016
comment
__defineGetter__ не работал у меня на Jasmine 2.4.1 и узле 6.8.1, но использование этого сработало отлично. - person karl; 06.12.2016
comment
Как его сбросить? - person joacoleza; 26.01.2017

Для тех, кто пытается сделать то же самое в TypeScript, вот решение:

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

navigator.userAgent; // 'foo'

Или то же самое для языка:

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

Думаю, я бы выбрал подход с внедрением зависимостей. Вместо того:

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.

person Grant Wagner    schedule 20.08.2009
comment
Конечно, можно было бы просто сделать navigator.userAgent getUserAgent, тогда в процессе работы я мог бы просто переопределить getUserAgent. Пока мои определения остаются последними, насмешка становится правдой. Тем не менее, это делает настоящий javascript больше, неуклюже и неприятнее, поэтому я стараюсь этого избежать. - person Stefan Kendall; 20.08.2009

Вышеуказанные ответы не работали для PhantomJS + TypeScript. Ниже код работал у меня:

var __originalNavigator = navigator;
(window as any).navigator = new Object();
navigator["__proto__"] = __originalNavigator["__proto__"];
navigator["__defineGetter__"]('userAgent', function () { return 'Custom'; });
person blackspacer    schedule 22.08.2017
comment
Очень помогло. Лучшее решение. Спасибо - person Dumbo; 21.04.2020

Поздно в этой теме, но для 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

person Avram Virgil    schedule 06.08.2018

Попробуйте, это сработало для меня без проблем с ворсом

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

navigator.userAgent - строковое свойство только для чтения, поэтому его нельзя редактировать

person Lil'Monkey    schedule 20.08.2009
comment
navigator.userAgent - это получатель, а не свойство строки только для чтения. - person Eli Grey; 21.08.2009
comment
@ Элайджа Грей, в чем разница между геттером и свойством только для чтения, с вашей точки зрения? - person Cédric Boivin; 21.08.2009

Измените 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, вы можете дважды подделать 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
person Priince paul Gates    schedule 06.05.2014
comment
Я думаю, что метод IE, о котором вы упомянули, сделав навигатор новым объектом, не работает. пожалуйста, подтвердите - person Pratik Pattanayak; 25.02.2015

Нет, я сомневаюсь, что вы сможете сделать это с помощью javascript. Но с помощью переключателя пользовательских агентов Firefox вы можете протестировать любой пользовательский агент, который захотите, так почему бы просто не использовать его?

person Marius    schedule 20.08.2009
comment
Вы не видели ту часть, где я сказал сотни строк пользовательского агента? - person Stefan Kendall; 20.08.2009
comment
Думаю, вы не совсем понимаете цель модульного тестирования. У меня всего 30 тестовых примеров, так почему бы не запускать их вручную каждый раз, когда я вношу малейшие изменения? - person Stefan Kendall; 20.08.2009
comment
Возьмите Тестер пользовательского агента и измените код для автоматического тестирования со всеми сотнями строк пользовательского агента. Если вы знаете javascript, это должно быть очень просто - person Marius; 20.08.2009
comment
Не похоже на то, что пользователи знают JS. Ваш ответ не содержит достоверной информации о том, как на самом деле плакат может достичь этого результата. Это больше комментарий, чем ответ. - person perry; 28.11.2016