Nightwatch101 #7:指令 Part 1
17 Dec 2017指令分為兩種-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();
},
};
url
打開指定網址。
browser.url('https://member.ruten.com.tw/user/login.htm'); // 打開露天會員登入頁
urlHash
在 launch_url
上加上 hash,可為 #hashvalue
或 hashvalue
。
module.exports = {
'Demo Test': (browser) => {
browser
.urlHash('#hashvalue') // 或 .urlHash('hashvalue')
.end();
},
};
回顧launch_url
。launch_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_id
和 my_password
,並按 enter 鍵送出。
browser
.setValue('#userid', 'my_id')
.setValue('#userpass', ['my_password', browser.Keys.ENTER])
getValue
取得表單元件目前的値。第一個參數使用 CSS 或 Xpath Selector 選定的元素,第二個參數是 callback(optional)。回傳物件,型別是字串。這裡使用 webdriver 的 elementIdValue 協定指令。
範例如下。
- 打開露天登入網頁。
- 將帳號
#userid
這個 input 鍵入「Hello World」。 - 對帳號
#userid
這個 input 取値,在 callback 中做一些確認…- 檢視回傳 result 是否為物件(O)
- 檢視回傳 result 的 status 是否為 0(O)
- 檢視回傳 result 的 value 是否為「HiHi」(X,應為 Hello World)。
- 結束 Session,關閉瀏覽器。
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();
},
};
執行結果。
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();
},
};
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();
},
};
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();
},
};
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();
},
};
getElementSize
取得 DOM element 的大小。這裡使用 webdriver 的 elementIdSize 協定指令。第一個參數是使用 CSS 或 Xpath Selector 選定的元素
,第二個參數是 callback(optional),回傳一個物件 { width: number, height: number }
,包含寬和高,單位是 pixel。
如下,打開網頁找到「Facebook 登入」這顆按鈕,來看看它的寬高是多少。
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。
isVisible
確認指定的 DOM element 在畫面上是否是可見的。這裡使用 webdriver 的 elementIdDisplayed 協定指令。第一個參數是使用 CSS 或 Xpath Selector 選定的元素,第二個參數是 callback(optional),回傳一個布林值,表示是否可見。
打開露天首頁,檢視 .rt-header-not-loaded
這個 DOM element 是否可見。由於在尚未登入的狀態,因此是可見的。可見的定義是 display: block
、display: 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();
},
};
waitForElementPresent
確認 DOM Element 載入完成。等待一段時間,來檢視元素是否存在,使用者不一定會肉眼能看到,但可在 HTML 裡面找到。假設在等待指定時間後,檢視該元素但不存在,測試就會失敗。但如果不希望測試失敗,可設定 abortOnFailure 為 false,這樣測試就會繼續下去。
參數
- 使用 CSS 或 Xpath Selector 選定的元素。
- time:等待時間 (ms),nightwatch test runner 會每 0.5 秒重複確認一次。
- abortOnFailure(optional)。
- callback(optional)。
- message(optional):報錯,%s 表示元素,%d 表是時間。
browser
.url('http://www.ruten.com.tw/')
.waitForElementPresent('body', 1000, false, function() {
// do something...
}, 'element %s presents in %d ms')
.end()
waitForConditionTimeout
可使用 nightwatch.config.js 的 waitForConditionTimeout 屬性來改變預設的 polling interval,單位是 ms。
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
執行結果。
測試報告。
今天看完 UI 操作相關的指令,明天來看 Cookie、Window 和 Log 相關的指令吧-點此。