puppeteer的简单使用
引言
對于編寫應用程序,尤其是要部署上線投入生產使用的應用,QA是其中重要的一環,在過去的工作經歷中,我參與的項目開發,大多是由測試同學主要來把控質量的,我很少編寫前端方面的測試代碼,對于測試工具的使用,也基本停留在一個小玩具的樣子,所以接觸的也少,回憶上一次寫單元測試,還是在一個vue3的課程中使用jest實現TDD,記得之前有的時候面試,會被問到有沒有在項目中用單測,但是因為以前工作中大多數時候需求排期都只考慮開發的時間,就很少考慮到這方面,然后就,面試中這方面也說不出什么東西,最近因為一個偶然的機會,我接觸了puppeteer用來做前端自動化測試,用著還感覺蠻有點小意思。
puppeteer能做什么
puppeteer是一個Node.js庫,通過puppeteer的文檔,我們可以快速的了解我們能使用puppeteer來做些什么:
Most things that you can do manually in the browser can be done using Puppeteer! Here are a few examples to get you started:
- Generate screenshots and PDFs of pages.
- Crawl a SPA (Single-Page Application) and generate pre-rendered content (i.e. "SSR" (Server-Side Rendering)).
- Automate form submission, UI testing, keyboard input, etc.
- Create an automated testing environment using the latest JavaScript and browser features.
- Capture a timeline trace of your site to help diagnose performance issues.
- Test Chrome Extensions.
第一句作為總領,點出了puppeteer可以模擬用戶與瀏覽器的交互。包括頁面截圖、生成SPA的預渲染內容、觸發用戶交互事件等等,可以用于進行UI和功能測試,另外可以看出除了普通的前端測試外,還可以作為爬蟲工具使用。本文針對簡單的用戶交互事件的模擬和頁面截圖,實現一個puppeteer的使用示例。
準備工作
-
首先在使用之前,需要先安裝依賴
npm i puppeteer # or using yarn yarn add puppeteer # or using pnpm pnpm i puppeteer我這里使用yarn global進行了全局的安裝。
-
然后我們來準備待測試的頁面
我這里準備了一個簡單的頁面,直接預覽如下所示:
頁面分為兩部分,最上面是標題,下面展示的是一個canvas。我們即將測試的內容除了基本的請求頁面和獲取頁面元素外,主要有兩項功能,分別為:
- 點擊canvas后展示一個彈窗,使用文字描述“土”與其他五行的關系,測試點擊事件的模擬和彈窗的展示
- 點擊canvas后在canvas上繪制,使用圖像描述“土”與其他五行的關系,測試puppeteer的截圖功能并引入
blink-diff模塊,用于圖像的對比
接下來我們就可以開始編寫測試代碼。
使用示例
因為是模擬交互,所以會有許多異步的操作,我們可以通過await獲取結果,所以這個例子中的代碼會使用異步函數async來包裹。
另外由于要模擬操作,所以選擇器也是核心功能,類似于document.querySelector或document.querySelectorAll的作用,puppeteer使用css選擇器語法的超集進行查詢,也就是說我們可以使用.class、#id等css選擇器來進行元素查詢。
基本功能
以下是基本的代碼:
/*
* check.js
*/
const puppeteer = require('puppeteer');
(async () => {
// Launch the browser and open a new blank page
const browser = await puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
const page = await browser.newPage();
// Set screen size
await page.setViewport({width: 1920, height: 1080});
// Navigate the page to a URL
await page.goto('http://0.0.0.0:8080');
// 關閉puppeteer
browser.close();
// ...
})()
在模擬交互前,我們需要先啟動瀏覽器并打開頁面,以上代碼就可以完成這些操作:
-
puppeteer.launch:啟動瀏覽器
在啟動瀏覽器時,我們可以設置一些啟動參數,這里的
'--no-sandbox'代表取消沙盒模式,放開權限,--disable-setuid-sandbox也是類似的作用,此兩者的區別可以參考這個discuss -
browser.newPage:可以理解為打開一個瀏覽器tab
-
page.setViewport:設置視窗尺寸
-
page.goto:跳轉頁面到指定地址,這里跳轉到了我們本地啟動的8080服務頁面
-
browser.close:關閉瀏覽器。我們可以在獲取到數據后就進行關閉操作,再在后續中使用抓取到的數據
可以看到在每步操作之前,我們都使用了await來等待操作完成,每一步都需要等待上一步操作完畢才能開始。
接下來我們就可以開始獲取頁面上的元素,比如示例頁面上的h3標簽。
const elm = await page.waitForSelector('h3');
// OR
const elm = await page.$('h3');
console.log(elm);
// CdpElementHandle {
// handle: CdpJSHandle {},
// [Symbol(_isElementHandle)]: true
// }
console.log(elm.innerText); // undefined
可以通過.waitForSelector或簡寫的.$方法獲取元素,可以看到打印出來的并不是DOM對象,而是一個經過封裝的CdpElementHandle類型的對象,因此我們無法通過elm.innerText的方式來獲取h3標簽內的文本內容,似乎這個選擇器方法只能用于判斷頁面上是否存在某個或某類匹配的元素。
如果想獲取元素對應的DOM屬性,可以使用Page.$eval()來實現,用法如下所示:
const elmText = await page.$eval('h3', h3 => h3.innerText);
console.log(elmText); // "土"與其他五行的關系
判斷DOM屬性
在本文的測試頁面中,實現了點擊canvas顯示彈窗的功能,彈窗的顯示是通過js代碼添加樣式類實現的,并且會在2s后關閉彈窗的顯示,所以我們需要測試樣式類的添加和移除。
同樣的,我們需要先獲取到canvas元素。
const canvas = await page.$('canvas');
接著模擬點擊,并獲取彈窗對應div的classList。
await canvas.click();
const popupClassList = await page.$eval('.popup-dialog', popup => popup.classList);
console.log(popupClassList); // { '0': 'popup-dialog', '1': 'visible' }
可以看到彈窗的classList中按照預期出現了代表顯示的樣式類visible。
接著我們繼續測試2s后彈窗關閉。
await new Promise(r => setTimeout(r, 2000));
const postPopupClassList = await page.$eval('.popup-dialog', popup => popup.classList);
console.log(postPopupClassList); // { '0': 'popup-dialog' }
可以看到在2s后,樣式類visible按照預期被移除了。這里我們使用一個promise來計時。
截圖功能
最后我們來使用puppeteer的截圖功能。在使用之前,先把測試頁面的點擊canvas顯示彈窗改為繪制圖像,然后我們來測試。
在截圖之前,我們需要先指定一個目錄用于存放截圖,這里我直接創建一個imgs文件夾,然后編寫以下代碼:
const imgDir = './imgs/';
canvas.screenshot({ path: `${imgDir}canvas.png` });
執行node check.js后,我們就可以看到imgs目錄下生成了一張圖片,和我們在瀏覽器中看到的是一樣的。
如果這是一個UI效果圖,我們可以把他重命名為target.png,然后使用代碼實現后,配合使用blink-diff模塊,對比UI設計圖與實際代碼實現所存在的差異大小;blink-diff模塊也可以通過NPM來安裝。blink-diff是一個輕量級的圖片對比工具,以下是一個簡單的使用展示:
const puppeteer = require("puppeteer"),
BlinkDiff = require('blink-diff');
// ...
// 關閉puppeteer
browser.close();
const diff = new BlinkDiff({
imageAPath: imgDir + 'target.png', // ui
imageBPath: imgDir + 'canvas.png', // 頁面截圖
imageOutputPath: imgDir + 'Diff.png', // 差異對比圖
threshold: 0.02
});
因為已經得到截圖,所以此時已經不需要瀏覽器了,new BlinkDiff可以在puppeteer關閉后執行。
imageAPath和imageBPath分別是設計圖和頁面截圖的存放路徑,imageOutputPath輸出兩張圖片的差異對比圖,threshold是一個百分比閾值,當差異比例低于該值時忽略差異,在這里這就是說,當差異比例低于2%,就認為兩張圖是相同的。
接下來就通過調用diff.run()方法來執行對比:
diff.run(function (error, result) {
if (error) {
throw error;
} else {
let rel = Math.round((result.differences /
result.dimension) * 100);
console.log(result.code);
console.log(diff.hasPassed(result.code));
console.log(diff.hasPassed(result.code) ? 'Passed' : 'Failed');
console.log('總像素:' + result.dimension);
console.log('發現:' + result.differences + ' 差異,差異占?'
+ rel + "%");
}
});
當正常執行后,會返回一個result對象包含對比結果的信息。
result.differences表示存在不同的像素數量,result.dimension表示像素的總數量,因此這里rel計算得到的就是像素的差異比例。
result.code就是一個結果狀態碼,調用diff.hasPassed方法會根據diff的配置對狀態碼進行解析,從而得出通過或失敗的判斷。
到這里為止就是一個截圖功能和圖像對比的簡單示例,看上去使用起來挺不錯的樣子,但實際還是存在一些問題,比如我最近遇到的,使用漸變函數設置樣式,得到的截圖會存在問題,并沒有得到應用漸變后的樣式截圖,不知道是兼容上的問題還是我的使用方式問題,所以暫時我使用了getComputedStyle作為替代方案。
總結
好啦,以上就是puppeteer的簡單使用,有感興趣的小伙伴可以繼續閱讀官方文檔深入研究。
總結
以上是生活随笔為你收集整理的puppeteer的简单使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【OpenVINO】 使用 OpenVI
- 下一篇: 尊嘟假嘟?三行代码提升接口性能600倍