掘金 后端 ( ) • 2024-03-28 23:43

theme: geek-black

本文是常用 puppeteer 技巧的最后一篇文章。希望大家在读完之后有所收获,文章中的代码相当于是一个个的单元,因此如果大家能够收藏一下供日后使用的话,笔者会非常感谢!

21. 获取点击跳转后新页面的 URL

  • 首先,设置一个监听新目标的 Promise,当浏览器中的目标改变时,这个 Promise 会解析出新的页面对象。
  • 使用 page.click 方法点击第一个符合 .shop 选择器的元素,这可能会导致页面跳转。
  • 使用之前设置的 Promise 获取新页面的对象,并从该对象中获取 URL。
const newPagePromise = new Promise(x => browser.once('targetchanged', target => x(target.page())));
// 点击第一个 .shop 店铺,检测页面是否跳转。
await page.click(`${shopSelector}:nth-child(1)`);
const newPage = await newPagePromise;  
console.log(newPage.url());

22. 获取当前浏览器的历史浏览路径

  • 使用 browser.pages() 获取当前浏览器中的所有页面对象列表。
  • 遍历这个列表,使用 map 方法从每个页面对象中提取 URL,并返回一个新的 URL 列表。
let pageList = await browser.pages();
let pageUrlList= pageList.map(item=>item.url());

23. 点击跳转后,获取跳转页面的链接

  • 这段代码与第21段非常相似,只是最后一步是获取新页面的 URL 而不是打印它。
  • 设置监听新目标的 Promise。
  • 点击 .shop 选择器的第一个元素。
  • 使用 Promise 获取新页面对象,并从中提取 URL。
const newPagePromise = new Promise(x => browser.once('targetchanged', target => x(target.page())));
// 点击第一个 .shop 店铺,检测页面是否跳转。
await page.click(`${shopSelector}:nth-child(1)`);
const newPage = await newPagePromise;  
const newPageUrl = newPage.url();

24. 操作本地存储 localStorage

  • 使用 page.evaluate 方法在页面上下文中执行 JavaScript 代码。
  • 在这个上下文中,设置 localStorage 的一个项,键为 'token',值为 'example-token'。
await page.evaluate(() => {
  localStorage.setItem('token', 'example-token');
});

25. page.evaluate 中直接调用外部 JS 函数

  • 定义一个名为 myFunc 的函数,该函数在控制台中打印 "lol"。
  • 使用 page.exposeFunction 方法将这个函数暴露给页面上下文,使其可以在 page.evaluate 中被调用。
  • page.evaluate 中调用这个函数并返回 true。注意,由于 evaluate 的上下文隔离性,通常不能直接在其中调用外部定义的函数,但 exposeFunction 提供了一种解决方法。
// 参考文章:https://qa.1r1g.com/sf/ask/3311326581/
var myFunc = function() { 
console.log("lol");
};
await page.exposeFunction("myFunc", myFunc);
await page.evaluate(
async () => {
    await myFunc();
    return true ;
});

26. 页面间的跳转(兼容基于原生或框架开发两种情况)

  • 设置初始页面 URL 并创建一个新页面对象。
  • 获取当前浏览器中的所有页面对象,并检查它们的 URL 是否包含 "/#/",以确定页面是使用 hash 模式还是 history 模式。
  • 根据检测到的模式,设置要跳转的页面的 URL。
  • 创建一个新的页面对象并导航到设置的 URL。等待直到网络空闲再触发后续操作。
    const pageURL = "http://localhost:8080/";
    const page = await browser.newPage();
    await page.setViewport({ width: 375, height: 667 });
    await page.goto(pageURL);
    
    let pageList = await browser.pages();
    let pageUrlList= pageList.map(item=>item.url());
    let isHash = pageUrlList.some(url => url.indexOf("/#/")!=-1);
    let checkerUrl = `${pageURL}shop.html?id=1`;
    if(isHash){
      checkerUrl = `${pageURL}#/shop/1`;
    }
    
    const newPage = await browser.newPage();
    await newPage.setViewport({ width: 375, height: 667 });
    await newPage.goto(checkerUrl, {
      waitUntil: "networkidle0", //不在有网络连接时候触发
    });

27. 检测通过导航跳转页面功能(兼容基于原生或框架开发两种情况)

  • 该段代码的描述似乎与第26段相似,但具体内容没有在提供的文本中显示。它可能涉及点击某个导航元素并检查页面是否正确地跳转到了预期的 URL。

28. 局部截图进行像素比对

  • 使用 $ 方法选择页面上的 ".app-box" 元素。
  • 对该元素进行截图,并将截图保存为 "box1.png" 文件。这可以用于后续的像素比对或视觉回归测试。
    let box1 = await page.$(".app-box");
    await box1.screenshot({
      path: `box1.png`,
    });

29. 鼠标移入元素

  • 等待页面上出现 '#element' 选择器对应的元素。
  • 使用 $eval 方法获取该元素的边界矩形信息,特别是 x 和 y 坐标。
  • 使用 page.mouse.move 方法将鼠标移动到这些坐标上。
  • 模拟鼠标按下事件。注意这里没有模拟鼠标释放事件,但在实际应用中可能也需要这样做。
await page.waitForSelector('#element');
const { x, y } = await page.$eval('#element', el => {
  const { x, y } = el.getBoundingClientRect();
  return { x, y };
});
await page.mouse.move(x, y);
await page.mouse.down()

30. 检测是否使用第三方库 (示例为 axios 和 jq )

  • 使用 page.evaluate 方法在页面上下文中执行代码,检查 axios$(通常代表 jQuery)是否定义。这将返回一个布尔值,指示这些库是否在页面上使用。

31. 检测 echarts

  • 使用 page.evaluate 方法在页面上下文中获取 echarts 实例,并从中提取系列数据和图例信息。这可以用于验证图表是否正确渲染或包含预期的数据。
    // 在页面上下文中获取系列数据
    const seriesData = await page.evaluate(() => {
      const chartInstance = echarts.getInstanceByDom(document.querySelector('.echarts'));
      const seriesData = chartInstance?.getOption()?.series[0]?.data || [];
      // 遍历系列,提取每个系列的数据
      return seriesData
    });
    
     // 在页面上下文中获取图例相关信息
    const legendData = await page.evaluate(() => {
      const chartInstance = echarts.getInstanceByDom(document.querySelector('.echarts'));
      const legendData = chartInstance?.getOption()?.legend?.[0]?.data || [];
      return legendData;
    });

32. 原生下拉列表(select)操作

  • 使用 page.select 方法选择下拉列表中的选项。对于单选列表,传递选择器的值和要选择的选项的文本。对于多选列表,传递选择器的值和多个要选择的选项的文本。注意,当有 value 值时,默认是选择 value 值对应的选项;否则,根据文本内容选择。
// 单选
await page.select(selector,"小花");

// 多选
await page.select(selector, "三叔", "闷油瓶");

// 需要注意的是:当有 value 值时默认是选择 value 值对应的选项,否则选中文本内容

33. 拦截请求修改请求数据(注意需要写在 page.goto 之前)

  • 创建一个模拟数据数组 mockData
  • 启动一个 puppeteer 浏览器实例,并创建一个新页面。
  • 在页面加载任何内容之前,通过设置 page.setRequestInterception(true) 来启用请求拦截。
  • 使用 page.on("request", callback) 监听页面上的所有请求。如果请求的 URL 以 "data.json" 结尾,则使用模拟数据响应请求;否则,继续原始请求。
  • 导航到指定的页面 URL,并等待直到页面完全加载和网络空闲。最后关闭浏览器实例。这段代码的主要目的是在测试期间模拟后端响应,以确保前端代码能够正确处理不同的数据场景。
 const mockData =[ 
  { "value": 30, "name": "测试一" },
  { "value": 26, "name": "测试二" },
  { "value": 20, "name": "测试三" },
  { "value": 10, "name": "测试四" },
  { "value": 25, "name": "测试五" }
];
(async () => {
  let score = 0;
  try {
    const browser = await puppeteer.launch({
      headless: false,
      args: ["--no-sandbox", "--disable-setuid-sandbox"],
    });
    const page = await browser.newPage();
    
    // 拦截页面请求 
    await page.setRequestInterception(true);
    page.on("request", (request) => {
      if (request.url().endsWith(`data.json`)) {
        request.respond({
          content: "application/json",
          headers: { "Access-Control-Allow-Origin": "*" },
          body: JSON.stringify(mockData), // 返回的数据
        });
      } else {
        request.continue();
      }
     });
    // 拦截页面请求代码结束 
     
     await page.goto(pageURL, { waitUntil: ["domcontentloaded", "load", 'networkidle0', 'networkidle2'] });

     await  browser.close();
  } catch (error) {
    console.error(`测试未通过,报错为${error}`)
    process.exit(1);
  }
})();

下面是前两篇文章的链接,方便大家跳转:

第一篇:写文章 - puppeteer 自动化检测常用 API (第一弹) - 掘金 (juejin.cn)

第二篇:写文章 - puppeteer 自动化检测常用 API (第二弹) - 掘金 (juejin.cn)