如何使用 JavaScript 模擬 <input type="file"> 選取檔案上傳操作,一直是我寫網頁自動測試的最大障礙。

基於安全考量,瀏覽器禁止 JavaScript 代替使用者為 <input type="file"> 的選取檔案路徑。試想一下,你連上某個網頁,裡面的程式可以任意指定本機檔案自動上傳,這是多可怕的漏洞。因此,<input type="file"> 要上傳什麼檔案,只能由使用者親自操作選擇,由不得程式亂來,而這點限制屢屢成為涉及檔案上傳的網頁介面難以完全自動化測試的原因。

之前試過一些曲折解法,例如另外寫程式透過 Windows API 找開啟檔案對話框,模擬鍵盤滑鼠操作選取檔案... 等等,工程浩大,而且無法轉為背景執行,不算是完美的解決方案。另一種做法是不過透網頁介面的上傳功能,另外寫一組測試專用的 AJAX 函數取代選取檔案上傳動作,但另寫替代函式是項工程,且繞道而行有失逼真。問題有點棘手,有時我就偷懶略過上傳檔案不做測試,蒙混過關。

最近在用 Puppeteer Sharp 寫網站自動測試,上傳附檔是整個功能重要一環,省略不測,後面就都不用玩惹。逃避多年,直球對決的時刻終於來了!

苦思多時,看到一線曙光。我的網頁是用 dm-file-uploader 這個 jQuery 套件處理檔案上傳,支援拖拉檔案上傳功能。鑽進原始碼(Open Source 萬歲!),我發現拖拉上傳功能是在網頁元素 drop 事件由 dataTransfer.files 取得檔案內容。(延伸閱讀: HTML5練習-從桌面拖拉檔案到網頁) 換言之,我只要能模擬出 dataTransfer.files 物件並觸發 drop 事件,就能用純 JavaScript 模擬檔案上傳了。當然,瀏覽器的安全管控還是在,上傳檔案的內容要自己生,不可能指定路徑叫瀏覽器抓。

最後我研究出來的程式碼如下,靠 jQuery Event 物件HTML5 File 物件 聯手模擬拖入 hello.txt 檔的效果:

var simuDropEvent = $.Event('drop', {
    preventDefault: function() {},
	originalEvent: {
		dataTransfer: {
			files: [
				new File(["Hello, world!"], "hello.txt")
			]
		}
	}
});
$("#drag-and-drop-zone").trigger(simuDropEvent);

用套件的示範網頁實測:

同樣原理應可適用所有支援拖拉上傳的 jQuery 套件,例如:對 Kendo UI 也可如法泡製。

意外解決高懸多年的難題,好開薰~

This article shows how to simulate file uploading via simulating file drag-and-drop event with jQuery in web page auto testing.


Comments

# by Kevinst

使用JavaScript formdata 就可以了 https://codertw.com/%E5%89%8D%E7%AB%AF%E9%96%8B%E7%99%BC/236201/

# by Jeffrey

to Kenvinst, 另闢途徑上傳的缺點是測不到與上傳套件整合的JavaScript邏輯,例如:檔案檢核、上傳成功事件觸發的網頁更新,逼真度較差。

# by JC

黑大好 請問如果用python配selenium會不會比較簡單呢? 像這篇說的這樣? http://allselenium.info/file-upload-using-python-selenium-webdriver/

# by Jeffrey

to JC, 哦哦哦,原來已經有人寫好現成的鍵盤輸入檔案路徑功能,感謝分享! 改用 Selenium 還有工具資源豐富並支援跨瀏覽器、不依賴 jQuery 及上傳套件的好處,是很好的選擇。文章採用的做法在符合 jQuery + 支援拖拉上傳的測試情境下只靠 JavaScript 就能完成,不必綁死 Selenium 及額外工具(如果原本沒使用 Selenium,可省去為了喝奶養牛),算是小小優點。

# by JC

( ´ ▽ ` )b 不會~

Post a comment