ギリギリジンジン

なんでもギリギリだ。

動かなくなったらWebDriverのバージョンを疑え

以下のようなエラーで動かなくなった場合、Chromeをバージョンアップした影響でChromeDriverのサポートバージョンとズレてしまっているためにエラーとなっている。

$ node hoge.js 
/home/grgrjnjn/Documents/hoge/node_modules/selenium-webdriver/lib/error.js:524
    let err = new ctor(data.message)
              ^

SessionNotCreatedError: session not created: This version of ChromeDriver only supports Chrome version 107
Current browser version is 110.0.5481.177 with binary path /usr/bin/google-chrome
    at Object.throwDecodedError (/home/grgrjnjn/Documents/hoge/node_modules/selenium-webdriver/lib/error.js:524:15)
    at parseHttpResponse (/home/grgrjnjn/Documents/hoge/node_modules/selenium-webdriver/lib/http.js:587:13)
    at Executor.execute (/home/grgrjnjn/Documents/hoge/node_modules/selenium-webdriver/lib/http.js:515:28)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  remoteStacktrace: '#0 0x563b89a30463 <unknown>\n' +
    '#1 0x563b897f48d8 <unknown>\n' +
    '#2 0x563b898213c6 <unknown>\n' +
    '#3 0x563b8981c580 <unknown>\n' +
    '#4 0x563b89817c05 <unknown>\n' +
    '#5 0x563b8985b802 <unknown>\n' +
    '#6 0x563b8985b2af <unknown>\n' +
    '#7 0x563b89853443 <unknown>\n' +
    '#8 0x563b898243c5 <unknown>\n' +
    '#9 0x563b89825531 <unknown>\n' +
    '#10 0x563b89a82dce <unknown>\n' +
    '#11 0x563b89a86192 <unknown>\n' +
    '#12 0x563b89a6793e <unknown>\n' +
    '#13 0x563b89a87103 <unknown>\n' +
    '#14 0x563b89a5ad85 <unknown>\n' +
    '#15 0x563b89aa80a8 <unknown>\n' +
    '#16 0x563b89aa8239 <unknown>\n' +
    '#17 0x563b89ac3492 <unknown>\n' +
    '#18 0x7fc4cce94b43 <unknown>\n'
}

Node.js v18.12.0

Seleniumサイトの次のページ下部のクイックリファレンスからダウンロードページに飛び、使っているChromeバージョンにあったドライバをダウンロードする。

ブラウザーのドライバーをインストールする | Selenium

ドライバーはPATHが通ったところにおく。僕は簡単に以下の通りした。

$ sudo cp ~/Downloads/chromedriver_linux64/chromedriver /usr/local/bin/

Node.jsでSeleniumを使用してWebページの読み込み時間を計測する

 driver.executeScript( 
   "return (window.performance.timing.loadEventEnd - window.performance.timing.requestStart) / 1000") 
    .then(function(sec_loading_time){
       console.log(sec_loading_time); 
     }); 

上記コードをChatGPTにawaitを使った記述に変更してもらった。

(async () => {
  const sec_loading_time = await driver.executeScript(
    "return (window.performance.timing.loadEventEnd - window.performance.timing.requestStart) / 1000"
  );
  console.log(sec_loading_time);
})();

以下は、ChatGPTにコードを説明してもらったものからの抜粋。

このコードは、Node.jsでSeleniumを使用して、特定のWebページの読み込み時間を計測するためにJavaScriptを実行しています。

executeScriptの説明部分

executeScript()は、ブラウザ内でJavaScriptコードを実行するためのSeleniumのメソッドです。引数として渡されたJavaScriptコードがブラウザ内で実行され、その結果を返します。

ここはキモとなる部分

"return (window.performance.timing.loadEventEnd - window.performance.timing.requestStart) / 1000"は、ページの読み込み時間を計算するJavaScriptコードです。performance.timingオブジェクトには、ブラウザのページ読み込みに関する情報が含まれています。ここでは、ページの読み込みが完了するまでにかかった時間を計算しています。この時間を秒単位で返すように指定しています。

ここの内容が重要

ここで測定される時間は、HTMLファイルを含めたページのすべてのリソースが読み込み完了するまでの時間です。

window.performance.timingオブジェクトは、ページの読み込みに関する様々なタイミング情報を提供します。その中には、HTMLファイルの読み込みが完了するresponseEnd、JavaScriptCSSファイルなどのすべてのリソースの読み込みが完了するloadEventEndなどが含まれます。このコードでは、loadEventEndとrequestStartの差を計算することで、すべてのリソースの読み込みが完了するまでの時間を計測しています。

つまり、このコードによって計測される時間は、HTMLファイルを含めたすべてのリソースが読み込み完了するまでの時間です。

selenium.devドキュメントを読む

(一部)日本語版が提供されているのがすばらしい。

www.selenium.dev

  • 冒頭
  • 概要
  • WebDriver
    • 入門
    • ドライバー
    • ブラウザ
    • 待機
    • Web待機
    • Interactions
    • Actions API
    • BiDirectional
    • Support features
  • テストの実践

冒頭

基本

const {Builder} = require('selenium-webdriver');
require("chromedriver");

(async function helloSelenium() {
    let driver = await new Builder().forBrowser('chrome').build();

    await driver.get('https://selenium.dev');

    await driver.quit();
})();

概要

概要 | Selenium

  • Seleniumはたくさんのツールから構成されている
  • ウェブサイトのテスト自動化にはWebDriver API
  • WebDriver APIはブラウザベンダーによって提供されている
  • IDESeleniumのテストケースを開発するためのツール
  • IDEChromeFirefox拡張機能
  • IDEはユーザーの動作を記録する
  • IDESeleniumスクリプトシンタックスを学ぶための優れた方法
  • Gridは複数のブラウザーとOSの組み合わせでテストを実行できる
  • Gridはテストケースの起動の制御はローカル端末で行われ、リモート端末によって自動的に実行される

用語 | Selenium

  • ドライバーは、実際のブラウザを制御します。 ドライバーはブラウザベンダー自身が作成します。一部の人々はドライバーをプロキシと呼んでいます。

部品構成 | Selenium

ドライバーは、ChromeDriver、GeckoDriverなどブラウザー固有のものです。 最低限、WebDriverはドライバーを経由してブラウザーと通信します。 コミュニケーションは双方向です。 ドライバーはブラウザと同じシステムで動きます。簡単な例は直接通信です。

[WebDriver]<--->[Driver]<--->[Browser]

RemoteWebDriverを経由したリモート通信もできます。 RemoteWebDriverは、ドライバーおよびブラウザと同じシステムで実行されます。

[WebDriver]<--(-->[Remote WebDriver]<--->[Driver]<--->[Browser] )

Selenium ServerまたはSelenium Gridを使用してリモート通信を行うこともできます。

[WebDriver]<--->[Selenium Server or Grid]<--(-->[Driver]<--->[Browser] )

どのフレームワークに適しているか | Selenium

WebDriverのジョブは1つです。テストに関することは知りません。

そこでフレームワーク登場です。例えば NUnit for .NET, JUnitfor Java, RSpec for Ruby, Cucumber など。 テストフレームワークは、WebDriverおよびテストの関連手順の実行を担当します。

(TEST FRAMEWORK [WebDriver]<--)-->[Selenium Server or Grid]<--(-->[Driver]<--->[Browser] HOST SYSTEM)

WebDriver

  • WebDriverはブラウザをネイティブに操作します
  • WebDriverは言語バインディングと個々のブラウザ制御コードの実装の両方を参照します

入門

インストール、セットアップは、別記事に譲る。

grgrjnjn.hatenablog.jp

最初のSeleniumスクリプトを書く

8つの基本コンポーネント

  1. ドライバーインスタンスでセッションを開始
  2. ブラウザ上で操作
  3. ブラウザに関する情報をリクエス
  4. 待ち時間の設定
  5. 要素を検索するためのコマンドを送信
  6. 要素に対してアクションを実行
  7. 要素に関する情報をリクエス
  8. セッションを終了
ドライバーインスタンスでセッションを開始

詳細は ドライバーセッション | Selenium 参照

driver = await new Builder().forBrowser('chrome').build();
ブラウザ上で操作

ブラウザー ナビゲーション | Selenium (get / back / forward / refresh)

await driver.get('https://www.selenium.dev/selenium/web/web-form.html');
ブラウザに関する情報をリクエス

Browser interactions | Selenium

ウィンドウハンドル、ブラウザのサイズ/位置、クッキー、アラートなど、要求できるブラウザに関する情報の種類はたくさんあります。

let title = await driver.getTitle();
待ち時間の設定

ブラウザの現在の状態とコードを同期させることはSeleniumの最大の課題の1つであり、それをうまく行うことは高度なトピックです。

基本的には、要素を見つけようとする前にその要素がページ上にあることを確認し、それと対話しようとする前に要素が対話可能な状態にあることを確認したいのです。

暗黙の待機が最善の解決策であることはほとんどありませんが、ここで示すのが最も簡単なので、プレースホルダとして使用することにします。

詳細は 待機 | Selenium 参照

await driver.manage().setTimeouts({ implicit: 500 });
要素を検索するためのコマンドを送信

ほとんどのSeleniumセッションのコマンドの大部分は要素に関連しており、最初に要素を見つけることなく、コマンド操作することはできません。

詳細は Web要素 | Selenium 参照

let textBox = await driver.findElement(By.name('my-text'));
let submitButton = await driver.findElement(By.css('button'));
要素に対してアクションを実行

要素に対して行うアクションはほんの一握りですが、頻繁に使用することになります。

await textBox.sendKeys('Selenium');
await submitButton.click();
要素に関する情報をリクエス

要素には、要求できる多くの情報が格納されています。

詳細は Web要素に関する情報 | Selenium 参照

let value = await message.getText();
セッションを終了

ドライバプロセスが終了し、デフォルトでブラウザも終了します。

after(async () => await driver.quit());

すべてをまとめる

// firstScript.spec.js
const { By, Builder } = require('selenium-webdriver');
const { suite } = require('selenium-webdriver/testing');
const assert = require("assert");

suite(function(env) {
    describe('First script', function() {
        let driver;

        before(async function() {
            driver = await new Builder().forBrowser('chrome').build();
        });

        after(async () => await driver.quit());

        it('First Selenium script', async function() {
            await driver.get('https://www.selenium.dev/selenium/web/web-form.html');

            let title = await driver.getTitle();
            assert.equal("Web form", title);

            await driver.manage().setTimeouts({ implicit: 500 });

            let textBox = await driver.findElement(By.name('my-text'));
            let submitButton = await driver.findElement(By.css('button'));

            await textBox.sendKeys('Selenium');
            await submitButton.click();

            let message = await driver.findElement(By.id('message'));
            let value = await message.getText();
            assert.equal("Received!", value);
        });

    });
});

テスト実行

テストフレームワークmochaをインストールして実行。

$ npm install mocha
$ node firstScript.spec.js
$ npx mocha --no-timeouts firstScript.spec.js

npxの説明は npxコマンドとは? 何ができるのか? を参照。

ドライバー

ドライバーセッション | Selenium

セッションの開始と停止は、ブラウザーを開いたり閉じたりするためのものです。

セッションの作成
セッションの終了

ブラウザーオプション

Command executors Command Listeners Browser Service Remote WebDriver

ブラウザ

待機

Web待機

Interactions

Actions API

BiDirectional

Support features

IDE

Grid

コンポーネントを理解する

Selenium 詳細

Selenium+Node.js

参照すべき公式情報

ライブラリのインストール

Seleniumライブラリのインストール | Selenium

$ npm init
$ npm install selenium-webdriver

ドライバーのインストール

ブラウザーのドライバーをインストールする | Selenium

クイックリファレンスの表からChromeのドライバーのダウンロードサイトへ遷移する。 使用しているChromeのバージョンが「バージョン: 107.0.5304.87(Official Build) (64 ビット)」なので、「If you are using Chrome version 107, please download ChromeDriver 107.0.5304.62」のリンクからchromedriver_linux64.zipをダウンロード、展開して、chromedriverファイルを得る。

PATHに設定する必要があるのだけど、どこに置くのがいいのだろう? .bashrcの設定しなくても使えるように /usr/local/binにでも置いておこうか。 chromedriverが起動すればOK。

$ sudo cp ~/Downloads/chromedriver_linux64/chromedriver /usr/local/bin/
$ ls -l /usr/local/bin/chromedriver 
-rwxr-xr-x 1 root root 14454968 11月  3 17:59 /usr/local/bin/chromedriver
$ chromedriver
Starting ChromeDriver 107.0.5304.62 (1eec40d3a5764881c92085aaee66d25075c159aa-refs/branch-heads/5304@{#942}) on port 9515
Only local connections are allowed.
Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
ChromeDriver was started successfully.
(Ctrl+C)

ブラウザの起動

browser_launch.jsを作成。

const {Builder} = require('selenium-webdriver');
const driver = new Builder().forBrowser('chrome').build();
$ node browser_launch.js

非推奨ながら、chromedriverのパスを指定して起動する方法。browser_launch2.jsを作成。4行目のパスはchromedriverを置いた場所を指定。

const {Builder} = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const service = new chrome.ServiceBuilder('/usr/local/bin/chromedriver');
const driver = new Builder().forBrowser('chrome').setChromeService(service).build();
$ node browser_launch2.js

※ここ修正する

最初に書くスクリプトこちらのほうが適切

冒頭は以下のように修正可能

(async () => {

最初のSeleniumスクリプトfirstScript.spec.jsを書く

最初のSeleniumスクリプトを書く | Selenium

const { By, Builder } = require('selenium-webdriver');
const { suite } = require('selenium-webdriver/testing');
const assert = require("assert");

suite(function(env) {
    describe('First script', function() {
        let driver;

        before(async function() {
            driver = await new Builder().forBrowser('chrome').build();
        });

        after(async () => await driver.quit());

        it('First Selenium script', async function() {
            await driver.get('https://www.selenium.dev/selenium/web/web-form.html');

            let title = await driver.getTitle();
            assert.equal("Web form", title);

            await driver.manage().setTimeouts({ implicit: 500 });

            let textBox = await driver.findElement(By.name('my-text'));
            let submitButton = await driver.findElement(By.css('button'));

            await textBox.sendKeys('Selenium');
            await submitButton.click();

            let message = await driver.findElement(By.id('message'));
            let value = await message.getText();
            assert.equal("Received!", value);
        });

    });
});

テストフレームワークmochaをインストールして実行。

$ npm install mocha
$ node firstScript.spec.js

実行はエラーになる。 下記を参考に実行。

javascript - after function on selenium test is not defined - Stack Overflow

$ npx mocha --no-timeouts firstScript.spec.js
[INFO] Searching for WebDriver executables installed on the current system...
[INFO] ... located chrome
[INFO] ... located firefox
[INFO] Running tests against [chrome, firefox]


  [chrome]
    First script
      ✔ First Selenium script (1317ms)

  [firefox]
    First script
      ✔ First Selenium script (1025ms)


  2 passing (4s)

これはどこから辿ったのだっけ?こちらも参考になりそう。

developer.mozilla.org

この記事では、Selenium/WebDriver や selenium-webdriver for Node のようなテストライブラリーを使って、自動化環境のインストールとテストを実行する方法を教えます。

google_test.js

const webdriver = require('selenium-webdriver');
const By = webdriver.By;
const until = webdriver.until;

const driver = new webdriver.Builder()
  .forBrowser('chrome')
  .build();

driver.get('http://www.google.com');

driver.findElement(By.name('q')).sendKeys('webdriver');

driver.sleep(1000).then(() => {
  driver.findElement(By.name('q')).sendKeys(webdriver.Key.TAB);
});

driver.findElement(By.name('btnK')).click();

driver.sleep(2000).then(() => {
  driver.getTitle().then((title) => {
    if (title === 'webdriver - Google Search') {
      console.log('Test passed');
    } else {
      console.log('Test failed');
    }
    driver.quit();
  });
});

ローカルファイルを開くとき

driver.get('file:///Users/chrismills/git/learning-area/tools-testing/cross-browser-testing/accessibility/fake-div-buttons.html');

ロギング

selenium-webdriver/lib/logging.Logger

selenium/logging_test.js at trunk · SeleniumHQ/selenium · GitHub

Logging - ChromeDriver - WebDriver for Chrome

selenium-webdriver/lib/logging

パフォーマンステストに使いたいというのが1つの目的だったが、お勧めしません?

パフォーマンステスト | Selenium

通常、SeleniumとWebDriverを使用したパフォーマンステストはお勧めしません。

Selenium Webdriver ページの読み込み時間の計測 - Qiita

SeleniumのexecuteScriptを利用して、ページのwindowオブジェクトに直接アクセスして読み込み時間を取得した。windowオグジェクトは、Performanceに関するAPIを持っていて詳しくは以下を参照。

BiDirectional APIには、Performanceに関するドキュメントがなかったので、Chrome DevTools Protocolを使って

Chrome DevTools Protocol | Selenium

こんなサイトも

パフォーマンスの測定 - ウェブ開発を学ぶ | MDN

これは大注目(あとで読む)

ChromeDriver - WebDriver for Chrome - Performance Log

検証用にログを出力。キーワード「webdriver.logging」?

Ubuntu22にNode.jsをインストール

サイドバー?の[Ubuntu Software]から、[探す]-[Development]-[node]-[インストール]でインストール完了。

$ node --version
v18.12.0

nvmは公式サイトのインストール手順を参照。

github.com

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash
(略)
=> Appending nvm source string to /home/tokushima/.bashrc
=> Appending bash_completion source string to /home/tokushima/.bashrc
=> Close and reopen your terminal to start using nvm or run the following to use it now:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion
$ source ~/.bashrc
$ nvm --version
0.39.2

現在使用のバージョンを調べる

$ nvm current
system

利用可能なバージョンを一覧表示

$ nvm ls-remote
(略)
       v18.11.0
       v18.12.0   (Latest LTS: Hydrogen)
       v19.0.0

特定のバージョンのノードをインストール

$ nvm install 18.12.0
Downloading and installing node v18.12.0...
Downloading https://nodejs.org/dist/v18.12.0/node-v18.12.0-linux-x64.tar.xz...
######################################################################### 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v18.12.0 (npm v8.19.2)
Creating default alias: default -> 18.12.0 (-> v18.12.0)
$ nvm current
v18.12.0

インストール済みバージョンを表示

$ nvm list
->     v18.12.0
         system
default -> 18.12.0 (-> v18.12.0)
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v18.12.0) (default)
stable -> 18.12 (-> v18.12.0) (default)
lts/* -> lts/hydrogen (-> v18.12.0)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.24.1 (-> N/A)
lts/erbium -> v12.22.12 (-> N/A)
lts/fermium -> v14.21.0 (-> N/A)
lts/gallium -> v16.18.0 (-> N/A)
lts/hydrogen -> v18.12.0

使用するバージョンを切り替える

$ nvm use system
system
$ nvm use 18.12.0
Now using node v18.12.0 (npm v8.19.2)

VSCode リモートコンテナ機能

リモートSSH機能

拡張機能ビューから「Remote Development」をインストール。

Ctrl+Shift+Pから「Dev Containers: Add Dev Container Configuration File...」を入力するか、左下のリモート開発機能アイコンから「Add Dev Container Configuration File...」を選択。 一覧から適合した環境を選択する。

.devcontainerディレクトリが作成され、Dev Containerの設定ファイルdevcontainer.jsonと、Dockerfileが生成される。

フォルダを開くと、Dev Container内でワークスペースを開き直すかを確認するダイアログが表示される。「Reopen in Container」を選択すると、リモートコンテナ機能が有効になったVSCodeが再起動する。手動で行う場合は「Remote-Containers: Reopen Folder in Container」。