Nightwatch101 #7:指令 Part 1

Nightwatch.js

指令分為兩種-Nightwatch 提供的指令與開發者撰寫的客製化指令,以下介紹 Nightwatch 所提供的指令。程式碼可以打在 test/e2e/testDemo.js 並執行 nightwatch ./test/e2e/testDemo.js 來跑跑看喔!

這篇 Part 1 先來看一些 UI 操作相關的指令,例如元素定位、設值、點擊和檢視是否存在或可見等。

本系列文章皆使用這個專案,可以拉下來玩玩;有什麼問題都可以提出 issue

callback

callback 不是指令,它是參數。Nightwatch 的指令可傳入 callback 參數,讓開發者在事件完成後對回傳的 response 物件做些事情。範例如下,在 click 登入按鈕後,console 字串「click btn login」。

module.exports = {
  'Demo Test': browser => {
    browser
      .url('https://member.ruten.com.tw/user/login.htm')
      .waitForElementVisible('body', 1000)
      .click('#btnLogin', function(res) {
        console.log('click btn login')
      })
      .end();
  }
}

callback

url

打開指定網址。

browser.url('https://member.ruten.com.tw/user/login.htm') // 打開露天會員登入頁

urlHash

launch_url 上加上 hash,可為 #hashvaluehashvalue

module.exports = {
  'Demo Test': browser => {
    browser
      .urlHash('#hashvalue') // 或 .urlHash('hashvalue')
      .end();
  }
}

回顧launch_urllaunch_url 是預設要瀏覽的網址,在設定檔 nightwatch.config.js 可依據不同環境來個別設定。

useCss / useXpath

切換定位網頁元素的方式,選擇使用 CSS Selector 或 Xpath 來選取網頁元素。

範例程式碼如下。在這邊會看到混用 CSS Selector 和 Xpath Selector 來選取頁面元素。預設是使用 CSS Selector,如果要切換成 Xpath 要用 .useXpath(),再切回 CSS Selector 就要用 .useCss()

module.exports = {
  'Ruten Login': function (browser) {
    browser
      .url('https://member.ruten.com.tw/user/login.htm')
      .waitForElementVisible('body', 1000) // 預設使用 CSS Selector 選取網頁元素
      .useXpath() // 切換成 Xpath 選取網頁元素
      .setValue('//input[@id="userid"]', 'nightwatch101')
      .setValue('//input[@id="userpass"]', 'nightwatch101')
      .useCss() // 切換成 CSS Selector 選取網頁元素
      .click('#btnLogin')
      .pause(1000)
      .assert.urlContains('http://www.ruten.com.tw/')
      .end();
  }
};

完整範例

關於使用 CSS Selector 或 Xpath 定位網頁元素的說明和詳細範例可參考這篇那篇文章。

click

模擬點擊 DOM element 的動作,第一個參數是使用 CSS 或 Xpath Selector 選定的元素,第二個參數是 callback(optional)。這裡使用 webdriver 的 elementIdClick 協定指令。

範例如下,點擊 .button-submit

browser.click('.button-submit')

clearValue

用於清除 text input 或 textarea 的輸入値,第一個參數是使用 CSS 或 Xpath Selector 選定的元素,第二個參數是 callback(optional)。這裡使用 webdriver 的 elementIdValue 協定指令。

範例如下。

browser.clearValue('input[type=text]')

setValue

對某個元件鍵入字串,一般用來對 input text 設値或模擬連續打字,字串格式為 utf-8。第一個參數是使用 CSS 或 Xpath Selector 選定的元素,第二個參數是要鍵入字串,可為字串或陣列,第三個參數是 callback(optional)。

對 id 為 userid 和 userpassword 的 input text 分別鍵入字串 my_idmy_password,並按 enter 鍵送出。

browser
  .setValue('#userid', 'my_id')
  .setValue('#userpass', ['my_password', browser.Keys.ENTER])

getValue

取得表單元件目前的値。第一個參數使用 CSS 或 Xpath Selector 選定的元素,第二個參數是 callback(optional)。回傳物件,型別是字串。這裡使用 webdriver 的 elementIdValue 協定指令。

範例如下。

module.exports = {
  'Demo Test': browser => {
    browser
      .url('https://member.ruten.com.tw/user/login.htm')
      .setValue('#userid', 'Hello World')
      .getValue('#userid', function(result) {
        this.assert.equal(typeof result, 'object');
        this.assert.equal(result.status, 0);
        this.assert.equal(result.value, 'HiHi');
      })
      .end()
  }
}

執行結果。

getValue

getAttribute

對指定的 DOM element 取得屬性値。這裡使用 webdriver 的 elementIdAttribute 協定指令。第一個參數使用 CSS 或 Xpath Selector 選定的元素,第二個參數是屬性名稱,第三個參數是 callback(optional),回傳屬性値。

範例如下,對 #userid 檢視其 class 是否為 rt-user-info-input valid

module.exports = {
  'Demo Test': browser => {
    browser
      .url('https://member.ruten.com.tw/user/login.htm')
      .getAttribute('#userid', 'class', function(result) {
        this.assert.equal(typeof result, "object");
        this.assert.equal(result.status, 0);
        this.assert.equal(result.value, 'rt-user-info-input valid');
      })
      .end()
  }
}

getAttribute

getTagName

取得特定 DOM element 的 tag name。這裡使用 webdriver 的 elementIdName 協定指令。第一個參數使用 CSS 或 Xpath Selector 選定的元素 ,第二個參數是 callback(optional),回傳 tag name,型別是字串。

範例如下,對 #userid 檢視其 tag name 是否為 abc,而實際得到的卻是 input。

module.exports = {
  'Demo Test': browser => {
    browser
      .url('https://member.ruten.com.tw/user/login.htm')
      .getTagName('#userid', function(result) {
        this.assert.equal(result.value, 'abc');
      })
      .end()
  }
}

getTagName

getText

取得特定 DOM element 的可見文字。這裡使用 webdriver 的 elementIdText 協定指令。第一個參數是使用 CSS 或 Xpath Selector 選定的元素 ,第二個參數是 callback(optional),回傳文字,型別是字串。

範例如下,對元素 .rt-button-fb-login 取得文字,並檢視是否等於 abc,而實際得到的卻是「Facebook 登入」。

module.exports = {
  'Demo Test': browser => {
    browser
      .url('https://member.ruten.com.tw/user/login.htm')
      .getText('.rt-button-fb-login', result => {
        browser.assert.equal(result.value, 'abc')
      })
      .end()
  }
}

getText

getTitle

取得頁面 <title> 的文字。這裡使用 webdriver 的 title 協定指令。沒有輸入參數,回傳字串。

範例如下,取得標題,並比對資料型別是否為字串、標題文字是否為「Nightwatch.js」。

browser
  .url('https://member.ruten.com.tw/user/login.htm')
  .getTitle(function(title) {
    this.assert.equal(typeof title, 'string');
    this.assert.equal(title, 'Nightwatch.js');
  });

getCssProperty

對特定 DOM element 取得 CSS 屬性的值。這裡使用 webdriver 的 elementIdCssProperty 協定指令。第一個參數是使用 CSS 或 Xpath Selector 選定的元素,第二個參數是 CSS 屬性名稱,第三個參數是 callback(optional),回傳屬性値。

取得元素 #search_input 的 CSS line-height 的值,並比對其值是否為「27px」。

module.exports = {
  'Demo Test': browser => {
    browser
      .url('http://class.ruten.com.tw/category/sub00.php?c=00080001')
      .getCssProperty("#search_input", "line-height", function(result) {
        this.assert.equal(typeof result, "object");
        this.assert.equal(result.status, 0);
        this.assert.equal(result.value, '27px');
      })
      .end()
  }
}

getCssProperty

getElementSize

取得 DOM element 的大小。這裡使用 webdriver 的 elementIdSize 協定指令。第一個參數是使用 CSS 或 Xpath Selector 選定的元素 ,第二個參數是 callback(optional),回傳一個物件 { width: number, height: number },包含寬和高,單位是 pixel。

如下,打開網頁找到「Facebook 登入」這顆按鈕,來看看它的寬高是多少。

getElementSize

module.exports = {
  'Demo Test': browser => {
    browser
      .url('https://member.ruten.com.tw/user/login.htm')
      .getElementSize('.rt-button-fb-login', result => {
        browser.assert.equal(typeof result, 'object');
        browser.assert.equal(result.status, 0);
        browser.assert.equal(result.value.width, 500);
        browser.assert.equal(result.value.height, 20);
      })
      .end()
  }
}

預期寬度是 500px,得到 124px。 預期高度是 20px,得到 31px。

getElementSize

isVisible

確認指定的 DOM element 在畫面上是否是可見的。這裡使用 webdriver 的 elementIdDisplayed 協定指令。第一個參數是使用 CSS 或 Xpath Selector 選定的元素,第二個參數是 callback(optional),回傳一個布林值,表示是否可見。

打開露天首頁,檢視 .rt-header-not-loaded 這個 DOM element 是否可見。由於在尚未登入的狀態,因此是可見的。可見的定義是 display: blockdisplay: inline-block 或 opacity 不為 0 等。

module.exports = {
  'Demo Test': browser => {
    browser
      .url('http://www.ruten.com.tw/')
      .isVisible('.rt-header-not-loaded', result => {
        browser.assert.equal(typeof result, 'object');
        browser.assert.equal(result.status, 0);
        browser.assert.equal(result.value, true);
      })
      .end()
  }
}

isVisible

waitForElementPresent

確認 DOM Element 載入完成。等待一段時間,來檢視元素是否存在,使用者不一定會肉眼能看到,但可在 HTML 裡面找到。假設在等待指定時間後,檢視該元素但不存在,測試就會失敗。但如果不希望測試失敗,可設定 abortOnFailure 為 false,這樣測試就會繼續下去。

參數

browser
  .url('http://www.ruten.com.tw/')
  .waitForElementPresent('body', 1000, false, function() {
    // do something...
  }, 'element %s presents in %d ms')
  .end()

waitForElementPresent

waitForConditionTimeout

可使用 nightwatch.config.js 的 waitForConditionTimeout 屬性來改變預設的 polling interval,單位是 ms。

waitForConditionTimeout

waitForElementNotPresent

與上面的 waitForElementPresent 相反。

waitForElementVisible

在執行相關動作前,等待指定的一段時間,來檢視元素是否可見。與 waitForElementPresent 類似。

waitForElementPresent vs waitForElementVisible

兩者差異在於waitForElementPresent 是指元素存在於 HTML,但 waitForElementVisible 是使用者肉眼可見。可參考說明

waitForElementNotVisible

與上面的 waitForElementVisible 相反。

moveToElement

移動滑鼠到指定的 DOM element。這裡使用 webdriver 的 moveTo 協定指令。第一個參數是選項與偏好設定 使用 CSS 或 Xpath Selector 選定的元素,第二個參數是 xoffset,第三個參數是 yoffset,第四個參數是 callback(optional)。

範例如下,移動到元素 .category-menu 右上方皆距離 10px 的位置。

browser
  .url('http://www.ruten.com.tw/')
  .moveToElement('.category-menu', 10, 10);
  .end()

範例

這裡為以上介紹的指令準備了一個綜合範例,來實際跑跑看。

nightwatch test/e2e/class/testSubCategory.js

執行結果。

Nightwatch101 #7:指令 Part 1

完整範例程式碼

測試報告。

Nightwatch101 #7:指令 Part 1


今天看完 UI 操作相關的指令,明天來看 Cookie、Window 和 Log 相關的指令吧-點此


2018 鐵人賽網址


comments powered by Disqus