Verspotten Sie einen Useragent in Javascript?

74

Ich suche nach einer Möglichkeit, navigator.userAgent im laufenden Betrieb programmgesteuert zu ändern. Bei meinem fehlgeschlagenen Versuch, einen automatisierten Javascript-Unit-Tester zu erhalten, gab ich auf und versuchte, Fireunit zu verwenden. Sofort bin ich in eine der Wände geraten, in denen ein tatsächlicher Browser für Javascript-Tests verwendet wurde.

Insbesondere muss ich navigator.userAgent ändern, um einige hundert userAgent-Zeichenfolgen zu simulieren und eine ordnungsgemäße Erkennung und Abdeckung einer bestimmten Funktion sicherzustellen. navigator.userAgent ist schreibgeschützt, also stecke ich fest! Wie kann ich navigator.userAgent verspotten? User Agent Switcher (Plugin) kann den Useragent von FF wechseln, aber kann ich dies in Javascript tun?

Stefan Kendall
quelle
Hast du dir env.js angesehen? ( groups.google.com/group/envjs )
Aaron Digulla

Antworten:

115

Versuchen:

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

navigator.userAgent; // 'foo'

Versuchte es in FF2 und FF3.

Halbmond frisch
quelle
4
Kann diese Lösung verwendet werden, um den Benutzeragenten von iframe festzulegen?
MANnDAaR
Funktioniert bei mir. (IE unter Windows)
Tyler Long
3
__defineGetter__ ist veraltet, verwenden Sie defineProperty stattdessen. Überprüfen Sie meine Antwort unten.
Tyler Long
bei Verwendung von eckigen .. stackoverflow.com/questions/24702003/…
danday74
Ich habe kürzlich eine Übersicht erstellt, um schreibgeschützte Eigenschaften beschreibbar zu machen (Sie finden ein Beispiel zum Überschreiben von navigator.userAgent). Es ist hässlich und nicht zu empfehlen, aber ich habe es für Unit-Tests verwendet, um Benutzeragenten im laufenden Betrieb zu wechseln. Seien Sie also vorsichtig. Kern
Philipp Möhler
24

Hinzufügen auf Crescent Frische-Lösung , die Neudefinition navigator.userAgentnicht Getter scheint nicht zu Arbeit in Safari 5.0.5 (auf Windows 7 und Mac OS X 10.6.7).

Sie müssen ein neues Objekt erstellen, das vom Objekt erbt, navigatorund einen neuen userAgentGetter definieren , um den ursprünglichen userAgentGetter auszublenden navigator:

var __originalNavigator = navigator;
navigator = new Object();
navigator.__proto__ = __originalNavigator;
navigator.__defineGetter__('userAgent', function () { return 'Custom'; });
maxyfc
quelle
Ich kann das window.navigator-Objekt nicht überschreiben, daher funktioniert Ihre Lösung bei mir nicht. Der von 'Crescent Fresh' tut es.
Cburgmer
6
Dies funktioniert auch mit Phantomjs ... das neue Objekt wird benötigt. Ich denke, es ist eine Webkit-Sache.
Pykler
Funktioniert nicht in Safari Version 12.1.2 (14607.3.9). Javascript-Fehler online navigator.__proto__ = __originalNavigator;heißt es: cyclic __proto__ valueoder so. Durch Entfernen dieser Zeile wird der Benutzeragent "lokal arbeiten", was bedeutet navigator.userAgent, dass er das zurückerhält, was Sie festgelegt haben. Bei XMLHttpRequest-Aufrufen wird jedoch nur die Standard-UA verwendet.
Jonny
24

Die folgende Lösung funktioniert in Chrome, Firefox, Safari, IE9 + und auch mit 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
            });
        }
    }
}

Beispiele:

setUserAgent(window, 'new user agent');
setUserAgent(document.querySelector('iframe').contentWindow, 'new user agent');
Joel Richard
quelle
Ich habe auf Safari 8 getestet und es schien nicht zu funktionieren. Ich verwende console.log (navigator.userAgent) und die Konsole protokolliert die Standardzeichenfolge des Benutzeragenten.
Aero Wang
Ich denke, es könnte sein, dass ich console.log vor dem Skript habe, das den Benutzeragenten ändert.
Aero Wang
@AeroWindwalker Ich habe es gerade wieder in Safari 8 getestet und es funktioniert.
Joel Richard
Ja, mir wurde klar, dass das Skript funktioniert. Es ist nur so, dass der Iframe den Standardbenutzeragenten an den Server gesendet hat, bevor das Skript einen neuen Benutzeragenten auf ihn anwendet.
Aero Wang
1
@AeroWang Wie hast du das gelöst? the iframe sent the default user agent to the server before the script applies a new user agent to it.
Licht
8

Wenn Sie Object.defineProperty verwenden, sollten Sie dem Mix mehrere weitere Browser hinzufügen:

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

Dieser Code sollte in Firefox 1.5+ , Chrome 6+ , Opera 10.5+ und IE9 + funktionieren (und wurde getestet) . Leider erlaubt Safari auf keiner Plattform das Ändern des userAgent.

Bearbeiten: Safari erlaubt nicht das Ändern des userAgent, aber man kann das gesamte Navigatorobjekt ersetzen , wie in einer anderen Lösung oben ausgeführt.

Bundyo
quelle
Hey Bundyo, gibt es eine Möglichkeit, dasselbe in IE8 zu tun?
Pratik Pattanayak
Leider kenne ich keine.
Bundyo
7

Für diejenigen hier, die den userAgent-Wert in Komponententests ändern müssen, funktioniert die Lösung von Tyler Long. Wenn Sie jedoch den anfänglichen userAgent wiederherstellen oder mehrmals ändern möchten, müssen Sie die Eigenschaft wahrscheinlich als konfigurierbar festlegen:

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

Andernfalls könnte ein TypeError: Cannot redefine propertyFehler auftreten. Funktioniert für mich auf Chrome Headless.

So6
quelle
1
Ich bin nach Versuch und Irrtum zu dem gleichen Schluss gekommen und wünschte, ich würde diese Antwort zuerst lesen.
MDahlke
Dies funktioniert derzeit nicht für mich in Windows Google Chrome Version 78.0.3904.108 (Official Build) (64-Bit)
Ryan
Danke, hat wie ein Zauber funktioniertconst setUserAgent = (userAgent: 'Chrome' | 'Android') => { Object.defineProperty(navigator, 'userAgent', { get: () => userAgent, configurable: true, }); };
Aamir Afridi
4

Die Antwort von Crescent Fresh ist richtig. Aber es gibt ein Problem: __defineGetter__ist veraltet:

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

Veraltet Diese Funktion wurde aus den Webstandards entfernt. Obwohl einige Browser dies möglicherweise noch unterstützen, wird es gerade gelöscht. Verwenden Sie es nicht in alten oder neuen Projekten. Seiten oder Web-Apps, die es verwenden, können jederzeit beschädigt werden.

Sie sollten definePropertystattdessen verwenden:

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

navigator.userAgent; // 'foo'
Tyler Long
quelle
1
TypeError: Versuch, den Getter einer nicht konfigurierbaren Eigenschaft zu ändern.
Uchuugaka
@uchuugaka, ich habe Safari überhaupt nicht getestet. Das tut mir leid.
Tyler Long
Keine Sorgen. Ich tat. Es ist sehr ernst, die Sicherheit zu sperren. Keine dieser Arbeiten.
Uchuugaka
Ich glaube, diese Lösung funktioniert in Firefox nicht mehr, da die Eigenschaft navigator.useragent schreibgeschützt ist.
Olivier de Broqueville
4

Um diesen Thread zu aktualisieren, funktioniert defineGetter in Jasmine nicht mehr, da es veraltet ist. Ich habe jedoch festgestellt, dass ich damit den Getter für navigator.userAgent in Jasmin ändern kann:

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

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

Denken Sie daran, das Navigatorobjekt zurückzusetzen, wenn Sie mit dem Testen in Jasmin fertig sind

Fernando De Vega
quelle
Die Verwendung __defineGetter__unter NodeJS 6.1 (unter Verwendung von Jasmine 2) scheint für mich ziemlich gut zu funktionieren.
Stephan Bijzitter
__defineGetter__ hat bei Jasmine 2.4.1 und Node 6.8.1 nicht funktioniert, aber dies hat perfekt funktioniert.
Karl
Wie setzen Sie es zurück?
Joacoleza
3

Für diejenigen, die versuchen, dasselbe in TypeScript zu tun, ist hier die Lösung:

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

navigator.userAgent; // 'foo'

Oder dasselbe für die Sprache:

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

Ich denke, ich würde einen Ansatz zur Abhängigkeitsinjektion wählen. Anstatt:

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

Vielleicht machen Sie so etwas wie:

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

Dann können Sie "verspotten", getUserAgent()indem Sie Folgendes tun:

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

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

Dieses Design ist immer noch nicht vollständig automatisiert (Sie müssen den Getter manuell umbenennen, um Ihre Tests durchzuführen), und es fügt etwas so Einfachem wie der Bedienung eine Menge Komplexität hinzu navigator.userAgent, und ich bin mir nicht sicher, wie Sie tatsächlich eines identifizieren würden Fehler myFunction, aber ich dachte nur, ich würde es da rauswerfen, um Ihnen einige Ideen zu geben, wie damit umgegangen werden könnte.

Vielleicht kann die hier vorgestellte Idee der "Abhängigkeitsinjektion" irgendwie in FireUnit integriert werden.

Grant Wagner
quelle
Sicher, könnte navigator.userAgent einfach zu getUserAgent machen, dann könnte ich getUserAgent im laufenden Betrieb einfach neu definieren. Solange meine Definitionen gültig sind, wird der Schein zur Wahrheit. Es macht das eigentliche Javascript jedoch größer, klobiger und unangenehmer, also versuche ich das zu vermeiden.
Stefan Kendall
1

Die obigen Antworten funktionierten nicht für PhantomJS + TypeScript. Der folgende Code hat bei mir funktioniert:

var __originalNavigator = navigator;
(window as any).navigator = new Object();
navigator["__proto__"] = __originalNavigator["__proto__"];
navigator["__defineGetter__"]('userAgent', function () { return 'Custom'; });
Blackspacer
quelle
Hat mir sehr geholfen. Beste Lösung. Vielen Dank
Dumbo
1

Versuchen Sie dies, es hat bei mir ohne Flusenprobleme funktioniert

Object.defineProperty(global.navigator, 'userAgent', { get: () => 'iPhone' });
vnxyz
quelle
0

navigator.userAgent ist eine schreibgeschützte Zeichenfolgeeigenschaft, daher kann sie nicht bearbeitet werden

Lil'Monkey
quelle
navigator.userAgent ist ein Getter, keine "schreibgeschützte Zeichenfolgeeigenschaft".
Eli Gray
6
@Elijah Gray, was ist aus Ihrer Sicht der Unterschied zwischen einer Nur-Getter- und einer Nur-Lese-Eigenschaft?
Cédric Boivin
0

Spät zu diesem Thema, aber für Karma + Jasmin und Typescript und wenn Sie die userAgent-Eigenschaft festlegen möchten, wird dies Folgendes tun:

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

});

Dieser Artikel hat geholfen: https://www.codeproject.com/Tips/1036762/Mocking-userAgent-with-JavaScript

Avram Virgil
quelle
-1

Ändern Sie navigator.userAgent in Firefox und Opera über defineGetter

navigator.__defineGetter__('userAgent', function(){
    return( "iPhone 5" );
});

alert( navigator.userAgent ); //iPhone 5

Ändern Sie navigator.userAgent in IE und Opera über die Objektinstanz

var navigator = new Object; 
navigator.userAgent = 'iPhone 5';

alert( navigator.userAgent ); //iPhone5

Gut ist, wenn Sie an der IE-Webbrowser-Steuerung arbeiten, können Sie sowohl die HTTP-Anforderung als auch den JavaScript-Navigator.userAgent über execScript doppelt fälschen

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
Prinz Paul Gates
quelle
Ich denke, die IE-Methode, die Sie erwähnt haben, indem Sie den Navigator zu einem neuen Objekt gemacht haben, funktioniert nicht. Bitte überprüfen Sie
Pratik Pattanayak
-2

Nein, ich bezweifle, dass Sie dies in Javascript tun können. Mit dem User Agent Switcher von Firefox können Sie jedoch jeden gewünschten Useragent testen. Warum also nicht einfach diesen verwenden?

Marius
quelle
1
Haben Sie den Teil nicht gesehen, in dem ich "Hunderte von Benutzeragentenzeichenfolgen" sagte?
Stefan Kendall
1
Ich glaube, Sie verstehen den Zweck von Unit-Tests nicht ganz. "Ich habe nur 30 Testfälle. Warum also nicht jedes Mal manuell durchlaufen, wenn ich die kleinste Änderung vornehme?"
Stefan Kendall
Nehmen Sie den User Agent Tester und ändern Sie den Code, um ihn automatisch mit allen "Hunderten von User Agent-Zeichenfolgen" zu testen. Wenn Sie Javascript kennen, sollte das sehr einfach sein
Marius
Scheint nicht relevant für das Wissen der Benutzer über JS. Ihre Antwort enthält keine gültigen Informationen darüber, wie das Poster dieses Ergebnis tatsächlich erzielen würde. Dies ist eher ein Kommentar als eine Antwort.
Perry