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是完全自己实现了浏览器整个生命周期管理,会更加复杂一些。

  • 30
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Playwright 是一种现代化的浏览器自动化工具,它可以用来录制和执行自动化测试脚本。以下是使用 Playwright 录制脚本的步骤: 1. 安装 Node.js 和 Playwright: ``` npm install -g playwright ``` 2. 启动录制工具: ``` npx playwright codegen ``` 3. 选择要录制的浏览器和操作系统,并打开浏览器: ``` const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch({ headless: false }); const context = await browser.newContext(); const page = await context.newPage(); await page.goto('https://example.com'); // 开始录制 })(); ``` 4. 在浏览器中进行操作,例如点击按钮或输入文本。 5. 停止录制并生成脚本,复制生成的代码并粘贴到测试脚本中。 6. 运行测试脚本: ``` node my-test.js ``` 这样就可以使用 Playwright 录制和执行自动化测试脚本了。 ### 回答2: Playwright是一个开源的自动化测试工具,它可以用于录制和执行浏览器自动化脚本。通过Playwright录制脚本,我们可以实现对Web应用程序的自动化操作和测试。 首先,我们需要安装Playwright。可以使用npm命令来安装Playwright的Node.js库,也可以使用pip命令来安装Python库。安装完成后,我们可以使用命令行或代码编辑器来编写脚本。 在开始录制脚本之前,需要先创建一个文件,比如script.js或者script.py。然后,我们可以使用Playwright提供的API来录制脚本。首先,需要导入Playwright库,并创建一个浏览器实例: const { webkit } = require('playwright'); (async () => { const browser = await webkit.launch(); const context = await browser.newContext(); const page = await context.newPage(); 接下来,我们可以在浏览器页面上执行各种操作,比如导航到指定的URL、填写表单、点击按钮、获取元素等等。我们可以使用page对象提供的方法来完成这些操作,比如: await page.goto('https://example.com'); await page.fill('input[name="username"]', 'myusername'); await page.click('button[type="submit"]'); const title = await page.title(); 在录制脚本期间,我们还可以使用Playwright开发工具,来查看浏览器页面的DOM结构和CSS样式。这样可以更方便地定位元素和编写脚本。 完成录制脚本后,我们可以保存脚本并执行它。执行脚本的方式取决于我们的需求,可以使用命令行工具传入脚本文件执行,也可以编写一个Node.js或Python程序来执行脚本。 总之,通过Playwright录制脚本,我们可以方便地实现对Web应用程序的自动化操作和测试。这样可以提高工作效率,减少人工操作的工作量,并确保应用程序的质量和稳定性。 ### 回答3: Playwright是一个强大的开源工具,用于录制、回放和调试Web应用程序的自动化脚本。使用Playwright录制脚本可以极大地提高开发人员的生产效率和测试质量。 首先,开发人员可以使用Playwright录制功能来快速生成自动化测试脚本。通过在浏览器中手动操作网页,Playwright能够记录下每一步操作,如点击、输入、滚动等。录制下来的脚本可以直接在后续的回放中使用,省去了开发人员手动编写脚本的时间和精力。 其次,Playwright录制脚本可以轻松地进行编辑和自定义。开发人员可以通过添加断言和条件语句来验证页面的正确性和功能的完整性。这样可以确保应用程序在各种场景下的稳定性和可靠性。 另外,Playwright还支持多种编程语言,包括JavaScript、TypeScript和Python等,这使得开发人员可以使用自己熟悉的语言编写和管理测试脚本。这种灵活性使得团队成员可以更好地合作,并且可以根据项目需要进行定制和扩展。 最后,Playwright还提供了丰富的调试工具,使得开发人员能够更好地跟踪和排查自动化测试脚本中的错误和问题。通过使用这些调试工具开发人员可以检查每一步操作的执行结果、输入的准确性和页面的状态,从而更好地了解脚本的执行流程和问题所在。 总之,Playwright录制脚本是一种高效且灵活的方式,可用于自动化测试和测试驱动开发。它能够大大提高开发人员的效率和测试质量,同时也提供了强大的调试功能,帮助开发人员更好地解决问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

taoli-qiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值