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


今日任務: 上傳指定檔案

前言,我們在昨天有稍微岔開話題去講在不同分頁的操作原則,不過在未來的操作過程中,因為分頁的操作不是必須的,所以我們先將它們註解掉,

今天來到自己簽署的重頭戲,我們將要透過Puppeteer提供的函式將,本機的檔案(PDF檔)上傳檔案,我第一次使用Puppeteer的時候,在做到上傳檔案之前都一直對這件事情感到懷疑,明明Puppeteer是用來操作網頁上的元素,它真的有辦法觸及到本機的檔案,並且將它上傳?

答案是可以的,而且在 Day1 當中提到的 Selenium 也同樣能夠將檔案上傳至網頁。

因為我覺得這件事情並沒有像 click, type, navigation, … etc 相對直覺,所以我是先看看網路上有沒有相關的文章,而我找到這一篇帶著大家一起實作上傳檔案的文章: Practical Puppeteer: How to upload a file programatically (推薦有興趣的人也看看)

記得當時在第一次操作的時候,就參照文章中提供的一些程式碼,很順利地就執行成功了,不過既然今天只有講「上傳檔案」這件事情,想必是有還有遇到其他的什麼問題,不過在告訴大家之前,我們還是先試著做一次吧!


動作 A: 上傳檔案

先請大家準備一個PDF檔(ex: test.pdf),並把它放在和puppet.js同一個路徑
而我在這邊就直接提供程式碼,

const inputUploadHandle = await page.$('input[type="file"]') 
await inputUploadHandle.uploadFile("test.pdf")

只需要兩行程式碼,就可以完成上傳檔案了:
首先,我們宣告一個變數(inputUploadHandle),並且 Assign 一個頁面元素給它,而這個頁面元素它是一個 input 的 HTML 標籤,其中的type是檔案(file),所以在第2行的地方,我們只需要用 inputUploadHandle 就可以代表 input(type="file") 的頁面元素了。

所以其實我們之前在做click的時候,我們都是寫

await page.click("#elementID").click

我們也可以把它改成

const btn = await page.$("#elementID")
await btn.click

大家可以發現,在搜尋頁面元素時我們都會用一個$字符(如:await page.$(selector)),這表示Puppeteer它會找到"第一個"符合selector的頁面元素;不過當我們用的是兩個$字符(如:await page.$$(selector)),則表示可能會找到"兩個以上"符合selector的頁面元素。不過通常一個頁面元素只會有一個 input(type="file")的元素,所以我們在這邊使用一個$字符就好了。

另外和大家談談 input(type="file") 這個頁面元素,我的觀察是,通常有上傳檔案服務的頁面,它都會有這個頁面元素像是在上方有提供連結的文章中,它所使用的網站就有這個標籤,

上圖反白處標籤: input class="file hide" type="file" name="file" id="uploadfile"

不過這個標籤是不會在頁面上直接讓我們看到,而我們正在操作的 DottedSign網站也是同樣的情況

上圖反白處標籤: input type="file" autocomplete="off" tabindex="-1" style="display: none;"

const inputUploadHandle = await page.$('input[type="file"]') 
await inputUploadHandle.uploadFile("test.pdf")

所以第一行就是這樣,至於第二行就是很單純的利用 uploadFile 函式將位於同一個路徑的 test.pdf 上傳到網頁上。現在我們來執行看看:

大家應該會得到以下的結果(如果沒有出錯那就表示你很幸運^_^,下面提供的解法當作參考就好了)

Error message: TypeError: Cannot read property 'uploadFile' of null

這表示針對 input[type="file"]的標籤,其實是不具備 uploadFile 這個 property 的,我們看看 Puppeteer 的技術文件怎麼說(ctrl+F: upload)

它表示input標籤是具備uploadFile這個方法的,但我們用的時候卻出錯了,究竟是為什麼呢?
結果轉念一想,會不會是其實是Puppeteer有漏洞呢?結果剛有這個想法的時候,馬上就在技術文件上的issues找到了相當多篇文章在討論Puppeteer UploadFile的問題


原來問題真的不是用錯方法,而是因為在 Puppeteer的新版本針對這個功能是有問題的,這是一個蠻難得的經驗,打程式打了三年左右,第一次發現bug不是在程式碼當中,而是在使用的工具上,所以也勉勵大家在未來Debug的時候,如果一直找不到bug在哪邊時,說不定我們真的沒有錯,可以考慮看看工具(ex: Puppeteer)的 Issues 頁面,也許會看到和我們有相同困擾的開發者們提出的見解呦。

基本上,它們發現Puppeteer到了2.1.0版本後(& 2.1.1),上傳檔案的功能就變得有問題,但是2.0.0的版本就很正常,所以如果跟著系列文章一起操作的你,不用緊張自己遇到了一個難解的BUG,也許你的Puppeteer就是2.1.0(&2.1.1)的版本,至於要怎麼看自己Puppeteer的版本呢?

請大家先打開Termina/ Command Prompt,我常用的方式有三種:

  1. 在專案目錄當中輸入 npm list,其中在上方就會看到現在Puppeteer的版本: (--puppeteer@2.1.1)

  2. 用編輯器打開 package.json 檔(ex: vim編輯器),dependencies:{ "puppeteer": "^2.1.1"}

  3. 或是直接輸入 $cat package.json,它就會把文件內的資訊顯示出來。內容同上

所以我發現自己的Puppeteer版本就是網路上isuue裡面所提到2.1.1版,所以為了能夠執行上傳的動作,我們就把我們的版本退回到2.0.0吧,具體來說,我們就再次安裝puppeteer套件,只是這次我們指定版本為2.0.0,
所以先讓Terminal/ Command Prompt到專案資料夾內(ex: QA-PUPPETEER),輸入

$npm i puppeteer@2.0.0

(參考連結:https://www.npmjs.com/package/puppeteer/v/2.0.0)

安裝後,再次確認Puppeteer的版本:

$cat package.json

這次就變成2.0.0了

然後我們再一次執行程式

$node puppet.js


這次就成功的將我們的檔案上傳到網頁上了!


動作 B: 點選右上方下一步(Continue)按鈕

這個大家也都非常的熟悉了,

  1. inspect這個元素,發現它的class是"sc-fjdhpX.fSImrr"
  2. 在B區塊當中寫下(注意會同時觸發Navigation是件)
     await Promise.all([
         page.click(".sc-fjdhpX.fSImrr"),
         Page.waitForNavigation()
     ]);
    

再次執行程式,這一次上傳完檔案後就會跳轉到下一個頁面了,我們也就完成今天的任務了!

目前程式碼:

今天的小總結:

  1. 實作上傳檔案
  2. 上傳檔案在2.1.0版本(&2.1.1)有BUG,未來可以看看文件的Issue區,說不定曾經遇到的BUG就在那裡。

下回預告: DOM 陣列操作,我們將會利用Puppeteer做出近似我們時常會在網站上出現的操作流程「點選-拖曳-點擊」。

#Puppeteer #uploadFile





在此系列文章中以Kdan Mobile主打服務之一的DottedSign做為Puppeteer的實作範例,從最開始的「環境建置」到稍微複雜的「切換頁面、上傳檔案」皆會走過,看完系列文章後我們就會得到一個能獨立完成任務的QA程式了呦^_^

留言討論