自動化測試 x Puppeteer - 玩偶QA參一咖 Day03


今日任務: 登入服務

在進入本日主題前,先和大家對照目前的稍微調整過的程式碼:

從這一篇文章開始,在參考程式碼上將會開始使用 Day X 來表示這是第X天的內容,而其子層級則是用大寫英文字母A, B, C, … 來區分執行的動作,目的是為了增加程式碼的可讀性,這樣也比較容易說明XD。雖然在目前的程式碼可能沒有說一定要這樣做,不過隨著我們繼續走下去,我們會寫下越來越多程式碼,而且也會遇到一些id和class長得比較特別的元素(比較特別指的是它們不像是 id = Login_Navibar 這種人類讀起來可以知道它是什麼意思的文字串),所以我們從這裡就建立一套寫程式的註解邏輯吧!


在今天的文章裡頭,我們大致上會繼續Follow下面這兩個流程

  1. 找到目標操作元素的 select 依據
  2. 對其進行相關的動作

除了會繼續用到昨天提到的 click 以外,在今天,針對專門用來讓使用者輸入資料的元素,我們將會使用到 type 的函式來與之搭配。


動作A:輸入信箱

為了要看看元素的 select 依據,先在 Command Prompt/ Terminal 執行

$ node puppet.js

並且等待瀏覽器來到這個畫面

我們打開 inspector (F12 or 右鍵+inpect),並找到 Email 欄位的資訊

發現它是個 input 元素,然後 class = input100, id = member_email,我們選用 id 做為我們的 select 依據,搭配前面提過的 type 函式,我們在A區塊內寫下

await page.type("#member_email", "Your_Email_Account@gmail.com")`

第一個要給type的參數是一個id,所以最前面有一個"#";
而第二個參數則是要提供希望輸入的文字,在此即為信箱。

結果情況卻不如預期,在 Command Prompt/ Terminal 中出現了這樣的錯誤:

其實有很多種可能性會導致這件事情的發生,在這一系列文章當中我們應該會遇到兩三個不一樣的可能原因。在這裡我們仔細分析一下它顯示的Error Message: No node found for selector. 而問題應該不會是因為我們的id打錯字,所以基本上漏洞不在 Day 3的A區塊裡面,而是在 Day 2的區塊裡面!

原來,在昨天我們把跳轉頁面想的過於單純,寫出來的程式碼實際上只是單純地按按鈕而已,在 Puppeteer的技術文件當中,特別有針對按鈕觸發網頁跳轉的事件做說明(page.clickpage.waitForNavigation)

我們實際上是需要在click下方多加一行程式碼

await page.waitForNavigation()

我的理解(不一定完全正確)是這一行是用來讓網頁跳轉到下一個頁面時(完成Navigation之後),才Return Promise 給當前的頁面(也就是新的頁面),這樣一來,page元素才會更新到新的的頁面,而不是停留在Navigation前的那個頁面。
我們在上方會遇到Error應該是因為我們的page元素還停留在Navigation前的頁面,所以在這樣的情況下page當然沒有 id 是 "member_email" 的 input 標籤

修正後,再次於Command Prompt/ Terminal 執行

$ node puppet.js

這次就能夠順利的執行了

在往下走之前,我們稍微修正一下目前的程式碼。在上方提供的Puppeteer技術文件中,它其實是建議我們這樣寫

await Promise.all([
    page.waitForNavigation(), // The promise resolves after navigation has finished
    page.click("#Login_Navibar") // Clicking the link will indirectly cause a navigation
]);

如果用了 Promise.all([action1, action2]);
那麼它就會等待action1, action2 都有做完事情並且回傳promise之後才會繼續下一個動作,針對按鈕觸發的頁面跳轉事件,在技術文件中是建議用Promise.all把動作都包起來,比較保險。(詳情參考上方提供的兩個連結)


動作B: 點擊Next按鈕

完成了動作A:輸入Email後,我們要做的下一件事情就是點擊Next按鈕,而關於點擊按鈕我們已經有經驗了,這次我們就做快一點吧!

  1. Inpsect Next按鈕 發現它的class 是 "login100-form-btn.form-step-1"
  2. 所以在動作B的區塊寫下
    await page.click(".login100-form-btn.form-step-1")
    

再次執行,輸入的信箱如果是正確的,password欄位就會跳出來了,所以我們可以進入動作C了!


動作C: 輸入密碼

這裡操作流程原則上和動作A一樣,所以我們也可以快一些呦。

  1. Inspect password 欄位,發現它的 id 是 member_password
  2. 所以在動作C的區塊寫下
    await page.type("#member_password","Your_Secret_Password")
    

然後我們執行看看,結果發現member_password是沒有反應,而且也沒有任何的錯誤訊息產生。
雖然我至今沒有找到其最根本的錯誤原因(推測是因為在這個元素顯示出來之前輸入的東西是無效的,在看到password欄位出現之前還是可以inspect到它,它原本是沒有顯示的),總之,在嘗試了一些方法後發現可以利用waitForSelector去等待元素顯示出來,我們再來使用type的方法。

所以我們在type上面新增等待元素顯示的函式

await waitForSelector("#member_password", {visible: true})

我們再來執行一次,這次就得到預期的結果了

最後,我們只要點擊Next按鈕,今天的任務就完成了!


動作D: 點擊Next按鈕

  1. Inspect Next按鈕,發現它的 id 是 "btn-ready-send"
  2. 所以在 動作D 的區塊內寫下
    await page.click("#btn-ready-send")
    

大家是不是都有跳轉到這個頁面呢?

不過! 這樣會有漏洞,要記得今天最一開始出現的問題(參考動作A),不要忘記 puppeteer 的技術文件提醒我們在按鈕觸發頁面跳轉的事件當中,我們要搭配使用waitForNavigation,所以我們把

await page.click(#btn-ready-send)

改成

await Promise.all([
    page.click("#btn-ready-send"),
    Page.waitForNavigation()
]);

做完修正之後,並再次執行程式,它一樣會到上面那張圖片所顯示的頁面,而且我們在下一篇的操作就不會再遇到相同的問題囉!

目前的程式碼是: 預計從下次開始程式碼的截圖就只能包含最新兩個Day的內容了


一樣,我們在這篇文章的小小總結如下:

  1. 如果按鈕觸發頁面跳轉有其推薦的撰寫方式(建議參考Puppeteer的技術文件) https://github.com/puppeteer/puppeteer/blob/v2.1.0/docs/api.md#pageclickselector-options
    https://github.com/puppeteer/puppeteer/blob/v2.1.0/docs/api.md#pagewaitfornavigationoptions

  2. 認識了一個Error Message: 動作A出現了 No node found for selector,原因是因為需要waitForNavigation

  3. 待釐清的地方(歡迎下方留言討論,謝謝大家)
    a. 對於waitForNavigation的理解
    b. 動作C: type沒有看起來沒有反應的原因

下回預告: 來到主頁面,而我們將會開始自我簽署的服務。

#Puppeteer #WaitForNavigation







你可能感興趣的文章

MTR04_0621

MTR04_0621

[day 07] Symbol & Proxy: 以前沒有的

[day 07] Symbol & Proxy: 以前沒有的

關於 React 小書:渲染陣列裡的各個項目

關於 React 小書:渲染陣列裡的各個項目






留言討論