chrome新特性-recorder

news2024/11/25 2:56:32

chrome新特性-recorder

    • 一、简介
    • 二、录制
    • 三、录制回放
      • 1.Chrome DevTool工具
      • 2.Puppeteer脚本
        • (1) 介绍
        • (2) 使用

一、简介

recorder
与Selenium IDE浏览器插件类似,可用于自动化测试及脚本生成(Puppeteer脚本);前者为Web自动化测试框架Selenium的配套浏览器插件支持Java、Python等测试脚本的生成。

二、录制

录制

三、录制回放

下面介绍两种录制回放方式

1.Chrome DevTool工具

导入json配置文件,通过Chrome DevTool工具回放

2.Puppeteer脚本

(1) 介绍

Puppeteer 是一个 Node library,提供了一套完整的通过 DevTools 协议操纵 Chrome 或 Chromium 的 API。Puppeteer 默认以 无头(headless) 的方式运行, 也可以使用 GUI 的方式运行 Chrome 和 Chromium。
熟悉爬虫或者 UI 自动化的同学可能会联想到 PhantomJS、CasperJS 或者 Selenium,而作为 Chrome DevTools 团队亲自出品和维护的 puppeteer 不管是在功能的完整性、稳定性、兼容性、安全性还是性能都将成为碾压其他工具的存在。

(2) 使用

  • 安装并配置nodejs

可参考 nodejs环境配置

  • 安装依赖包
npm install -g puppeteer
npm install -g url
npm install -g @puppeteer/replay
  • 修改js脚本

禁用headless模式(启用GUI模式)

    const browser = await puppeteer.launch();

改为

    const browser = await puppeteer.launch({
      headless: false, // GUI模式
    });
  • 运行js脚本
node test.js
  • test.js

recorder导出的puppeteer脚本如下

const puppeteer = require('puppeteer'); // v13.0.0 or later

(async () => {
    const browser = await puppeteer.launch({
    	headless: false, // GUI模式
    });
    const page = await browser.newPage();
    const timeout = 5000;
    page.setDefaultTimeout(timeout);

    {
        const targetPage = page;
        await targetPage.setViewport({"width":1752,"height":884})
    }
    {
        const targetPage = page;
        const promises = [];
        promises.push(targetPage.waitForNavigation());
        await targetPage.goto("https://www.baidu.com/");
        await Promise.all(promises);
    }
    {
        const targetPage = page;
        await scrollIntoViewIfNeeded([["#kw"],["xpath///*[@id=\"kw\"]"]], targetPage, timeout);
        const element = await waitForSelectors([["#kw"],["xpath///*[@id=\"kw\"]"]], targetPage, { timeout, visible: true });
        const type = await element.evaluate(el => el.type);
        if (["select-one"].includes(type)) {
          await element.select("你好");
        } else if (["textarea","text","url","tel","search","password","number","email"].includes(type)) {
          await element.type("你好");
        } else {
          await element.focus();
          await element.evaluate((el, value) => {
            el.value = value;
            el.dispatchEvent(new Event('input', { bubbles: true }));
            el.dispatchEvent(new Event('change', { bubbles: true }));
          }, "你好");
        }
    }
    {
        const targetPage = page;
        await targetPage.keyboard.down("Enter");
    }
    {
        const targetPage = page;
        await targetPage.keyboard.up("Enter");
    }
    {
        const targetPage = page;
        await scrollIntoViewIfNeeded([["aria/你好,汉语词语,百度百科[role=\"link\"]"],["#\\31  > div > div > h3 > a"],["xpath///*[@id=\"1\"]/div/div/h3/a"],["text/你好(汉语词语) - 百度百科"]], targetPage, timeout);
        const element = await waitForSelectors([["aria/你好,汉语词语,百度百科[role=\"link\"]"],["#\\31  > div > div > h3 > a"],["xpath///*[@id=\"1\"]/div/div/h3/a"],["text/你好(汉语词语) - 百度百科"]], targetPage, { timeout, visible: true });
        await element.click({
          offset: {
            x: 99,
            y: 14,
          },
        });
    }

    await browser.close();

    async function waitForSelectors(selectors, frame, options) {
      for (const selector of selectors) {
        try {
          return await waitForSelector(selector, frame, options);
        } catch (err) {
          console.error(err);
        }
      }
      throw new Error('Could not find element for selectors: ' + JSON.stringify(selectors));
    }

    async function scrollIntoViewIfNeeded(selectors, frame, timeout) {
      const element = await waitForSelectors(selectors, frame, { visible: false, timeout });
      if (!element) {
        throw new Error(
          'The element could not be found.'
        );
      }
      await waitForConnected(element, timeout);
      const isInViewport = await element.isIntersectingViewport({threshold: 0});
      if (isInViewport) {
        return;
      }
      await element.evaluate(element => {
        element.scrollIntoView({
          block: 'center',
          inline: 'center',
          behavior: 'auto',
        });
      });
      await waitForInViewport(element, timeout);
    }

    async function waitForConnected(element, timeout) {
      await waitForFunction(async () => {
        return await element.getProperty('isConnected');
      }, timeout);
    }

    async function waitForInViewport(element, timeout) {
      await waitForFunction(async () => {
        return await element.isIntersectingViewport({threshold: 0});
      }, timeout);
    }

    async function waitForSelector(selector, frame, options) {
      if (!Array.isArray(selector)) {
        selector = [selector];
      }
      if (!selector.length) {
        throw new Error('Empty selector provided to waitForSelector');
      }
      let element = null;
      for (let i = 0; i < selector.length; i++) {
        const part = selector[i];
        if (element) {
          element = await element.waitForSelector(part, options);
        } else {
          element = await frame.waitForSelector(part, options);
        }
        if (!element) {
          throw new Error('Could not find element: ' + selector.join('>>'));
        }
        if (i < selector.length - 1) {
          element = (await element.evaluateHandle(el => el.shadowRoot ? el.shadowRoot : el)).asElement();
        }
      }
      if (!element) {
        throw new Error('Could not find element: ' + selector.join('|'));
      }
      return element;
    }

    async function waitForElement(step, frame, timeout) {
      const count = step.count || 1;
      const operator = step.operator || '>=';
      const comp = {
        '==': (a, b) => a === b,
        '>=': (a, b) => a >= b,
        '<=': (a, b) => a <= b,
      };
      const compFn = comp[operator];
      await waitForFunction(async () => {
        const elements = await querySelectorsAll(step.selectors, frame);
        return compFn(elements.length, count);
      }, timeout);
    }

    async function querySelectorsAll(selectors, frame) {
      for (const selector of selectors) {
        const result = await querySelectorAll(selector, frame);
        if (result.length) {
          return result;
        }
      }
      return [];
    }

    async function querySelectorAll(selector, frame) {
      if (!Array.isArray(selector)) {
        selector = [selector];
      }
      if (!selector.length) {
        throw new Error('Empty selector provided to querySelectorAll');
      }
      let elements = [];
      for (let i = 0; i < selector.length; i++) {
        const part = selector[i];
        if (i === 0) {
          elements = await frame.$$(part);
        } else {
          const tmpElements = elements;
          elements = [];
          for (const el of tmpElements) {
            elements.push(...(await el.$$(part)));
          }
        }
        if (elements.length === 0) {
          return [];
        }
        if (i < selector.length - 1) {
          const tmpElements = [];
          for (const el of elements) {
            const newEl = (await el.evaluateHandle(el => el.shadowRoot ? el.shadowRoot : el)).asElement();
            if (newEl) {
              tmpElements.push(newEl);
            }
          }
          elements = tmpElements;
        }
      }
      return elements;
    }

    async function waitForFunction(fn, timeout) {
      let isActive = true;
      const timeoutId = setTimeout(() => {
        isActive = false;
      }, timeout);
      while (isActive) {
        const result = await fn();
        if (result) {
          clearTimeout(timeoutId);
          return;
        }
        await new Promise(resolve => setTimeout(resolve, 100));
      }
      throw new Error('Timed out');
    }
})().catch(err => {
    console.error(err);
    process.exit(1);
});

参考:
chrome开发者工具-recorder-录制回放
Puppeteer 入门指引

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/68789.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一文学会所有Web应用安全知识点

据调查大部分网络安全事故是软件缺陷导致的网络攻击。由于应用程序代码漏洞和安全漏洞&#xff0c;我们的企业正在遭受越来越多的攻击。调查显示攻击者可以通过盗用凭证&#xff0c;攻击10个测试过的web应用程序中的9个&#xff0c;进行恶意软件注入以及网络钓鱼攻击。因此&…

QuTrunk与MindSpore量子神经网络初探

QuTrunk是启科量子开发和已经开源的一款量子编程框架软件产品&#xff0c;关于QuTrunk的详细介绍&#xff0c;用户可以访问启科的开发者社区站点详细了解&#xff0c;也可以进入github上此项目下进行查询。 QuTrunk开发框架是可以与第三方AI框架深度结合进行量子计算程序开发的…

安装 Unity 个人免费版

安装 Unity 个人免费版 正版是需要收费的&#xff0c;所以我们个人开发使用个人免费版就可以 一、下载Unity 1.打开unity官网 点击”下载Unity" 官网 https://unity.cn/releases 2.下拉&#xff0c;会有各种版本&#xff0c;进入Unity的下载页面&#xff0c;找到对应的版…

Nacos2.1.2+Seata1.5.2+Mysql8+SpringCloud+Feign实现分布式事务笔记

搭建环境可以使用docker或是手动自己构建 1.启动nacos服务&#xff0c;nacos/nacos登录http://nacos-ip:8848 2.修改Seata的配置文件&#xff0c;注册到nacos上&#xff1a; seata\conf\application.yml server:port: 7091spring:application:name: seata-serverlogging:con…

VS调试安卓Unity应用

最近遇到了一个bug&#xff0c;需要在安卓手机上实机调试才行&#xff0c;所以这里记录一下怎么用VS调试安卓应用。我使用的VS是2017&#xff0c;但是更新的2019等版本应该也是差不多的&#xff08;毕竟实际上依赖的是Visual Studio Tools for Unity插件&#xff09;。Unity版本…

管理RMAN备份_管理恢复目录(Recovery Catalog)

本章阐述如何管理RMAN恢复目录。Catalog是一个数据库模式&#xff0c;它包含一个或多个目标数据库的RMAN仓库数据。 1&#xff0e;RMAN恢复目录概述 本节阐述与管理恢复目录相关的基本概念。 1.1&#xff0e;RMAN恢复目录的目的 恢复目录是一个数据库模式&#xff0c;RMAN使…

OutOfMemory内存溢出问题排查

OutOfMemory内存溢出问题排查 错误 2022-12-06 00:43:53.296[,] [registrationTask1] ERROR o.s.s.support.TaskUtils$LoggingErrorHandler.handleError96 - Unexpected error occurred in scheduled task. java.lang.OutOfMemoryError: Java heap space 2022-12-06 00:42:49.…

密码技术学习一:密码

0、前言 本文是作者阅读《图解密码技术》时&#xff0c;学习总结的一些知识&#xff0c;在此推荐想要深入学习密码知识的同志&#xff0c;将此书作为入门书来看还是比较适合的。 1、密码技术简介 1.1加密与解密 在两者通信过程中&#xff0c;不想通信的内容被第三方知道&am…

在R语言中进行缺失值填充:估算缺失值

介绍 缺失值被认为是预测建模的首要障碍。因此&#xff0c;掌握克服这些问题的方法很重要。最近我们被客户要求撰写关于缺失值处理的研究报告&#xff0c;包括一些图形和统计输出。 估算缺失值的方法的选择在很大程度上影响了模型的预测能力。在大多数统计分析方法中&#xf…

有状态软件如何在 k8s 上快速扩容甚至自动扩容

概述 在传统的虚机/物理机环境里, 如果我们想要对一个有状态应用扩容, 我们需要做哪些步骤? 申请虚机/物理机安装依赖下载安装包按规范配置主机名, hosts配置网络: 包括域名, DNS, 虚 ip, 防火墙...配置监控 今天虚机环境上出现了问题, 是因为 RabbitMQ 资源不足. 手动扩容…

一夜之间火爆GitHub的JavaWeb轻量级开发面试教程,终于浮出水面

前言 学习是一种基础性的能力。然而&#xff0c;“吾生也有涯&#xff0c;而知也无涯。”&#xff0c;如果学习不注意方法&#xff0c;则会“以有涯随无涯&#xff0c;殆矣”。 学习就像吃饭睡觉一样&#xff0c;是人的一种本能&#xff0c;人人都有学习的能力。我们在刚出生…

[附源码]Python计算机毕业设计Django医院门诊管理信息系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

React组件通信

react因为组件化&#xff0c;使得组件间通信十分的重要。本文就来简单介绍一些常见的react组件间传递的内容。 我将归纳为以下几种关系来详述:父组件与子组件之间&#xff0c;子组件与父组件之间&#xff0c;发布者与订阅者模式&#xff08;context&#xff09;&#xff0c;兄弟…

Netty入门——基于NIO实现机器客服案例

Netty简单案例前言环境准备前置知识网络传输的几种实现方式BIO——同步阻塞IONIO——同步非阻塞IOAIO——异步非阻塞IO适用范围Netty简介特点核心组件使用场景运行简图案例简介关键代码客户端服务器端运行状况总结前言 最近学完了Netty&#xff0c;在这里关于Netty中实现NIO做…

【Shell 脚本速成】09、Shell 循环控制语句 for

目录 一、循环控制 break 语句 二、循环控制 continue 语句 三、循环控制 sleep 四、参数控制命令-shift 五、脚本退出命令 exit 总结 六、for 嵌套 6.1 for 嵌套 if 七、for 与数组 7.1 使用 for 循环遍历读出数组 7.2 使用 for 循环进行数组存值 一、循环控制 bre…

Zabbix6.0使用教程 (一)—zabbix新增功能介绍2

上一篇我们已经介绍了部分zabbix6.0的新增功能&#xff0c;这期我们将继续为家详细介绍下余下的zabbix6.0新增功能&#xff0c;大家可以往下看。 六、监控项 6.1 自动类型选择 监控项配置表单会自动建议匹配的信息类型&#xff0c;如果选定的监控项键值仅返回特定类型的数据…

迷宫逃离的问题-CoCube

ROS1云课→20迷宫不惑之A*大法&#xff08;一种虽古老但实用全局路径规划算法&#xff09; 将CoCube分别放入如下地图中的左侧&#xff0c;如何从右侧逃离&#xff1a; 需要算法&#xff1a;求解起点到终点的路径。 还需要什么&#xff1f; 参考&#xff1a; &#xff08;eng.…

目标检测算法——垃圾分类数据集汇总(附下载链接)

&#x1f384;&#x1f384;近期&#xff0c;小海带在空闲之余收集整理了一批垃圾分类数据集供大家参考。 整理不易&#xff0c;小伙伴们记得一键三连喔&#xff01;&#xff01;&#xff01;&#x1f388;&#x1f388; 目录 一、Kaggle 垃圾分类图片数据集 二、垃圾分类数据…

全国青少年软件编程等级考试Python标准解读(1_6级)

考核性质&#xff1a; 全国青少年软件编程等级考试标准&#xff08;Python语言&#xff09;由中国电子学会科普培训与应用推广中心和北京大学信息科学技术学院共同制定。由全国青少年电子信息科普创新联盟标准工作组开发&#xff0c;由中国电子学会普及工作委员会审核通过&…

央视主持人康辉再次出圈,一口气播出一个多小时不卡顿、零失误

说起中央电视台的主持人&#xff0c;曾经有好几个国字脸&#xff0c;不过随着时间的流逝&#xff0c;他们都消失在历史的长河里。如今的央视主持人队伍&#xff0c;康辉可以算得上天花板级别的人&#xff0c;他也因此俘获了一大批观众&#xff0c;成为最受欢迎的主持人。 专业科…