2019年1月31日

使用 nightwatch.js 進行 E2E 測試

這兩天來努力使用 nightwatch.js 對網站進行驗證,一些小心得先記錄一下好了。以下內容不一定是正規解法,有比較好的方式歡迎留言討論,謝謝。

nightwatch.js 目前還不支援 Data Driven Testing

整個場景是要針對已知的數筆資料(可能會增減)對網站系統進行登入及查驗的動作,這些資料被塞在一個 .txt 檔內,理想狀況是 nightwatch.js 打開該檔後依序跑指定的 test case。
但是它沒有迴圈的概念,所以永遠只跑第一筆記錄...
Google 後有人說可以用它的 mocha runner,但是... 看不懂啊... XD
沒關係,macOS 環境下有 shell,在外面跑迴圈把資料餵食給 nightwatch.js 總行了吧?

奇怪的命令列參數傳遞

待測試的資料是數字型的,直接丟給 nightwatch.js 竟然報錯...
node_modules/nightwatch/bin/nightwatch  tests/crawler.js 1234345678
   The "path" argument must be of type string. Received type number
       at validateString (internal/validators.js:125:11)
       at Object.resolve (path.js:1080:7)
最後傳參數的方式變成以下這種很奇怪的格式:
node_modules/nightwatch/bin/nightwatch  tests/crawler.js -ABCDEFG -12345678
在 nightwatch.js 內取得參數的方式倒是不難,就是濾掉 - 號以及檔案內可能參雜的 \r 或 \n 而以。
const args = process.argv.slice(3);
const param1 = args[0].replace( /^-/, '').replace( /\r/, '').replace( /\n/, '');
const param2 = args[1].replace( /^-/, '' ).replace( /\r/, '').replace( /\n/, '');

點選某一個 radiobutton

本來應該使用 .click() api 去點選頁面上的元件,但它馬的不會動啊... 最後透過 .execute() 搭配 document.querySelector() 方式總算點到了... = =
browser
    .execute(function(){
        document.querySelector( '#A > span > input[type="radio"]' ).click();
        document.querySelector( '#B > div.section_wrap > form > ul > li.ht > div.ipt_wrap_2 > label:nth-child(1) > span > input[type="radio"]' ).click();
    })

checkbox 還要檢查是不是可見元素

一樣還是透過 .execute() 及 document.querySelector() 取得 checkbox 本身,然後檢查它的 offsetParent 屬性是不是 null,若是 null 表示使用者看不到該 checkbox ,可以不用理它。
另外,別忘了檢查 checked 屬性,亂點可是會捅漏子的...
let box = document.querySelector( '#A input[type="checkbox"]' );
if( box.offsetParent != null && box.checked == false ) {
    box.click();
}

關於 elements() API

如果單純在 elements() 的 callback() 檢視它的傳回值時,會看到一組類似這樣的東西:

{ sessionId: '31bf7adb53f4f9a93a0c4404e41d303f',
  status: 0,
  value:
   [ { ELEMENT: '0.7637505561737095-2' },
     { ELEMENT: '0.7637505561737095-3' },
     { ELEMENT: '0.7637505561737095-4' } ] }
這似乎是 Selenium 內部用來管理 element 用的代號,可以透過 .elementIdXXXXXX() 之類的 API 取得真正的 element 的指定屬性(對,是那個指定屬性放在 .value 屬性內,不是傳回該 element ),所以可以這樣子捉出頁面上的資訊:
browser
    .elements( 'css selector', 'div.plan_items', function(result){
         result.value.forEach(element => {
            browser.elementIdAttribute(element.ELEMENT, 'id', function(result){
                console.log( result.value );
                planList.push( result.value.replace( /^plan_items_/, '' ) );
                });
            });
        })

結論

這些心得微小的不足為外人道,但它馬的花了我 2 天啊....
補個幹!