【每日一水文】 Day02 JS篇 浅析puppeteer用于UI测试的基本操作

171 阅读4分钟

1.前要

无聊无聊好无聊,感觉上班很无聊,学习很无聊,什么都很无聊,反正时间浪费也是浪费,不如水篇文章算了,里面的内容覆盖面可能不全,希望大家见谅。

1.png

2.为什么要用puppeteer?

这是一个说来话长的故事,我也不知道从哪说去,简单来说,自从我在某个夜晚接到领导的电话,领导告诉我刚刚上线的功能出bug,让我赶紧去看看,虽然这个功能不是我写的,这次上线我也不负责,但是因为我离公司近,领导就让我去赶紧处理bug,我也只好放下手上的游戏去改bug了。到了现场三下五除二就把bug解决了,原来是一个数据处理的有问题导致一个核心功能出了问题,但是理论上上线后这个功能是肯定会被测试到的,但是却被人为疏忽了。可恶啊!居然因为其他人的不严谨导致我加班,岂可修!就没有什么东西可以规范一下测试的流程吗,至少核心的流程可以走一遍,于是乎我就想到了UI自动化测试,而作为一个前端,UI自动化测试的工具肯定首推puppeteer了,因为它是node环境下运行的,安装也简单。

3.puppeteer的安装与引入以及基本使用

puppeteer的基本使用逻辑很简单,就2步

  • 获取对应的节点
  • 执行相应的操作,此文只涉及点击以及输入
    本文将重点介绍这2部分功能
  1. 安装 Puppeteer:在项目目录中运行命令安装 Puppeteer。

       // 根据node版本选择对应的puppeteer版本,不然可能会安装失败
    npm install puppeteer
    
  2. 引入 Puppeteer:在代码中引入 Puppeteer 模块。

    const puppeteer = require('puppeteer');
    
  3. 启动浏览器实例:使用 puppeteer.launch() 方法启动一个新的浏览器实例。

    // launch的的时候可以设置参数
    const browser = await puppeteer.launch({
        headless:false, //设置为true就不会打开浏览器,设置成false会打开游览器
        defaultViewport: { width: 1920, height: 880 }, // 设置页面大小
        args: ['--start-maximized'] // 设置游览器启动时最大化
    });
    
  4. 创建页面:通过浏览器实例的 newPage() 方法创建一个新的页面。

    const page = await browser.newPage();
    
  5. 页面导航:使用 page.goto() 方法导航到指定的 URL。

    await page.goto('https://example.com');
    
  6. 进行操作:使用页面实例的方法进行各种操作,例如点击按钮、填写表单等。

    await page.click('#myButton');
    await page.type('#myInput', 'Hello, World!');
    
  7. 获取数据:使用页面实例的方法获取需要的数据。

    const title = await page.title();
    console.log('页面标题:', title);
    
  8. 关闭浏览器实例:完成操作后,关闭浏览器实例。

    await browser.close();
    

4.定位Dom节点

  • 根据css定位dom节点
   await page.$('css选择器') // 返回第一符合css选择器的dom
   await page.$$('css选择器') // 返回所有符合css选择器的dom
    // 例子
   await page.$('.test') // 返回类为test的第一个dom
   await page.$$('.test') // 返回类名为test的所有dom
  • 根据属性内容定位
   await page.$('.test[属性名=属性值]') // 返回类名为test,且属性值满足要求的第一个dom
   
   // 例子
   await page.$('.test[placeholder="123"]') // 返回类名为test,且placeholder值为123的第一个dom
  • xpath定位
    await page.$x(xpath语法) // xpath语法这边不做详细介绍,因为我也是个彩笔,只会简单使用
    
    // 例子
    
    await page.$x('//span[text()="重置"]') //选择所有内容为重置的span标签

基本上学会了这3个在html中就没有你选不中的dom

5.模拟用户操作操作

  • 点击操作
    let ele = await page.$('.test[placeholder="test"]')
    await ele?.click() // 对元素进行点击
  • 输入操作
    let ele = await page.$('.test[placeholder="test"]')
    await ele?.type("testing") // 对元素进行输入‘testing’ 内容,前提是该元素可输入,否则会报错
  • 键盘模拟按键
page.keyboard.down(key, options)  //模拟按下指定的键。`key` 参数表示要按下的键位,可以是单个字符或键码值。`options` 参数可选,可以设置按键的修饰符,比如 `shift`、`ctrl` 等。

page.keyboard.up(key)  //模拟松开指定的键。与 `down()` 方法对应,用于模拟按键事件的释放。

page.keyboard.press(key, options) //模拟按下并松开指定的键。相当于调用 `down()` 和 `up()` 方法的组合。

page.keyboard.type(text, options) //将指定的文本输入到当前聚焦的元素中。`text` 参数表示要输入的文本,`options` 参数可选,可以设置文本输入的延迟、修改器键等。

  • 鼠标模拟
page.mouse.down(options) //模拟按下鼠标按键。`options` 参数可以设置 `button` 属性来指定按下哪个鼠标按键。

page.mouse.up(options) //模拟松开鼠标按键。`options` 参数与 `down()` 方法类似,可以指定松开哪个鼠标按键。

page.mouse.click(x, y, options) //模拟鼠标点击事件。会先调用 `move()` 将鼠标移动到指定位置,再调用 `down()` 和 `up()` 方法模拟按下和松开鼠标按键。

page.mouse.move(x, y, options) //将鼠标移动到指定位置。

page.mouse.wheel(options) //模拟鼠标滚动事件。可通过设置 `options` 对象的 `deltaX` 和 `deltaY` 属性来模拟鼠标滚动方向和速度。

page.mouse.buttons //表示当前鼠标按下的按钮状态。返回一个整数值,使用二进制位掩码表示不同的按钮状态。例如,`1` 表示左键,`2` 表示右键,`4` 表示中键。

其中对于键鼠操作options的值有所不同,如下:

对于键盘操作,options 对象可以包含以下属性:

  • delay:指定输入文本的延迟时间(以毫秒为单位),即每个字符之间的间隔时间。默认值为 0。
  • text:指定要输入的文本内容。可以与 type() 方法一起使用,将指定的文本输入到当前聚焦的元素中。
  • modifiers:用于设置修饰键的状态。例如,modifiers: ['Shift'] 表示按下 Shift 键。可以与 down()up()press() 方法一起使用。

对于鼠标操作,options 对象可以包含以下属性:

  • button:用于指定鼠标按键,可以是 'left''right''middle' 中的一个,默认为 'left'
  • clickCount:用于指定鼠标点击的次数,默认为 1。
  • deltaXdeltaY:用于模拟鼠标滚动事件时设置滚动的偏移量。

6.监听网络接口加载

Puppeteer 可以获取页面中的指定接口。你可以通过监视网络请求,并筛选出符合条件的请求来获取特定的接口数据。

下面展示如何使用 Puppeteer 获取页面中的指定接口数据:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch(); // 运行游览器
  const page = await browser.newPage(); // 创建page实例

  // 监听页面中的网络请求
  await page.setRequestInterception(true);

  page.on('request', (request) => {
    // 判断请求的 URL 是否符合条件
    if (request.url().includes('/api')) {
      request.continue(); // 继续请求
    } else {
      request.abort(); // 中止请求
    }
  });

  // 导航到指定的 URL
  await page.goto('http://example.com');

  // 等待页面加载完成
  await page.waitForNavigation();

  // 获取符合条件的网络响应
  const responses = await page.waitForResponse(response => response.url().includes('/api'));

  // 获取接口数据
  const responseData = await responses.json();
  console.log(responseData);

  await browser.close();
})();

7.一些需要注意的点

  • 注意iframe的切换

很多页面会通过iframe内嵌其他页面,此时如果不指定该iframe的话,是无法获取到该iframe下的dom元素的。

    let iframe = await page.$('#myIframe') // 获取id为myIframe的iframe框架
    const frame = await iframe.contentFrame(); // 获取iframe的框架内容
    let ele = await frame.$('#test') // 获取iframe下id为test的元素
  • await的使用

puppeteer中很多操作都是非阻塞异步操作,需要用await来确保程序的执行顺序

    let frame = await (await page.$('#myIframe'))?..contentFrame(); // 灵活使用await减少代码行数
  • 等待元素出现

很多时候我们无法确定元素什么时候加载出现,这个时候就需要等待dom的加载,不然会报错

await page.waitForTimeout(1000) // 等待1s

await page.waitForNavigation(); // 等待页面跳转完成

await page.waitForSelector(`input[placeholder="请输入账号"]`); // 等待匹配的输入框加载完成

await page.waitForXPath("//span[text()='退出']"); // xpath写法的等待元素加载

8.总结

此文只写了如何获取dom,如何对dom进行操作,以及一些特别需要注意的点,若想进行UI测试,让程序识别功能是否完整,其内部逻辑还需各位自己设计。在我看来此物甚是有趣,可堪大用。此文只做基础入门使用,也是我本人的学习笔记,望与君共勉。