これを行う良い方法はありますか?コンテンツスクリプトとしてWebサイトと対話し、localstorageを使用してデータを保存する拡張機能を作成しています。この動作をテストするために使用できるツール、フレームワークなどはありますか?javascriptをテストするための一般的なツールがいくつかあることはわかっていますが、それらは拡張機能をテストするのに十分な能力がありますか?単体テストが最も重要ですが、他の種類のテスト(統合テストなど)にも興味があります。
7 に答える
はい、既存のフレームワークは非常に便利です。
最近、私はすべてのテストを、アプリケーションに埋め込まれているが、物理的に入力しない限り到達できない「テスト」ページに配置しました。
たとえば、すべてのテストを下のページからアクセスできるようにしますchrome-extension://asdasdasdasdad/unittests.html
テストはlocalStorage
などにアクセスできます。コンテンツスクリプトにアクセスするには、理論的にはテストページに埋め込まれたIFRAMEを介してテストできますが、これらはより統合レベルのテストであるため、単体テストでは実際のページから抽象化して、 localStorageへのアクセスと同様に、それらに依存しないでください。
ページを直接テストする場合は、拡張機能を調整して新しいタブを開くことができます(chrome.tab.create({"url": "someurl"})。新しいタブごとにコンテンツスクリプトを実行する必要があり、次を使用できます。テストフレームワークを使用して、コードが実行すべきことを実行したことを確認します。
いくつかのChrome拡張機能に取り組んで、、、およびsinon-chrome
を使用して単体テストを実行できるプロジェクトを思いつきました。mocha
nodejs
phantomjs
chrome.*
基本的に、事前定義されたjson応答を配置できるすべてのAPIのシノンモックを作成します。
次に、ノードのvm.runInNewContext
バックグラウンドページとphantomjs
レンダリングポップアップ/オプションページを使用してスクリプトをロードします。
そして最後に、必要な引数を指定してchromeapiが呼び出されたと主張します。
例を見てみましょう
。ボタンバッジに開いているタブの数を表示する単純なクロム拡張機能があるとします。
背景ページ:
chrome.tabs.query({}, function(tabs) {
chrome.browserAction.setBadgeText({text: String(tabs.length)});
});
それをテストするには、次のものが必要です。
- 事前定義された応答を返すためのモック
chrome.tabs.query
。たとえば、2つのタブ。 chrome.*
模擬APIをいくつかの環境に注入します- この環境で拡張コードを実行する
- ボタンバッジが「2」に等しいことを主張する
コードスニペットは次のとおりです。
const vm = require('vm');
const fs = require('fs');
const chrome = require('sinon-chrome');
// 1. mock `chrome.tabs.query` to return predefined response
chrome.tabs.query.yields([
{id: 1, title: 'Tab 1'},
{id: 2, title: 'Tab 2'}
]);
// 2. inject our mocked chrome.* api into some environment
const context = {
chrome: chrome
};
// 3. run our extension code in this environment
const code = fs.readFileSync('src/background.js');
vm.runInNewContext(code, context);
// 4. assert that button badge equals to '2'
sinon.assert.calledOnce(chrome.browserAction.setBadgeText);
sinon.assert.calledWithMatch(chrome.browserAction.setBadgeText, {
text: "2"
});
これで、それをmochaのdescribe..it
関数にラップして、ターミナルから実行できます。
$ mocha
background page
✓ should display opened tabs count in button badge
1 passing (98ms)
あなたはここで完全な例を見つけることができます。
さらに、sinon-chromeを使用すると、事前定義された応答で任意のchromeイベントをトリガーできます。
chrome.tab.onCreated.trigger({url: 'http://google.com'});
うまく機能しているsinon.js
ように見えますが、プレーンなJasmineを使用して、必要なChromeコールバックをモックすることもできます。例:
chrome = {
runtime: {
onMessage : {
addListener : function() {}
}
}
}
describe("JSGuardian", function() {
describe("BlockCache", function() {
beforeEach(function() {
this.blockCache = new BlockCache();
});
it("should recognize added urls", function() {
this.blockCache.add("http://some.url");
expect(this.blockCache.allow("http://some.url")).toBe(false);
});
} // ... etc
デフォルトSpecRunner.html
を変更してコードを実行するだけです。
Chromeの既存のツールについて:
Chromeデベロッパーツールには、ローカルストレージのリソースのセクションがあります。
開発者ツール>リソース>ローカルストレージ
そこでローカルストレージの変更を参照してください。
console.profileを使用して、パフォーマンスをテストし、実行時のコールスタックを監視できます。
- fileSystemの場合このURLを使用して、ファイルがアップロードされているかどうかを確認できます:filesystem:chrome-extension:///temporary/
バックグラウンドページ/スクリプトやメッセージパッシングなしでコンテンツスクリプトとローカルストレージを一緒に使用している場合、ローカルストレージにはそのサイトからのみアクセスできます。したがって、これらのページをテストするには、これらのタブにテストスクリプトを挿入する必要があります。
Seleniumは拡張機能の「ビュー」を駆動できないため、拡張機能がプリインストールされた新しいブラウザーインスタンスを起動するために、 Selenium Webドライバーを使用でき、クリックに対してpyautoguiを使用できることがわかりました。クリックした後、スクリーンショットを作成し、それらを「予想される」ものと比較して、95%の類似性を期待できます(異なるブラウザーでは、数ピクセルへのマークアップの移動が許容されるため)。
エンドツーエンドをテストするには、を使用できますpuppeteer
。ロードされた拡張機能をチェックし、拡張title
機能がシークレットモードで有効になっているかどうかを確認するために、拡張機能用に作成したスニペットを次に示します。
const path = require("path");
const puppeteer = require("puppeteer");
const assert = require("assert");
const Constants = require("../contants");
const Utils = require("./util");
const extensionID = Constants.EXTENSION_ID;
const extensionPath = path.join(__dirname, "../dist");
const extensionOptionHtml = "option.html";
const extPage = `chrome-extension://${extensionID}/${extensionOptionHtml}`;
let extensionPage = null;
let browser = null;
async function boot() {
browser = await puppeteer.launch({
// slowMo: 250,
headless: false, // extension are allowed only in head-full mode
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
"--no-sandbox",
"--disable-setuid-sandbox"
]
});
extensionPage = await browser.newPage();
await extensionPage.goto(extPage);
}
describe("Extension UI Testing", function() {
this.timeout(20000); // default is 2 seconds and that may not be enough to boot browsers and pages.
before(async function() {
await boot();
});
describe("option page home", async function() {
it("check title", async function() {
const h1 = "Allow extension in Incognito Mode";
const extH1 = await extensionPage.evaluate(() =>
document.querySelector("h1").textContent.trim()
);
assert.equal(extH1, h1);
});
it("show option ui after enabling extension in incognito", async () => {
await extensionPage.goto(`chrome://extensions/?id=${extensionID}`);
extensionPage.evaluate(() =>
document
.querySelector("body > extensions-manager")
.shadowRoot.querySelector("#viewManager > extensions-detail-view")
.shadowRoot.querySelector("#allow-incognito")
.shadowRoot.querySelector("#crToggle")
.click()
);
await Utils.sleep(2000);
await extensionPage.goto(
`chrome-extension://${extensionID}/${extensionOptionHtml}`
);
const h3 = "Mark Incognito";
const headingID = `#${Constants.OPTION_SCRIPT_HOST_ID} > div > div > header > div > h6`;
await extensionPage.waitFor(headingID);
console.log({ headingID });
const extH3 = await extensionPage.evaluate(headingID => {
return document.querySelector(headingID).textContent.trim();
}, headingID);
console.log({ extH3 });
assert.equal(extH3, h3);
});
});
after(async function() {
await browser.close();
});
});
以前のいくつかの回答を確認するために、JasmineはChrome拡張機能でうまく機能しているようです。バージョン3.4.0を使用しています。
Jasmineスパイを使用して、さまざまなAPIのテストダブルを簡単に作成できます。自分でゼロから作成する必要はありません。例えば:
describe("Test suite", function() {
it("Test case", function() {
// Set up spies and fake data.
spyOn(chrome.browserAction, "setPopup");
spyOn(chrome.identity, "removeCachedAuthToken");
fakeToken = "faketoken-faketoken-faketoken";
fakeWindow = jasmine.createSpyObj("window", ["close"]);
// Call the function under test.
logout(fakeWindow, fakeToken);
// Perform assertions.
expect(chrome.browserAction.setPopup).toHaveBeenCalledWith({popup: ""});
expect(chrome.identity.removeCachedAuthToken).toHaveBeenCalledWith({token: fakeToken});
expect(fakeWindow.close.calls.count()).toEqual(1);
});
});
それが役立つ場合は、いくつかの詳細:
別の回答で述べたように、テストを実行するブラウザー拡張機能の一部としてHTMLページを作成しました。HTMLページには、Jasmineライブラリ、拡張機能のJavaScriptコード、およびテストスイートが含まれています。テストは自動的に実行され、結果はフォーマットされます。テストランナーや結果フォーマッターを作成する必要はありません。インストール手順に従い、そこに記載されているHTMLを使用してテストランナーページを作成し、テストスイートもページに含めます。
Jasmineフレームワークを別のホストから動的にフェッチできるとは思わないので、拡張機能にJasmineリリースを含めました。もちろん、本番用の拡張機能を構築するときは、それとテストケースを省略します。
コマンドラインでテストを実行する方法を見ていません。これは、自動展開ツールに便利です。