Издеваться над useragent в javascript? - PullRequest
61 голосов
/ 20 августа 2009

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

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

Ответы [ 14 ]

102 голосов
/ 20 августа 2009

Попробуйте:

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

navigator.userAgent; // 'foo'

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

24 голосов
/ 10 мая 2011

Добавление к решения 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'; });
21 голосов
/ 12 ноября 2014

Следующее решение работает в 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');
7 голосов
/ 24 марта 2014

Использование 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 на любой платформе не позволяет менять агента пользователя.

Редактировать: Safari не позволяет изменять userAgent, но можно заменить весь объект navigator , как указано в другом решении выше.

3 голосов
/ 20 октября 2016

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

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

navigator.userAgent; // 'foo'

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

(<any>navigator)['__defineGetter__']('language', function(){
    return 'de-DE';
});
3 голосов
/ 23 июня 2015

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

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

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

Просто не забудьте сбросить объект навигатора, как только вы закончите тестирование в jasmine

3 голосов
/ 30 января 2015

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'
2 голосов
/ 20 августа 2009

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

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.

1 голос
/ 08 января 2019

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

0 голосов
/ 06 августа 2018

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

...