web-dev-qa-db-ja.com

javascriptでuseragentをモックしていますか?

プログラムでnavigator.userAgentをオンザフライで変更する方法を探しています。自動化されたjavascriptユニットテスターを取得しようとして失敗したため、私はあきらめ、fireunitの使用を開始しようとしました。すぐに、JavaScriptのテストに実際のブラウザーを使用することの壁の1つに非難しました。

具体的には、navigator.userAgentを変更して、数百のuserAgent文字列をシミュレートし、特定の関数で適切な検出とカバレッジを確保する必要があります。 navigator.userAgentは読み取り専用なので、動けないようです! navigator.userAgentをモックするにはどうすればよいですか?ユーザーエージェントスイッチャー(プラグイン)はFFのユーザーエージェントを切り替えることができますが、javascript内で行うことはできますか?

63
Stefan Kendall

試してください:

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

navigator.userAgent; // 'foo'

FF2とFF3で試してみました。

104
Crescent Fresh

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'; });
24
maxyfc

次のソリューションは、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');
22
Joel Richard

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の変更を許可しませんが、上記の別のソリューションで指摘されているように、ナビゲータオブジェクト全体を置き換えることができます。

7
Bundyo

TypeScriptで同じことを行おうとする場合の解決策は次のとおりです。

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

navigator.userAgent; // 'foo'

または言語についても同じこと:

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

このスレッドを更新するために、defesGetterは非推奨になったため、Jasmineではもう機能しません。ただし、これにより、ジャスミンのnavigator.userAgentのゲッターを変更できることがわかりました。

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

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

ジャスミンでのテストが完了したら、ナビゲーターオブジェクトをリセットすることを忘れないでください

3
fdvj

Crescent Freshの答えは正しいです。しかし、問題があります:__defineGetter__は非推奨です:

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

廃止この機能はWeb標準から削除されました。一部のブラウザではまだサポートされている場合がありますが、削除される過程にあります。古いプロジェクトや新しいプロジェクトでは使用しないでください。これを使用するページまたはWebアプリはいつでも壊れる可能性があります。

代わりにdefinePropertyを使用する必要があります。

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

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

私は依存性注入のアプローチを取ると思います。の代わりに:

_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と統合できます。

2
Grant Wagner

ユニットテストで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。

1
So6

このトピックの後期ですが、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

0
Avram Virgil

上記の回答は、PhantomJS + TypeScriptでは機能しませんでした。以下のコードは私のために働いた:

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

navigator.userAgentは読み取り専用の文字列プロパティであるため、編集できません

0
Lil'Monkey