Skip to Content

Browserアクション

Browserアクションはchromedp を通して実際のChromeを操作します。ページを開き、内容を読み、入力し、要素を待ち、スクリーンショットを撮ります。Probeを実行する環境にChromeかChromiumが必要です。

外部アクションとしてmozership/probe-browser で公開しています。Probe v1.21.0までは組み込みアクションでした。ワークフローが初めて使うときにProbeがダウンロードし、固定したコミットのaction.ymlが示すSHA-256と一致する実行ファイルだけを実行します。Probe v1.21.0以降が必要です。各リリース のノートの先頭に、コピーして使うusesの行があります。

このアクションはページが何をするかを判定できないため、ガードに従いません。--read-onlyや--allow-hostの下では、--allow-actionで指定しない限り、このアクションを使うステップを拒否します。

基本的な構文

browserステップでは、行う操作をactionsに順番に並べます。ステップごとに新しいブラウザを起動し、操作を実行して閉じます。そのためクッキーやログイン状態は、そのままでは次のステップに引き継がれません。一連の流れを1つのステップにまとめるか、ログイン状態の引き継ぎのとおりsave_storage_stateでログイン状態を保存し、次のステップのブラウザをそこから始めます。

steps: - name: Read the heading uses: github.com/mozership/probe-browser@634baa8dff2b273ed326996fec4d91c2dba9d38b # v0.2.0 with: actions: - name: navigate url: "{{vars.url}}/" - name: text id: heading selector: h1 test: res.code == 0 && res.results.heading == "Welcome"

パラメータ

以下はwithの直下に書きます。

パラメータ型デフォルト説明
actionsList必須順に実行する操作。アクションを参照
timeout期間の文字列、または秒数5sステップ全体の制限時間。ブラウザの起動とすべての操作を含む。30s、30、"30"はどれも30秒。それ以外の値や0以下の値はエラーになる
headlessBooleantrueウィンドウを表示せずにChromeを動かす
window_wInteger1920ウィンドウの幅(ピクセル)
window_hInteger1080ウィンドウの高さ(ピクセル)
evidence_dirString一時ディレクトリ操作が失敗したときにページを保存する場所。失敗時のページを参照
storage_stateStringなしsave_storage_stateが書いたファイル。ブラウザはこのファイルのクッキーとlocalStorageを持った状態で始まる。ログイン状態の引き継ぎを参照

Chromeの起動にもtimeoutの一部を使い、1秒以上かかることもよくあります。短すぎる制限では、最初のページを開く前に時間切れになることがあります。

アクション

actionsの各要素は、nameで操作を指定し、次のフィールドを取ります。

フィールド使うアクション説明
nameすべて下の表にある操作の名前
id結果を返すアクションres.resultsやres.filepathsで結果を引くキー。省くとアクション名がキーになるため、同じ種類のアクションを後に置くと前の結果を置き換える
urlnavigate開くアドレス
selector要素に対するアクションCSSセレクタ
valuetype、send_keys、select使うテキスト
attributeget_attribute読む属性をhrefか[href]で指定する。リストの場合は最初の要素だけを読む
qualityfull_screenshot1から99はその品質のJPEGで保存する。それ以外は、省略、0、100以上、負の数も含めてPNGで保存する
pathスクリーンショット、save_storage_stateスクリーンショットでは非推奨で、画像をこのパスにも書き出す。代わりにres.filepathsを使う。save_storage_stateでは、一時ディレクトリに新しいファイルを作る代わりに書き出す場所
equalswait_textテキストがこの値と一致するまで待つ。equals: ""は空のテキストになるまで待つ
containswait_textテキストがこの値を含むまで待つ
matcheswait_textテキストがこの正規表現に一致するまで待つ。正規表現はGoの構文 で書く

wait_textにはequals、contains、matchesのうち1つまでしか書けず、ほかのアクションには書けません。空のcontainsやmatchesはどのテキストでも通るため、これもエラーになります。これらに反するワークフローは、ブラウザを起動する前にエラーになります。

要素を探すアクションは、要素が現れるまで待ちます。先にtimeoutが尽きるとステップは失敗します。多くは要素が表示されるまでも待ちます。

移動と待機

ページを開くか、次の操作に必要な状態になるまでステップを待たせます。

アクション内容
navigateurlを開き、ページの読み込みを待つ。net::ERR_CONNECTION_REFUSEDのようにブラウザがネットワークのエラーを返すとすぐに失敗する。応答のないアドレスではtimeoutまで待つ
wait_visibleselectorが表示されるまで待つ
wait_not_visibleselectorが表示されなくなるまで待つ
wait_enabledselectorが表示され、有効になるまで待つ
wait_readyページのbodyの準備ができるまで待つ。selectorは使わない

ページの読み取り

以下は文字列をres.resultsに、idまたはアクション名をキーにして入れます。

アクション結果
textselectorのテキスト
wait_textselectorが表示されるまで待ち、そのテキスト。equals、contains、matchesを書くと、テキストが期待どおりになるまでも待つ。テキストを待つを参照
valueinputなどのフォーム項目の値
get_attributeselectorの、attributeの最初の属性の値。要素がその属性を持たなければ空文字列
get_html表示されたselectorの外側のHTML

操作

利用者と同じようにページを操作します。結果は返しません。

アクション内容
click表示されたselectorをクリックする
double_click表示されたselectorをダブルクリックする
type、send_keysselectorの内容を消してからvalueを入力する
submitselectorが属するフォームを送信する
focusselectorにフォーカスを移す
scrollselectorが見える位置までスクロールする
selectselectorの<select>が表示されたら、値がvalueの選択肢を、なければラベルがvalueの選択肢を選び、利用者が選んだときと同じくinputとchangeのイベントを発生させる。選択肢がない、選択肢が無効になっている、<select>が無効になっている、要素が<select>でない場合は、その理由を示してステップが失敗する。囲んでいる<optgroup>や<fieldset>のdisabledによる無効も含む。失敗のメッセージには選択肢の値が入る。<select multiple>では、すでに選ばれている選択肢に加える
hoverselectorにmouseoverイベントを送る。待たず、一致する要素がなければ何もしない
right_clickselectorにcontextmenuイベントを送る。待たず、一致する要素がなければ何もしない

スクリーンショット

以下は画像をファイルに保存し、そのパスをres.filepathsに、idまたはアクション名をキーにして入れます。ファイル名の拡張子は形式に合わせて.pngか.jpgになります。

アクション画像
capture_screenshotウィンドウに見えている範囲。PNG
full_screenshotページ全体。PNG。qualityが1から99ならJPEG
screenshot表示されたselectorだけ。PNG

ストレージの状態

アクション内容
save_storage_stateブラウザのクッキーと、開いているページのオリジンのlocalStorageをJSONファイルに書き出し、そのパスをres.filepathsに、idまたはアクション名をキーにして入れる。ファイルはpathに書く。pathがなければ一時ディレクトリにprobe-browser-state-*.jsonを新しく作る。ログイン状態の引き継ぎを参照

レスポンスオブジェクト

すべての操作が成功すると、ステップからは次の値を参照できます。

フィールド型説明
res.codeInteger0
res.resultsObject読み取りのアクションが返した値。キーはidまたはアクション名
res.filepathsObjectスクリーンショットとストレージの状態の保存先。キーはidまたはアクション名
res.console_errorsList操作の実行中にページがコンソールに書いたエラー。書いた順に並ぶ。コンソールの確認を参照
rt.durationStringステップにかかった時間。"663.268333ms"など
statusInteger0

操作が失敗した場合は、テストできるレスポンスはありません。ステップはエラーハンドリングのとおりアクションのエラーとして失敗します。

例

どの例も1つのステップです。1つのステップの操作は同じブラウザを共有し、次のステップは新しいブラウザで始まるためです。ただしログイン状態の引き継ぎは、ログイン状態を次のステップに渡すため、2つのステップからなります。

ログイン

ログインと、それが成功したことの確認は同じブラウザで行う必要があるため、1つのステップにします。

secrets: - PASSWORD vars: url: "{{APP_URL}}" user: "{{USERNAME}}" password: "{{PASSWORD}}" jobs: - name: Sign in steps: - name: Log in and reach the dashboard uses: github.com/mozership/probe-browser@634baa8dff2b273ed326996fec4d91c2dba9d38b # v0.2.0 with: timeout: 30s actions: - name: navigate url: "{{vars.url}}/login" - name: type selector: "#username" value: "{{vars.user}}" - name: type selector: "#password" value: "{{vars.password}}" - name: click selector: "button[type='submit']" - name: wait_visible selector: "#dashboard" - name: text id: greeting selector: "#dashboard h1" test: res.code == 0 && res.results.greeting contains vars.user

複数の値の読み取り

結果が互いに置き換わらないよう、読み取りのアクションにはそれぞれidを付けます。

- name: Product page uses: github.com/mozership/probe-browser@634baa8dff2b273ed326996fec4d91c2dba9d38b # v0.2.0 with: actions: - name: navigate url: "{{vars.url}}/products/42" - name: text id: title selector: "h1" - name: text id: price selector: ".price" - name: get_attribute id: image selector: "img.product" attribute: [src] test: | res.code == 0 && res.results.title != "" && res.results.price startsWith "$" outputs: image_url: res.results.image

テキストを待つ

データを取得して表示するページでは、読み込みから少し遅れてデータが表示されます。そのため、要素が表示された時点でtextで読むとLoadingが返り、テストが失敗することがあります。wait_textにequals、contains、matchesを書くと、テキストが期待どおりになるまで読み直します。

- name: Order list uses: github.com/mozership/probe-browser@634baa8dff2b273ed326996fec4d91c2dba9d38b # v0.2.0 with: timeout: 30s actions: - name: navigate url: "{{vars.url}}/orders" - name: wait_text id: status selector: "#status" matches: '^\d+ orders$' test: res.code == 0 outputs: status: res.results.status

先にステップのtimeoutが尽きると、最後に読んだテキストを示すアクションのエラーとしてステップが失敗し、失敗時のページのとおりページを保存します。

wait_text "#status": the text was "Loading", want it to match "^\\d+ orders$": context deadline exceeded (page at failure: ...)

ログイン状態の引き継ぎ

ログインするステップでsave_storage_stateを使ってログイン状態を保存し、後のステップでstorage_stateにそのファイルを指定すると、後のステップのブラウザはログインした状態で始まります。ステップごとにログインし直す必要はありません。

- name: Sign in id: login uses: github.com/mozership/probe-browser@634baa8dff2b273ed326996fec4d91c2dba9d38b # v0.2.0 with: timeout: 30s actions: - name: navigate url: "{{vars.url}}/login" - name: type selector: "#username" value: "{{vars.user}}" - name: type selector: "#password" value: "{{vars.password}}" - name: click selector: "button[type='submit']" - name: wait_visible selector: "#dashboard" - name: save_storage_state id: state test: res.code == 0 outputs: state: res.filepaths.state - name: Orders uses: github.com/mozership/probe-browser@634baa8dff2b273ed326996fec4d91c2dba9d38b # v0.2.0 with: storage_state: "{{outputs.login.state}}" actions: - name: navigate url: "{{vars.url}}/orders" - name: wait_text selector: "h1" equals: "Orders" test: res.code == 0

ファイルには、ブラウザが持つすべてのサイトのクッキー(HttpOnlyのものも含む)と、保存したときに開いていたページのオリジンのlocalStorageが入ります。別のオリジンのlocalStorageも残すには、そのオリジンのページを開いた状態で保存します。ファイルはPlaywrightのstorage state と同じ形式なので、どちらのツールで書いたファイルももう一方で読めます。

storage_stateを指定したブラウザは、最初の操作の前にクッキーを読み込みます。localStorageは、項目のあるオリジンごとにそのオリジンのページを開いて書き込みます。このページへのリクエストにはブラウザの中で空のページを返すため、サーバーには届かず、アプリのスクリプトも動きません。最初の操作が始まるときには、ブラウザはabout:blankに戻っています。ファイルがない場合や形式が違う場合は、ブラウザを起動する前にステップが失敗します。ファイルを書くステップが失敗していた場合も同じです。

クッキーを使えばそのユーザーとしてログインできるため、ファイルはProbeを実行したユーザーだけが読めるようにします。pathを指定しなければ一時ディレクトリに書くため、リポジトリには入りません。pathを指定する場合は、バージョン管理の対象と、CIでアップロードするファイルから外してください。

コンソールの確認

クリックの処理で例外が起きるなど、ページは表示に出ないところで壊れていることがあります。res.console_errorsには、操作の実行中にページがコンソールにエラーとして書いたものが入ります。console.errorの呼び出し、失敗したconsole.assert、捕まえられなかった例外と、処理されなかったPromiseのrejectです。各要素は次のフィールドを持ちます。

フィールド説明
typeerror、assert、exceptionのいずれか
textDevToolsのコンソールに表示される内容。例外ではUncaughtで始まり、PromiseのrejectではUncaught (in promise)で始まる
url、line、columnエラーが発生した場所。行と列はDevToolsと同じく1から数える。ブラウザが場所を返さない場合は空と0

画像の読み込みの失敗のようなブラウザ自身のメッセージや、ログと警告は入りません。エラーはページのタブから読むため、ポップアップや、別のサイトのiframeのエラーも入りません。

エラーが1つでもあればステップを失敗させるには、リストが空であることをテストします。

- name: Checkout page uses: github.com/mozership/probe-browser@634baa8dff2b273ed326996fec4d91c2dba9d38b # v0.2.0 with: actions: - name: navigate url: "{{vars.url}}/checkout" - name: click selector: "#pay" - name: wait_visible selector: "#receipt" test: res.code == 0 && len(res.console_errors) == 0 echo: "{{res.console_errors}}"

操作が失敗したときは、失敗のメッセージに最初のいくつかのエラーが入ります。操作が失敗した原因がそのエラーであることが多いためです。

... context deadline exceeded (console errors: "Uncaught TypeError: Cannot read properties of null (reading 'total') (http://localhost:8090/checkout.js:42:17)") (page at failure: ...)

スクリーンショットを残す

保存した画像のパスはres.filepathsに入り、表示したり次に渡したりできます。

- name: Checkout page uses: github.com/mozership/probe-browser@634baa8dff2b273ed326996fec4d91c2dba9d38b # v0.2.0 with: window_w: 1280 window_h: 800 actions: - name: navigate url: "{{vars.url}}/checkout" - name: full_screenshot id: page test: res.code == 0 echo: "Saved {{res.filepaths.page}}"

エラーハンドリング

アクションが完了できないと、ステップ全体がアクションのエラーとして失敗し、実行は終了ステータス3で終わります。セレクタが何にも一致しなくても空の結果にはなりません。wait_visible、text、clickなど要素を探すアクションは、ステップのtimeoutまで待ち続けます。ナビゲーションは、接続の拒否のようにブラウザがネットワークのエラーを返すとすぐに失敗しますが、応答のないアドレスではtimeoutまで待ちます。いずれの場合もテストできるresはなく、失敗のメッセージにはブラウザのエラーと、コンソールの確認のとおりページがコンソールに書いたエラーが入ります。

失敗時のページ

アクションが失敗すると、Probeはその時点のページを保存します。ページ全体のPNGのスクリーンショットと、文書のHTMLです。2つのファイルにはprobe-browser-failure-1947177907.pngと.htmlのように同じ名前が付きます。失敗のメッセージには、ページのURLとあわせて両方のファイルのパスが入ります。

... context deadline exceeded (page at failure: url http://localhost:8090/, screenshot /tmp/probe-browser-failure-1947177907.png, html /tmp/probe-browser-failure-1947177907.html)

--reportで書き出すファイルでも、このメッセージが失敗のmessageになります。テストがfalseになるのはアクションの失敗ではないため、そのときはページを保存しません。テキストが期待どおりでないときに保存させるには、testで確かめる代わりにwait_textにequals、contains、matchesを書きます。どんな場合でもページを残すにはcapture_screenshotアクションを加えます。

ファイルはシステムの一時ディレクトリに保存します。evidence_dirを指定するとそこに保存し、ディレクトリは必要に応じて作成します。CIのアーティファクトとしてアップロードする場合に便利です。ページには個人の情報が写りうるため、ファイルはProbeを実行したユーザーだけが読めるようにします。

- name: Checkout page uses: github.com/mozership/probe-browser@634baa8dff2b273ed326996fec4d91c2dba9d38b # v0.2.0 with: evidence_dir: out/browser actions: - name: navigate url: "{{vars.url}}/checkout" - name: wait_visible selector: "#pay"

ページを読み取れるように、ブラウザはタイムアウトのあとも最大10秒残します。ブラウザが起動しなかった場合や、時間切れの時点でページがまだ読み込み中だった場合など、読み取れなかったときはその旨をメッセージに書き、何も保存しません。

CIでの実行

CIのランナーには、ブラウザと、手元より少し長い時間が必要です。

  • ランナーにChromeかChromiumを入れる。
  • headless: trueのままにするか、ウィンドウが必要ならXvfbなどのディスプレイを用意する。
  • コンテナで多く必要になるため、Chromeは--no-sandboxで起動する。信頼できるサイトだけを開く。
  • コールドスタートのランナーではChromeの起動が遅いため、timeoutに余裕を持たせる。
更新日時