Nightwatch の次期メジャーバージョンがアルファプレリリースとして NPM で利用可能 になったことを発表いたします。このバージョンには、テストの作成と実行のための幅広い新機能と改良、そして W3C WebDriver 準拠ブラウザの完全なクロスブラウザテストサポートが含まれています。

基盤となるアーキテクチャは、ブラウザドライバとの通信に公式の selenium-webdriver Node.js ライブラリを使用するように完全に書き直されました。これにより、クロスブラウザ統合の向上、DOM要素コマンド処理の信頼性向上、そして全体的なテストの安定性と速度の向上が実現します。

この *アルファ* リリースの目的は、フィードバックを収集し、主要なバグを特定して修正すると同時に、新しい API を完成させ、ドキュメントを更新することです。そのため、発生した主要な問題を報告していただき、v1.x からのアップグレードを可能な限りスムーズに行えるようにすることが非常に重要です。この投稿の最後に記載されているように、いくつかの破壊的な変更点がありますが、それらは比較的小規模なものになるはずです。

また、既存の v1.7 バージョンへのパッチと重要な修正の発行も継続します。v2.0 の新機能、改良点、その他の変更点の概要を以下に示します。

アルファ版をインストールするには、以下を実行してください

npm i nightwatch@alpha

WebDriver Actions API のサポート

WebDriver は、Actions API と呼ばれる複雑なユーザー操作を生成するための包括的な API を提供しています。これは、Nightwatch の既存の .perform() コマンドを介して利用できます。 perform() コマンドの以前の機能は引き続き存在し、以前と同じ方法で動作します。

新しい Actions API の使用方法の基本的な例を紹介します


try {
  const performResult = await browser.perform(function() {
    const actions = this.actions({async: true});

    return actions
       .keyDown(Key.SHIFT)
       .keyUp(Key.SHIFT);
  });

  console.log('perform', performResult)
} catch (err) {
  console.error(err)
}

Selenium のドキュメント Web サイトにある API ドキュメント には、さらに多くの例が掲載されています。上記の例では、Actions クラスのインスタンスは this.actions(<options>) を使用して作成されます。Selenium のドキュメントで必要な最後の .perform() は、Nightwatch では自動的に呼び出されるため省略する必要があります。

Chrome DevTools プロトコルのサポート

ChromeDriverEdgeDriver はどちらも、それぞれのブラウザを操作するための一部の特定のコマンドを公開しています。

ChromeDriver または EdgeDriver を使用する場合、Chrome DevTools プロトコル を介してコマンドを実行できるようになりました。 Nightwatch の browser オブジェクトの chrome 名前空間で使用可能なコマンドの完全なリストを以下に示します

browser.chrome

詳細情報

Firefox 固有の新しいコマンド

FirefoxDriver は、「特権」JavaScript コードを実行するためのコンテキストを設定したり、アドオンを操作したりするためなど、いくつかの特定のコマンドを公開しています。 これらは、Nightwatch で firefox 名前空間で直接使用できるようになりました。

browser.firefox

詳細情報

新しい .ensure アサーション

新しい .ensure 名前空間は、until モジュール (selenium-webdriver から) に基づいています。


describe('demo test for .ensure', function() {
  test('basic test', function(browser) {
    browser
      .url('https://nightwatch.dokyumento.jp')
      .ensure.titleMatches(/Nightwatch.js/)
      .ensure.elementIsVisible('#index-container')  });
});

新しい element() グローバルと WebElements の使用のサポート

新しく追加された `element()` グローバルを使用して、テストケースの外部で要素オブジェクトを事前に構築できます。 また、新しく追加された `by()` グローバルユーティリティを使用することもできます。これは、selenium-webdriver の `By()` クラスを使用して要素ロケータを作成することと同じです。

さらに、`browser` オブジェクトもグローバルとして使用できるため、Nightwatch v1.x のようにテストに引数として渡す必要はありません。

nightwatch 設定ファイルで `disable_global_apis` を `true` に設定することにより、グローバル API を無効にすることもできます。


const assert = require('assert');
const {WebElement} = require('selenium-webdriver');

describe('demo element() global', function() {
  const signupEl = element(by.css('#signupSection'));
  const loginEl = element('#weblogin');

  test('element globals command',  async function() {
    const tagName = await browser.waitForElementPresent(loginEl, 100).getTagName(loginEl);
    assert.strictEqual(tagName, 'div');

    // use elements created with element() to regular nightwatch assertions
    browser.assert.visible(loginEl);

    // use elements created with element() to expect assertions
    browser.expect.element(loginEl).to.be.visible;

    // retrieve the WebElement instance
    const loginWebElement = await loginEl.getWebElement();
    assert.ok(loginWebElement instanceof WebElement);
  });
});

Selenium WebDriver オブジェクトの直接使用

`WebDriver` インスタンスは、Nightwatch api オブジェクトの `driver` プロパティとしても使用できます。

WebDriver 固有のコマンドをチェーンしたい場合は、それらを `perform()` または `waitUntil()` コマンドでラップする必要があります。


describe('demo driver object', function() {

  it('get browser logs – classic',  function() {
    browser
      .url('https://nightwatch.dokyumento.jp')
      .waitForElementVisible('body')
      .perform(function() {
        this.driver.manage().logs().get('browser').then(result => {
          console.log('Browser logs:', result)
        })
      });
  });

  it('get browser logs – with ES6 async/await', async function() {
    await browser.url('https://nightwatch.dokyumento.jp').waitForElementVisible('body');
    const logs = await browser.driver.manage().logs().get('browser');

    console.log('Browser logs:', logs)
  });
});

Nightwatch での WebDriver BiDi の使用

Selenium WebDriver 上に構築されているということは、WebDriver の最新機能と機能 (今後の Webdriver BiDi プロトコルなど) が Nightwatch で直接利用できることを意味します。「クロスブラウザ自動化の未来」と見なされています。

WebDriver BiDi は、ブラウザと通信するための新しいプロトコルであり、現在進行中の新しい W3C 仕様 として定義されています。

Selenium 4 で初期サポートが利用可能であり、Chrome Developer Tools を介して ChromeDriver ですでに利用可能です。

WebDriver Bidi を使用すると、WebDriver が他の API に使用している従来のリクエスト/レスポンスのアプローチを使用するのではなく、発生したときにブラウザからイベントをキャプチャできます。

WebDriver は内部で、イベントとコマンドを送信するためにブラウザへの WebSocket 接続を作成します。

以下の例では、WebSocket 経由の WebDriver 双方向接続を介して CDP から `Page.getLayoutMetrics` メソッドを呼び出します。


describe('demo webdriver bidirectional', function() {

  it('samepl test bidi', async function(browser) {
    await browser.url('https://nightwatch.dokyumento.jp/');

    const cdpConnection = await browser.driver.createCDPConnection('page');
    browser.assert.ok(cdpConnection._wsConnection && cdpConnection._wsConnection._url.startsWith('ws://'),
            CDP connection is successful to: ${cdpConnection._wsConnection._url});

    const layoutMetrics = await browser.perform(function(callback) {
      const id = 99;
      cdpConnection._wsConnection.on('message', function getLayoutMetrics(message) {
        const params = JSON.parse(message)
        if (params.id === 99) {
          cdpConnection._wsConnection.off('message', getLayoutMetrics);
          callback(params.result);
        }
      });

      cdpConnection.execute('Page.getLayoutMetrics', id, {});
    });

    console.log('Layout Metrics:', layoutMetrics)
  });
});

新しい API コマンド

いくつかの新しいコマンドが追加され、いくつかの既存のコマンドの互換性も向上しました。

browser.getAccessibleName(<selector> | <WebElement>)

要素の計算された WAI-ARIA ラベルを返します。

 const result = await browser.getAccessibleName('input[type=search]');

browser.getAriaRole(<selector> | <WebElement>)

要素の計算された WAI-ARIA ロールを返します。

 const result = await browser.getAriaRole('input[type=search]');
 

browser.takeElementScreenshot(<selector> | <WebElement>)

要素の境界矩形に囲まれた表示領域のスクリーンショットを撮ります。

 const data = await browser.takeElementScreenshot('#container');
 require('fs').writeFile('out.png', data, 'base64');
 

browser.uploadFile(<selector> | <WebElement>)

絶対ファイルパスを使用して要素にファイルをアップロードします。

 await browser.uploadFile('#myFile', '/path/to/file.pdf');
 

browser.waitUntil(<conditionFunction>, [optionalMaxTimeout], [optionalRetryInterval], [optionalCallback])

テストランナーが条件が「truthy」値に評価されるのを待機させることができる汎用コマンド。条件は、評価される値を返す任意の関数、または待機する Promise によって指定できます。条件が満たされない場合、`TimeoutError` がスローされ、テストは失敗します。

 let conditionValue;
 await browser.waitUntil(function() {
    return conditionValue === true;
 });

 await browser.waitUntil(async function() {
   const title = await this.execute(function() {
      return document.title;
   });
   return title === 'Nightwatch.js';
 }, 1000);

async/await の使用のサポートの向上

`await` 演算子を使用する場合、Nightwatch コマンドの結果形式を変更して、値を直接返すようにしました。

コールバックに渡される値は v1.x と同じです。この動作は、nightwatch 設定で `backwards_compatibility_mode` を `true` に設定することで無効にできます。

`await` を使用する場合の値の取得


const value = await browser.getText('#weblogin');
console.log('Value is:', value);

コールバックを使用する場合の値の取得


browser.getText('#weblogin', function(result) {
  console.log('Value is:', result.value);});

WebDriver 機能を定義するためのより多くの方法

nightwatch.conf.js ファイルで Selenium Capabilities オブジェクトのインスタンスを `capabilities` 値として設定することにより、セッション機能を定義できるようになりました。

利用可能なすべての機能については、Selenium のドキュメントを参照できます。 `nightwatch.conf.js` でヘッドレスモードの Chrome の `capabilities` オブジェクトを定義する例を以下に示します


// nightwatch.conf.js
const chrome = require('selenium-webdriver/chrome');
const capabilities = new chrome.Options();
capabilities.headless();

module.exports = {
  test_settings: {
    chrome: {
      capabilities,
      webdriver: {
        start_process: true,
        server_path: require('chromedriver').path,
        cli_args: [
          // --verbose
        ]
      }
    }
  }
};

新しい設定

以下は、v2.0 で導入された新しい設定とそのデフォルト値です

module.exports = {
  // Set this to true to use the v1.x response format for commands when using ES6 async/await

  backwards_compatibility_mode: false,

  // Set this to true to disable the global objects such as element(), browser, by(), expect()
  disable_global_apis: false,

  // Ignore network errors (e.g. ECONNRESET errors)

  report_network_errors: true,

  // Interactive element commands such as "click" or "setValue" can be retried if an error occurred (such as an "element not interactable" error)
  element_command_retries: 2,

  // Sets the initial window size, defined as an object with "width" and "height" numerical properties
  window_size: null

}

新しい WebDriver 設定

以下は、v2.0 で導入されたさまざまなブラウザドライバの新しい `webdriver` 設定です

module.exports = {
  webdriver: {
    // Sets the path to the Chrome binary to use. On Mac OS X, this path should reference the actual Chrome executable, not just the application binary (e.g. "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome").
    chrome_binary: '',

    // Sets the path to Chrome's log file. This path should exist on the machine that will launch Chrome.
    chrome_log_file: '',

    // Configures the ChromeDriver to launch Chrome on Android via adb.
    android_chrome: false,

    // Sets the path to the Edge binary to use.

    edge_binary: '',

    // Sets the path to the Edge binary to use.
    edge_log_file: '',

    // Sets the binary to use. The binary may be specified as the path to a Firefox executable or a desired release Channel.
    firefox_binary: '',

    // Sets the path to an existing profile to use as a template for new browser sessions. This profile will be copied for each new session - changes will not be applied to the profile itself.
    firefox_profile: ''
  }
}

破壊的変更

破壊的変更の量を最小限に抑えるように努めましたが、避けられないものもありました。すでに非推奨の機能も削除されました。

概要は次のとおりです。 1.5 以降のバージョンからアップグレードした後に他に動作しないものがある場合は、Github でお知らせください。

  • ES6 async/await テストケースを使用する場合、Nightwatch コマンドの結果値には `status` および `value` プロパティは含まれず、値のみが含まれます (これは、nightwatch 設定で `backwards_compatibility_mode` を `true` に設定することで元に戻すことができます)
  • `setValue` は、キーストロークを送信する前に値をクリアするようになりました
  • `sendKeys` は `setValue` のエイリアスではなくなりました。値をクリアするのではなく、単にキーを送信するためです

要素の位置特定エラーの場合の結果オブジェクトの変更

  • Error オブジェクトインスタンスである `error` プロパティが含まれています
  • `httpStatusCode` プロパティは含まれなくなりました
  • `value` プロパティは含まれなくなりました
  • 依存関係の問題を引き起こすことが多いため、`proxy-agent` を依存関係として削除しました。 proxy-agent パッケージは NPM から個別にインストールして、同じ方法で使用できます。

フィードバック

バグは Github Issues に、一般的なフィードバックは Discussions ページに送信してください。ご協力をお願いいたします!