playwright录制脚本原理
Paywright录制工具UI
在上一篇博客中介绍了如何从0构建一款具备录制UI测试的小工具。此篇博客将从源码层面上梳理playwright录制原理。当打开playwright vscode插件时,点击录制按钮,会开启一个新浏览器,如下图所示,在新开浏览器页面上,有录制,查看等按钮。
查看vscode的源码,会看到有个recorder的folder,该folder下由react构建了一个应用的UI,执行npm run dev,在5173端口启动这样一个web应用,web应用的UI如下图所示,可以看到里面的录制按钮等和上图vscode插件大家的相同。从这里可以推断,recorder里面用react构建的componen被嵌入到了开启的浏览器中。
通过上一篇博客的介绍,我们知道,实现录制功能的核心原理是是在浏览器中注入了脚本,通过监听用户行为,并将用户行为转换为playwright的语法,从而实现录制脚本的能力。
Playwright的脚本注入
查看playwright得source code,在playwright-core/src/server/injected目录下就是注入脚本相关的内容。查看injected/recorder/recorder.ts脚本,在该脚本中在interface RecordTool中定义了大量操作页面元素的方法,例如onClick,onInput等。
在recorder.ts中,在document对象上添加了很多listener,如下图所示:
具体每个listener完成了哪些逻辑呢?以onInput为例子,下面的onInput方法的部分代码,可以看到,首先是获取Input的目标对象target,再依次判断Input的具体属性,例如时textarea,或者select,或者checkbox等。根据判断的结果返回不同的内容。
生成locator
下面是playwright中生产locator的一个function,可以看到通过注入脚本injectedScript._evaluator.begin()开始,这段代码的主要作用是为指定的HTML元素生成一个或多个唯一的CSS选择器,并返回相关的选择器和匹配的元素列表,以便用于自动化测试或其他需要唯一定位元素的场景。首先是初始化,开始评估选择器生成过程,启用ARIA缓存。如果选项中包含 forTextExpect,则会尝试为目标元素生成一个带有文本的选择器。否则,首先尝试在目标元素的父元素或影子宿主中找到符合特定角色(如按钮、链接等)的元素。然后根据是否允许多个选择器,生成一个或多个选择器,可能包含或不包含文本和CSS ID。生成locator结束后,使用Set去重生成的选择器列表,确保唯一性。最后返回结果。可以看到,为了生成合理的locator,playwright进行很多逻辑处理来保证生成locator的唯一性和合理性。
export function generateSelector(injectedScript: InjectedScript, targetElement: Element, options: GenerateSelectorOptions): { selector: string, selectors: string[], elements: Element[] } {injectedScript._evaluator.begin();beginAriaCaches();try {let selectors: string[] = [];if (options.forTextExpect) {let targetTokens = cssFallback(injectedScript, targetElement.ownerDocument.documentElement, options);for (let element: Element | undefined = targetElement; element; element = parentElementOrShadowHost(element)) {const tokens = generateSelectorFor(injectedScript, element, { ...options, noText: true });if (!tokens)continue;const score = combineScores(tokens);if (score <= kScoreThresholdForTextExpect) {targetTokens = tokens;break;}}selectors = [joinTokens(targetTokens)];} else {targetElement = closestCrossShadow(targetElement, 'button,select,input,[role=button],[role=checkbox],[role=radio],a,[role=link]', options.root) || targetElement;if (options.multiple) {const withText = generateSelectorFor(injectedScript, targetElement, options);const withoutText = generateSelectorFor(injectedScript, targetElement, { ...options, noText: true });let tokens = [withText, withoutText];// Clear cache to re-generate without css id.cacheAllowText.clear();cacheDisallowText.clear();if (withText && hasCSSIdToken(withText))tokens.push(generateSelectorFor(injectedScript, targetElement, { ...options, noCSSId: true }));if (withoutText && hasCSSIdToken(withoutText))tokens.push(generateSelectorFor(injectedScript, targetElement, { ...options, noText: true, noCSSId: true }));tokens = tokens.filter(Boolean);if (!tokens.length) {const css = cssFallback(injectedScript, targetElement, options);tokens.push(css);if (hasCSSIdToken(css))tokens.push(cssFallback(injectedScript, targetElement, { ...options, noCSSId: true }));}selectors = [...new Set(tokens.map(t => joinTokens(t!)))];} else {const targetTokens = generateSelectorFor(injectedScript, targetElement, options) || cssFallback(injectedScript, targetElement, options);selectors = [joinTokens(targetTokens)];}}const selector = selectors[0];const parsedSelector = injectedScript.parseSelector(selector);return {selector,selectors,elements: injectedScript.querySelectorAll(parsedSelector, options.root ?? targetElement.ownerDocument)};} finally {cacheAllowText.clear();cacheDisallowText.clear();endAriaCaches();injectedScript._evaluator.end();}
}
定义在injectedScript.js中的generateSelector方法,实际在recorder.ts中被调用。下图是recorder.ts中onMouseMove方法的完整代码。可以看到,通过注入脚本,playwright获取到了目标对象event,将event作为参数传入方法中,在生成selector部分就是通过injectedScript.generateSelector生成的。
onMouseMove(event: MouseEvent) {consumeEvent(event);let target: HTMLElement | null = this._recorder.deepEventTarget(event);if (!target.isConnected)target = null;if (this._hoveredElement === target)return;this._hoveredElement = target;let model: HighlightModel | null = null;let selectors: string[] = [];if (this._hoveredElement) {const generated = this._recorder.injectedScript.generateSelector(this._hoveredElement, { testIdAttributeName: this._recorder.state.testIdAttributeName, multiple: false });selectors = generated.selectors;model = {selector: generated.selector,elements: generated.elements,tooltipText: this._recorder.injectedScript.utils.asLocator(this._recorder.state.language, generated.selector),tooltipFooter: selectors.length > 1 ? `Click to select, right-click for more options` : undefined,color: this._assertVisibility ? '#8acae480' : undefined,};}if (this._hoveredModel?.selector === model?.selector)return;this._hoveredModel = model;this._hoveredSelectors = selectors;this._recorder.updateHighlight(model, true);}
上面大致解释了playwright如何通过注入脚本的方式来录制脚本,接下来看看playwright是如何启动浏览器的。
启动浏览器
在util/protocol-types-generator目录下的index.js文件中,可以看到调用了playwright.launch()方法可以加载启动不同的浏览器,例如chromium,firefox,webkit等。这里调用的还是playwright-core中的chromium包来launch浏览器的。
继续查看源代码,可以看到,在playwright-core/server/chromium目录下,有很多代码,这里就是playwright自己实现的管理整个浏览器生命周期的代码。
以上就是playwright实现录制的大致过程。当调用playwright的功能录制到页面内容后,再调用vscode插件的textedit对象,将生成的内容写入当前打开的测试文件中即可。playwright是一个非常强大的工具,源代码相对比较复杂,如果要快速理解如何通过注入脚本实现录制功能,可以参考上一篇博客,他们在实现思路上是一致的。上一遍博客直接调用puppeteer来启动浏览器,playwright是完全自己实现了浏览器整个生命周期管理,会更加复杂一些。
相关文章:

playwright录制脚本原理
Paywright录制工具UI 在上一篇博客中介绍了如何从0构建一款具备录制UI测试的小工具。此篇博客将从源码层面上梳理playwright录制原理。当打开playwright vscode插件时,点击录制按钮,会开启一个新浏览器,如下图所示,在新开浏览器页…...
awk脚本监控
awk脚本监控 使用脚本监控内存,cpu和硬盘的根目录,超过80%提示用户,写成函数库的行,每天早上 的8.50分,执行一次脚本 现在脚本中写需要的内容 cpuu () {aa$(top -b -n 1 |awk NR3 {printf "%.F",$2$4})if …...

Python高压电容导电体和水文椭圆微分
🎯要点 🎯二维热传导二阶偏微分方程 | 🎯调和函数和几何图曲率 | 🎯解潮汐波动方程 | 🎯解静止基态旋转球体流体运动函数 | 🎯水文空间插值 | 🎯流体流动模拟求解器 | 🎯随机算法解…...

微信小程序 引入MiniProgram Design失败
这tm MiniProgramDesign 是我用过最垃圾的框架没有之一 我按照官网的指示安装居然能安装不成功,牛! 这里说明我是用js开发的 到以上步骤没有报错什么都没有,然后在引入组件的时候报错 Component is not found in path “./miniprogram _npm/vant/weapp/button/index” (using…...

Java 8 Date and Time API
Java 8引入了新的日期和时间API,位于java.time包下,旨在替代旧的java.util.Date和java.util.Calendar类。新API更为简洁,易于使用,并且与Joda-Time库的一些理念相吻合。以下是Java 8 Date and Time API中几个核心类的简要概述&…...

pyppeteer模块经常使用的功能,相关操作案例
官方仓库地址:https://github.com/miyakogi/pyppeteer 官方文档地址:API Reference — Pyppeteer 0.0.25 documentation Selenium环境的相关配置比较繁琐,此外,有的网站会对selenium和webdriver进行识别和反爬,因此在…...

nginx+keepalived+tomcat集群实验
如遇星河 | nginx+keepalived高可用集群实验 木子87 | Keepalived+Nginx+Tomcat 实现高可用Web集群 环境 192.168.40.204 tomcat-1 192.168.40.138 tomcat-2 安装tomcat [root@bogon local]# vim /etc/profile 添加环境变量 JAVA_HOME=/usr/local/java PATH=$J…...
vue脚手架 axios的二次封装
目录 01 路由懒加载(重要) 02 axios在脚手架中的使用 03.axios的二次封装 04 组件缓存 01 路由懒加载(重要) 一次性导入会出现严重的问题 : 首屏卡顿 因为main.js中引入了router/index.js router/index.js又使用了import语句 静态的引入了每一个组件 导致了首屏卡顿 所以我…...

人机恋爱新趋势:与AI男友谈恋爱的甜蜜与挑战
"我曾经把ChatGPT当成工具,从未追过星,也没有嗑过CP。没想到,到了36岁,我竟然嗑上了AI男友。Open AI,你赢了。你不仅是最好的AI公司,还是乙女游戏公司。" 转行大龄互联网人,走遍20国…...

文生视频开源产品的一些调研(一)
笔者尝试AI视频生成的几个特点: 玄学prompt,每个视频的prompt可能也需要微调很多次,需要找到使用模型的最佳prompt词组合,不恰当的比喻,骑自行车,座位高度等都是人与车彼此熟悉玄学生成,因为需…...

一切前端概念,都是纸老虎
4、listener可以通过 store.getState() 得到当前状态。如果使用的是 React,这时可以触发重新渲染 View。 function listerner() { let newState store.getState(); component.setState(newState); } 对比 Flux 和 Flux 比较一下:Flux 中 Store 是…...

使用自签名 TLS 将 Dremio 连接到 MinIO
Dremio 是一个开源的分布式分析引擎,为数据探索、转换和协作提供简单的自助服务界面。Dremio 的架构建立在 Apache Arrow(一种高性能列式内存格式)之上,并利用 Parquet 文件格式实现高效存储。有关 Dremio 的更多信息,…...

嵌入式系统软件开发环境_2.一般架构
1.Eclipse框架 嵌入式系统软件开发环境是可帮助用户开发嵌入式软件的一组工具的集合,其架构的主要特征离不开“集成”问题,采用什么样的架构框架是决定开发环境优劣主要因素。Eclipse框架是当前嵌入式系统软件开发环境被普遍公认的一种基础环境框架。目…...

单门户上集成多种数据库查询入口
(作者:陈玓玏) 开源项目,欢迎star哦,https://github.com/tencentmusic/cube-studio 在一家公司,我们通常会有多种数据库,每种数据库因为其特性承担不同的角色,比如mysql这种轻量…...

华芯微特SWM34-使用定时器捕获快速解码EV1527编码
在无线应用领域,很多433Mhz和315Mhz的遥控器,红外探测器,门磁报警器,无线门铃等都使用EV1527编码格式来发射数据。发射和接收均有对应的RF芯片完成,而且成本极低(目前市场价3毛钱不到)。接收芯片…...

小程序安卓手机点击uni-data-select 下拉框选择器会出现蓝色阴影
解决方法:在导入的包中找到uni-data-select.vue,接着找到.uni-stat__select样式,把cursor: pointer去掉。 如果出现穿透问题,uni-select__selector的z-index加高,默认是2。...

playwright vscode 插件源码解析
Playwright vscode插件主要功能 Playwright是微软开发的一款主要用于UI自动化测试的工具,在vscode中上安装playwright vscode插件,可以运行,录制UI自动化测试。 playwright vscode插件主要包括两块功能,功能一是在Test Explorer中…...

Mysql: SQL-DDL
一.SQL通用语法 1.SQL可以单行或者多行书写,以分号结尾。 2.SQL语句可以使用空格/缩进来增强语句的可读性。 3.MySQL数据库的SQL语句不区分大小写,关键字建议用大写。 4.注释: 单行注释:注释内容或#注释内容(Mysql特有) 多行注释:/*注释内容*/ 二.SQL分类 1.D…...
Java中的加密与解密:实现安全的数据传输
Java中的加密与解密:实现安全的数据传输 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!在当今信息安全至关重要的时代,保护数据的安全性…...
基于SSM的美食推荐系统
文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SSM的美食推荐系统,java项目。 ecli…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...

9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...

Android写一个捕获全局异常的工具类
项目开发和实际运行过程中难免会遇到异常发生,系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler,它是Thread的子类(就是package java.lang;里线程的Thread)。本文将利用它将设备信息、报错信息以及错误的发生时间都…...