引言

对于编写应用程序,尤其是要部署上线投入生产使用的应用,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.querySelectordocument.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的简单使用的更多相关文章

  1. puppeteer(headless chrome)实现网站登录

    puppeteer简介 puppeteer是Chrome团队开发的一个node库,可以通过api来控制浏览器的行为,比如点击,跳转,刷新,在控制台执行js脚本等等.有了这个神器,写个爬虫,自动签到,网 ...

  2. 使用Puppeteer进行数据抓取(一)——安装和使用

    Puppeteer是 Google Chrome 团队官方的Chrome 自动化工具.它本身是基于Chrome Dev Protocol协议实现的,但它提供了更高层次API封装,使用起来更加方便快捷. ...

  3. puppeteer 试用

    puppeteer 是chrome 团队提供的Headless chrome node api 库,我们可以用来方便的进行chrome 操作,同时 可以做好多事情(web 爬虫,生成pdf,截图... ...

  4. 【工具篇】在.Net中实现HTML生成图片或PDF的几种方式

    前段时间由于项目上的需求,要在.Net平台下实现把HTML内容生成图片或PDF文件的功能,特意在网上研究了几种方案,这里记录一下以备日后再次使用.当时想着找一种开发部署都比较清爽并且运行稳定的方案,但 ...

  5. Net中实现HTML生成图片或PDF

    Net中实现HTML生成图片或PDF的几种方式 前段时间由于项目上的需求,要在.Net平台下实现把HTML内容生成图片或PDF文件的功能,特意在网上研究了几种方案,这里记录一下以备日后再次使用.当时想 ...

  6. 使用Puppeteer进行数据抓取(三)——简单的示例

    本文以一个示例简单的介绍一下puppeteer的用法,我们的目的是:获取我博客上的文章的前十页的所有随笔的标题和链接.由于puppeteer本身是自动化chorme,因此这里我们的步骤和手动操作浏览器 ...

  7. puppeteer(二)操作实例——新Web自动化工具更轻巧更简单

    一.入门实例 了解puppeteer见上一篇文章: https://www.cnblogs.com/baihuitestsoftware/p/9957343.html 1)本例主要是启动浏览器 con ...

  8. nodejs puppeteer linux(centos)环境部署以及用puppeteer简单截图

    1.安装Node环境 如果有安装Node请忽略第1点 #下载cd /usr/local/srcwget https://nodejs.org/dist/v10.15.3/node-v10.15.3-l ...

  9. 一个简单的puppeteer爬虫

    const puppeteer = require("puppeteer"); const path = require('path'); const pathToExtensio ...

  10. 使用Puppeteer抓取受限网站

    不要相信前端是安全的,今天简单验证一下,但是希望大家支持正版,支持原作者,毕竟写书不易. 安装Puppteer npm install --save puppeteer 选择目标网站 我们这里选择胡子 ...

随机推荐

  1. 汇编debug的安装

    实验一查看CPU和内存,用机器指令和汇编指令编程 在做实验前需要debug命令. 工具:dosbox,debug.exe 安装:dosbox :https://www.dosbox.com/ debu ...

  2. win10系统单独编译和使用WebRTC的回声消除(AEC)、音频增益(AGC)、去噪(NS)模块

    一.简介 本人想单独编译并使用WebRTC的音频回声消除模块,奈何技术有限,于是在百度的海洋里大海捞针,发现了https://www.cnblogs.com/mod109/p/5827918.html ...

  3. np.random.beta

    numpy.random.beta(a,b,size=None) 从β分布中提取样本.β分布是狄里克莱分布的一个特例,与伽马分布有关. 在这里我们将参数(3个参数)设置为32 32 3 参数1:32次 ...

  4. 栈溢出-GOT表劫持测试

    1.目标程序源代码 char name[64]; int main(){ int unsigned long long addr; setvbuf(stdin,0,2,0); setvbuf(stdo ...

  5. Chromium Trace and Perfetto使用详解

    1. Trace chromium 在 base 库中提供了 base::trace_event::TraceLog 类,该类是 TRACE_EVENT* , TRACE_COUNTER* 等宏的底层 ...

  6. OpenGL 坐标系统详解

    GL中的坐标系是标准设备坐标,即他的每个坐标轴的取值范围都是[-1.0,1.0].通常,我们输入到顶点着色器中的顶点坐标都会被转换为标准化设备坐标,然后进行光栅化,转变成屏幕坐标.然而事实上,从顶点坐 ...

  7. 接口开放太麻烦?试试阿里云API网关吧

    前言 我在多方合作时,系统间的交互是怎么做的?这篇文章中写过一些多方合作时接口的调用规则和例子,然而,接口开放所涉及的安全.权限.监控.流量控制等问题,可不是简简单单就可以解决的,这一般需要专业的开放 ...

  8. Transformers 中原生支持的量化方案概述

    本文旨在对 transformers 支持的各种量化方案及其优缺点作一个清晰的概述,以助于读者进行方案选择. 目前,量化模型有两个主要的用途: 在较小的设备上进行大模型推理 对量化模型进行适配器微调 ...

  9. C#中LINQ的使用知多少?LINQ常功能整理,实例源代码解析

    LINQ(Language-Integrated Query)是C#语言中的一个强大的查询技术,它提供了一种统一的查询语法,可以用于查询和操作各种数据源,包括集合.数据库.XML等.下面详细描述了LI ...

  10. 在Ubuntu机器上使用war包安装Jenkins

    因为一些需求需要迁移之前使用的Jenkins,原来是按照官方文档使用apt方式安装的,这次搬迁后的机器由于默认不通外网(可以通过代理走外网),因此趁此机会,尝试改用war包方式安装 环境目标 系统Ub ...