概述

大多數情況下,您需要擴充 Nightwatch 命令以符合您自己的應用程式需求。若要執行此操作,請建立新的資料夾 (例如 nightwatch/commands),並開始在其中定義您自己的命令,每個命令都放在自己的檔案中。

然後在 nightwatch.json 檔案中,將該資料夾的路徑指定為 custom_commands_path 屬性。

nightwatch.json
{
  "custom_commands_path" : "nightwatch/commands"
}

命令名稱是檔案本身的名稱。

定義自訂命令

您可以透過兩種主要方式定義自訂命令

1) 類別樣式命令

這是撰寫自訂命令的建議樣式,同時也是大多數 Nightwatch 本身命令的撰寫方式。您的命令模組需要匯出一個類別建構函式,其中包含一個 command 實例方法來表示命令函式。

所有 Nightwatch 命令都是非同步的,這表示自訂命令必須發出完成訊號 (在 command 方法中)。這可以透過兩種方式達成

  1. 傳回 Promise
  2. 發出 "complete" 事件 (在此情況下,類別需要繼承自 Node 的 EventEmitter)

建議的方式是傳回 Promise。基於類別的 command 方法會在類別實例的內容中執行 (this 的值)。browser 物件可透過 this.api 取得。

以下範例會執行與 .pause() 命令相同的功能。請注意有關完成的兩種變化。

傳回值

您也可以指定傳回值,作為 Promise 將解析的引數,或作為呼叫 "complete" 事件的引數。

透過 Promise 完成
nightwatch/commands/customPause.js
module.exports = class CustomPause {
  command(ms, cb) {
    // If we don't pass the milliseconds, the client will
    // be suspended indefinitely
    if (!ms) {
      return;
    }
    
const returnValue = { value: 'something' };
return new Promise((resolve) => { setTimeout(() => { // if we have a callback, call it right before the complete event if (cb) { cb.call(this.api); }
resolve(returnValue); }, ms); }); } }

command 方法也可以是 async。在這種情況下,您只需要傳回一個值,因為 async 方法已經會傳回 Promise。

以下是另一個範例

nightwatch/commands/customCommand.js
module.exports = class CustomCommand {
  async command() {
    let returnValue;
    try {
      returnValue = await anotherAsyncFunction();
    } catch (err) {
      console.error('An error occurred', err);
      returnValue = {
        status: -1,
        error: err.message
      }
    }
    
return returnValue; } }
透過 "complete" 事件完成
nightwatch/commands/customPause.js
const Events = require('events');

module.exports = class CustomPause extends Events { command(ms, cb) { // If we don't pass the milliseconds, the client will // be suspended indefinitely if (!ms) { return; }
const returnValue = { value: 'something' };
setTimeout(() => { // if we have a callback, call it right before the complete event if (cb) { cb.call(this.api); }
// This also works: this.complete(returnValue) this.emit('complete', returnValue); }, ms); } }

使用 Nightwatch 通訊協定動作

v1.4 起,您也可以直接使用 Nightwatch 用於其內建 API 的通訊協定動作 (透過 this.transportActions)。這些是到 Selenium JsonWireW3C Webdriver 通訊協定端點的直接 HTTP 對應,取決於目前使用的是哪一個。

以下是一個範例

nightwatch/commands/customCommand.js
module.exports = class CustomCommand {
  async command() {
    let returnValue;
    
// list all the avaialble transport actions // console.log(this.transportActions);
try { returnValue = await this.transportActions.getCurrentUrl(); } catch (err) { console.error('An error occurred', err); returnValue = { status: -1, error: err.message } }
return returnValue; } }

直接呼叫 Selenium/Webdriver 端點

此外,自 v1.4 起,您可以 (透過 this.httpRequest(options)) 從自訂命令直接呼叫 Selenium/Webdriver 伺服器上可用的 HTTP 端點。這可能是一種擴充提供之 API 通訊協定的便捷方式,因為它使用與其他通訊協定動作相同的 HTTP 請求介面。

當使用提供額外端點的服務時,例如 Appium 時,這特別有用。

以下是一個範例

nightwatch/commands/customCommand.js
module.exports = class CustomCommand {
  async command() {
    let returnValue;
    
try { returnValue = await this.httpRequest({ // the pathname of the endpoint to call path: '/session/:sessionId/url',
// the current Selenium/Webdriver sessionId sessionId: this.api.sessionId,
// host and port are normally not necessary, since it is the current Webdriver hostname/port //host: '', //port: '',
// the body of the request data: { url: 'https://127.0.0.1/test_url' },
method: 'POST' }); } catch (err) { console.error('An error occurred', err); returnValue = { status: -1, error: err.message } }
return returnValue; } }

2. 函式樣式命令

這是一種較簡單的命令定義方式,但它們也相當有限。

命令模組需要匯出一個 command 函式,該函式至少需要呼叫一個 Nightwatch api 方法 (例如 .execute())。這是由於命令非同步佇列系統的工作方式限制。您也可以將所有內容包裝在 .perform() 呼叫中。用戶端命令 (例如 executeperform) 可透過 this 取得。

nightwatch/commands/customCommand.js
module.exports.command = function(file, callback) {
  var self = this;
  var imageData;
  var fs = require('fs');
  
try { var originalData = fs.readFileSync(file); var base64Image = new Buffer(originalData, 'binary').toString('base64'); imageData = 'data:image/jpeg;base64,' + base64Image; } catch (err) { console.log(err); throw "Unable to open file: " + file; }
this.execute(function(data) { // execute application specific code App.resizePicture(data); return true; }, [imageData], // arguments array to be passed function(result) { if (typeof callback === "function") { callback.call(self, result); } });
return this; };

上述範例定義了一個命令 (例如 resizePicture.js),該命令會將影像檔案載入為 data-URI,並呼叫應用程式內部定義的名稱為 resizePicture 的方法 (透過 .execute())。

透過這個命令,測試看起來會像這樣

tests/sampleTest.js
module.exports = {
  "testing resize picture" : function (browser) {
    browser
      .url("http://app.host")
      .waitForElementVisible("body")
      .resizePicture("../../../var/www/pics/moon.jpg")
      .assert.element(".container .picture-large")
      .end();
  }
};

使用 async/await

您也可以在函式樣式自訂命令內使用 ES6 async/await 語法。以下是另一個自訂命令範例

module.exports = {
  command: async function () {
    this.url('https://nightwatch.dev.org.tw');
    this.waitForElementVisible('section#index-container');
    
const result = await this.elements('css selector', '#index-container ul.features li'); this.assert.strictEqual(result.value.length, 7, 'Feature elements number is correct'); } };