web-dev-qa-db-fra.com

Se moquer d'un useragent en javascript?

Je cherche un moyen de modifier par programmation navigator.userAgent à la volée. Dans ma tentative infructueuse d’obtenir un testeur d’unités javascript automatisé, j’ai abandonné et tenté de commencer à utiliser fireunit. Immédiatement, j'ai claqué dans l'un des murs de l'utilisation d'un navigateur réel pour les tests javascript.

Plus précisément, je dois modifier navigator.userAgent pour simuler quelques centaines de chaînes de userAgent afin d’assurer une détection et une couverture appropriées pour une fonction donnée. navigator.userAgent est en lecture seule, donc je semble bloqué! Comment puis-je me moquer de navigator.userAgent? User Agent Switcher (plugin) peut changer d'utilisateur pour FF, mais puis-je le faire en javascript?

58
Stefan Kendall

Essayer:

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

navigator.userAgent; // 'foo'

Essayé dans FF2 et FF3.

101
Crescent Fresh

Ajouter la solution de Crescent Fresh et redéfinir le navigator.userAgent getter ne semblent pas fonctionner dans Safari 5.0.5 (sous Windows 7 et Mac OS X 10.6.7).

Besoin de créer un nouvel objet qui hérite de l'objet navigator et de définir un nouveau userAgent getter pour masquer l'original userAgent getter dans navigator:

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

La solution suivante fonctionne avec Chrome, Firefox, Safari, IE9 + et également avec les 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
            });
        }
    }
}

Exemples:

setUserAgent(window, 'new user agent');
setUserAgent(document.querySelector('iframe').contentWindow, 'new user agent');
21
Joel Richard

Utiliser Object.defineProperty devrait ajouter plusieurs autres navigateurs au mélange:

if (navigator.__defineGetter__) {
    navigator.__defineGetter__("userAgent", function () { 
        return "ua"; 
    });
} else if (Object.defineProperty) { 
    Object.defineProperty(navigator, "userAgent", { 
        get: function () { 
            return "ua";
        }
    });
}

Ce code devrait fonctionner (et a été testé) dans Firefox 1.5+, Chrome 6+, Opera 10.5+ et IE9 +. Malheureusement, Safari sur n'importe quelle plate-forme ne permet pas de changer l'utilisateurAgent.

Edit: Safari n’autorise pas la modification de l’agent utilisateur, mais vous pouvez remplacer l’ensemble de l’objet navigator, comme indiqué dans une autre solution ci-dessus.

7
Bundyo

Pour mettre à jour ce fil, defineGetter ne fonctionne plus dans Jasmine car il était obsolète. Cependant, j'ai trouvé que cela me permettait de modifier le getter de navigator.userAgent dans jasmine:

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

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

N'oubliez pas de réinitialiser l'objet de navigation une fois que vous avez terminé de tester jasmine

3
fdvj

La réponse de Crescent Fresh est correcte. Mais il y a un problème: __defineGetter__ est obsolète:

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

Obsolète Cette fonctionnalité a été supprimée des normes Web . Bien que certains navigateurs puissent encore le supporter, il est en train de être laissé tomber. Ne l'utilisez pas dans des projets anciens ou nouveaux. Pages ou applications Web l'utiliser peut casser à tout moment.

Vous devriez utiliser defineProperty à la place:

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

navigator.userAgent; // 'foo'
3
Tyler Long

Pour ceux qui essaient de faire la même chose dans TypeScript, voici la solution:

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

navigator.userAgent; // 'foo'

Ou même chose pour la langue:

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

Je suppose que je prendrais une approche d'injection de dépendance. Au lieu de:

function myFunction() {
    var userAgent = navigator.userAgent;
    // do stuff with userAgent
}

Peut-être faire quelque chose comme:

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);
}

Ensuite, vous pouvez "simuler" getUserAgent() en faisant:

function getUserAgentReal() { // formerly not 'Real'
    // ...
}

function getUserAgent() { // formerly 'Mock'
    // ...
}

Cette conception n'est toujours pas complètement automatisée (vous devez renommer manuellement le getter pour effectuer vos tests), et elle ajoute une complexité supplémentaire à quelque chose d'aussi simple que d'opérer sur navigator.userAgent, et je ne suis pas sûr de savoir comment vous identifieriez tous les bogues dans myFunction, mais je pensais juste que je le jetterais là-bas pour vous donner quelques idées sur la façon dont cela pourrait être traité.

L’idée d’injection de dépendance présentée ici peut peut-être être intégrée à FireUnit.

1
Grant Wagner

En retard sur ce sujet, mais pour Karma + Jasmin et TypeScript et que vous souhaitez définir la propriété userAgent, ceci le fera: 

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

});

Cet article a aidé: https://www.codeproject.com/Tips/1036762/Mocking-userAgent-with-JavaScript

0
Avram Virgil

Les réponses ci-dessus ne fonctionnaient pas pour PhantomJS + TypeScript. Le code ci-dessous a fonctionné pour moi:

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

navigator.userAgent est une propriété de chaîne en lecture seule, il n'est donc pas possible de la modifier.

0
Lil'Monkey

Pour ceux qui sont ici parce qu'ils ont besoin de changer la valeur de UserAgent dans les tests unitaires, la solution de Tyler Long fonctionne, mais si vous souhaitez restaurer l'utilisateurAgent initial ou le modifier plusieurs fois, vous devrez probablement définir la propriété comme étant configurable:

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);

Sinon, vous pourriez rencontrer une erreur TypeError: Cannot redefine property . Fonctionne pour moi sur Chrome Headless.

0
So6