WebExtensionsでE2Eテストを自動化した

Running Screenshot

Vim Vixen の開発では、開発スピードではなく品質を重視しており、毎リリース直前にフルリグレッションテストを行ってます。 それらの試験を手動で行っており、各リリース前にQA Pull Req.に試験結果を記録してます。

現在の試験項目は 100 を超えてます。 品質は大事にするとはいえ、各リリース毎に手動で試験を行うのは、地道で退屈な作業です。 今後機能追加などで更に試験項目数や複雑な試験が増えることは予想されます。 このままでは将来、同様の品質を保つのは難しいと思い、Vim Vixen でも E2E テストの自動化を始めることにしました。

構成

WebExtensions をテストするには、テストのセットアップや実行結果を観測するために、WebExtensions と同等の権限が必要です。 Karma の Firefox launcher 上で実行する JavaScript は、ページにロードされる JavaScript なので、そこまでの権限はありません。 そこで WebExtensions をテストするための、ヘルパー的な WebExtensions を新たに作りました。 これを ambassador アドオン と名付けています。

Karma で実行される JavaScript から、Vim Vixen のテストのセットアップや実行結果を取得するフローは次の図のようになります。 Karma 上の JavaScript から、ambassador アドオンを介して、タブの状態の取得や、他のタブのページの情報を取得します。

Architecture

ambassador アドオン

Page Script から Content Script にメッセージを送るには、Page Script が自身の Window にwindow.postMessage()でメッセージを発行します。 WebExtensions の Content Script は、ページと同じ Window にアクセスできます。 そのため Page Script から投げられたメッセージは、Content Script でも受け取れます。 ambassador アドオンの Content Script は window.addEventListener('message', callback) でメッセージを受け取ります。

ambassador アドオンの Content Script がメッセージを受け取ると、その先は WebExtensions にある runtime.sendMessage() で ambassador アドオンの Background Script にメッセージを送信します。 新たなタブを作成したり、他のタブ情報の取得は、Background Script から取得できます。 その結果を再び Page Script に返すには、Content Script にsendResponse()で結果を返し、Content Script からwindow.postMessage()でメッセージを Page Script に返します。

Vim Vixen のメッセージは、Background Script だけでなく、他のタブにメッセージを送ることが多いです。 その場合は宛先のタブ ID をメッセージに付与させ、それを Background Script を経由して、他のタブの Content Script に送ります。 Karma が実行しているタブを閉じるとテストが終了するので、試験は新たに作成したウィンドウ上で試験します。 そのため Karma から他のタブに対して、キーボード入力をしたり、ページの結果(スクロール量など)を取得します。

Karma launcher

Karma が提供しているkarma-firefox-launcherは WebExtensions に対応してません。 そのため Vim Vixen や ambassador アドオンのロードができません。 そこで Mozilla が提供している、WebExtensions 開発ツールであるweb-extを使うことにしました。 web-ext は WebExtensions のアーカイブを作成したり、WebExtensions をロードした Firefox を起動したりできるツールです。

本家の web-ext は、現在は複数の WebExtensions ができません(Issue はあがってる)。 なので自分で複数の WebExtensions をロードできるよう修正しました。 そして Karma 用の launcher を定義して、修正した web-ext を起動するようにします。 以上で Karma から Vim Vixen と ambassador アドオンをロードした Firefox が起動できるようになりました。 具体的な実装はここらへんです。

テストを書く。

Karma から ambassador アドオンとのやり取りは、クライアントライブラリとしてメッセージ送信をラップして、ambassador アドオンに含めました。 以下の例は、gg を押すとページのトップにスクロールするかの試験です。

import * as windows from "../ambassador/src/client/windows"
import * as tabs from "../ambassador/src/client/tabs"
import * as keys from "../ambassador/src/client/keys"
import * as scrolls from "../ambassador/src/client/scrolls"

describe("scroll test", () => {
  let targetWindow
  let targetTab

  before(() => {
    return windows
      .create()
      .then(win => {
        targetWindow = win
        return tabs.create(targetWindow.id, "localhost:11111")
      })
      .then(tab => {
        targetTab = tab
      })
  })

  it("scrolls top by gg", () => {
    return scrolls
      .set(targetTab.id, 100, 100)
      .then(() => {
        return keys.press(targetTab.id, "g")
      })
      .then(() => {
        return keys.press(targetTab.id, "g")
      })
      .then(() => {
        return scrolls.get(targetTab.id)
      })
      .then(actual => {
        expect(actual.y).to.be.equals(0)
      })
  })
})

まず before() メソッドで、試験前にwindows.create()メソッドでテスト用のウィンドウを作成します。 Window が作成できたら(Promise が resolve されたら)、その Window ID に対して新たにタブを作成します。 そしてタブが作成できてページがロードされたら、作成されたタブの情報をtargetTabに格納します。

試験では、まずscrolls.set() で、targetTab のスクロール量を(100, 100)にセットします。 そしてgキーを 2 度送信し、最後にscrolls.get()で現在のスクロール量取得します。

改めて見ると ES7 のawait構文と親和性が高そうですね。そのうち書き直します。

おわりに

この記事を公開した現在、まだ 1 割程度の試験しか自動化できてないです。 これから機能が増えると試験項目は増えますが、全ての試験を自動化できるとは思いません。 しかし自動化は良いことなので、これからも継続的に試験の自動化を行いたいと思います。


Profile picture

Shin'ya Ueoka

B2B向けSaaSを提供する会社の、元Webエンジニア。今はエンジニアリング組織のマネジメントをしている。