工具 – Playwright

前言

一年多前,我写了一篇 e2e 测试工具 Cypress

当时它的 npm 下载量是 e2e 测试工具里排最高的。

没曾想,短短一年间,它就被 Playwright 完全赶超了😱

Playwright 是谁?怎么这么厉害?

Playwright 大有来头,它是 Microsoft 开源的 e2e 测试工具。

团队是从 Google Puppeteer 过来的大佬。

因此它不仅仅是 e2e 测试工具,也可以向 Puppeteer 那样作为 headless browser 使用。

除此之外,它还支持很多语言,不仅有 JavaScript, TypeScript,后端语言 C#,Java,Python 它也都支持。

不得不说,这么强大,要不火都难啊。

本篇我会延用上一篇 Cypress 的例子,重新用 Playwright 实现一遍,体验一下 Playwright 的强大。

 

Playwright 环境

单元测试,我们通常会把测试代码和被测试代码放到一块,under 同一个项目。

而 e2e 测试则不一定,有些人 prefer 分开两个项目管理,有些人则会放到一块。

Playwright 俩都支持,本篇会采用放到一块的方式。

此外,Playwright 支持多平台,想使用 JS / TS 的话,就搭配 Node.js,想使用 C# 就搭配 .NET。

这意味着,我们甚至可以用 C# 写测试代码,然后去测试一个 Angular (非 C#) 的项目。

本篇会采用 JS / TS + Node.js 的方式。

 

Get Started

create project

创建一个项目,这里我用 Vite TypeScript,你也可以用其它的。

写一个简单的页面

.html

复制代码
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + TS</title>
  </head>
  <body>
    <form autocomplete="off">
      <input placeholder="First Name">
      <input placeholder="Last Name">
      <button>Submit</button>
      <p class="thank-you">Thank you! We have received your enquiry.</p>
    </form>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
View Code
复制代码

.scss

复制代码
* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
  &:focus {
    outline: none;
  }
  button {
    border-width: 0;
  }
}

body {
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;

  form {
    min-width: 320px;

    display: flex;
    flex-direction: column;
    gap: 16px;

    input {
      border-radius: 4px;
      border: 1px solid black;
      padding: 16px;
    }

    button {
      border-radius: 4px;
      background-color: pink;
      color: red;
      padding: 20px;
      font-size: 20px;
      text-transform: uppercase;
      letter-spacing: 1px;
    }

    .thank-you {
      line-height: 1.5;

      &:not(.shown) {
        display: none;
      }
    }
  }
}
View Code
复制代码

.ts

复制代码
import './style.scss'

document.querySelector('form')!.addEventListener('submit', e => {
  e.preventDefault();
  document.querySelector('.thank-you')!.classList.add('shown');
});
View Code
复制代码

执行 command

yarn run vite

效果

install Playwright

yarn create playwright

安装过程中,它会问 3 个问题

第一个是,e2e 测试代码文件要放在哪个 folders,默认是 /tests。

第二个是,要不要搭配 Github,每当 checkin 的时候自动做测试,我没有这个习惯,所以选了 No。

第三个是,要不要安装游览器,我已经安装过了,所以选 No,第一次安装的可以选 Yes,或者之后单独安装也可以

Playwright 主要使用的游览器是 Chromium (Chrome / Edge 底层游览器)、Firefox、WebKit (它不是 Safari 游览器,而是一个搭载 WebKit 渲染引擎和 JavaScriptCore JS 引擎的 Safari 环境模拟器)

通常做 e2e 测试,这三个主流游览器都会一起测试。

安装好后会多出 3 个 files

往后的测试文件通通放入 tests folder 里。

tests-example 只是一个参考例子,可以删掉。

playwright.config.ts 是配置 playwright 的文件。

write test and view result

把我们的测试代码写进 example.spec.ts 里

复制代码
import { test, expect } from '@playwright/test';

test('test form', async ({ page }) => {
  await page.goto('http://localhost:5173');                  // 访问我们的程序页面

  await expect(page.locator('.thank-you')).toBeHidden();     // 检查 thank you message 必须是 hidden

  await page.getByPlaceholder('First Name').fill('Derrick'); // 查找 input 'First Name' 输入 'Derrick'

  await page.getByPlaceholder('Last Name').fill('Yam');      // 查找 input 'Last Name' 输入 'Yam'

  await page.locator('button').click();                      // 点击 submit

  await expect(page.locator('.thank-you')).toBeVisible();    // 检查 thank you message 必须是 shown
});
复制代码

Playwright 的测试代码大致上就是 UI 操作,然后检查 UI 的变化。

当然它也可以发 request 检查资料是否输入到 database 等等。

反正人怎么 test 它就怎么 test 就对了。

执行 command

yarn dev
yarn playwright test 

效果

之所以是 3 passed,3 workers 是因为默认启动了 3 个游览器测试。(主:如果觉得 Firefox 没有必要的话,可以去 playwright.config.ts 把它移除)

如果觉得 command 的 result 太单调,可以接着执行这个 command

yarn playwright show-report

它会以页面的方式呈现之前的 result。(注:如果测试 failed,它会自动显示这个 report,如果成功默认不会额外显示这个 report)

另外呢,它还有一个更细节操控的模式,执行 command

yarn playwright test --ui

在执行 test command 时加上一个 --ui

它会打开一个 dialog,我们可以选定要执行的 test,可以看到每一行 expect 的执行过程,非常细,细到可以 debug 了。

Codegen の recording a test

不知道大家用过 Excel macro 没有,我依稀记得在还没有学习编程以前,我只会在 Excel 上写点 formula,用 macro 录制生成一些 Visual Basic 代码。

那时厂里有个资深工人会写 Visual Basic,我就觉得他好厉害啊。

好,回忆结束,回来。

Playwright 也有类似 marco 的功能,我们可以录制生成测试代码。

执行 command

yarn playwright codegen http://localhost:5173

它会 popup 2 个窗口

左边是我们的录制程序,右边是自动生成出来的测试代码。

用法很简单,当我们 hover 到 'First Name' input 时,它会显示相关的测试代码

点击后会自动生成一行测试代码

我们继续填写资料到 input 上

它会持续添加测试代码

最后

它会生成出 

用 recording 的方式 generate 测试代码,虽然可能不太精准,但体验还是挺好的,推荐大家去玩一玩。

 

常用招数

base URL

去 playwright.config.ts 加上 baseUrl

export default defineConfig({
  use: {
    baseURL: 'http://localhost:5173',
  },
});

效果

await page.goto('http://localhost:5173');  // before
await page.goto('/');                      // after

testMatch

Playwright 默认测试文件的路径是 tests/**/*.spec.ts

有时候我们更希望把 .spec.ts 和测试页面放到一块

可以这样配置

export default defineConfig({
  // testDir: './tests', 
  testMatch: ['/src/**/*.spec.ts', '/tests/**/*.spec.ts'],
  testIgnore: [],
});

把 testDir 改成 testMatch,然后写 Glob Patterns

要 skip 掉某些 file 或 folder,可以使用 testIgnore。

query element and doing something / check with element

输入字到 input

await page.locator('input').fill('Derrick');

check 是否可见

await expect(page.locator('.thank-you')).toBeHidden();

TODO

等我以后用到了才回来补。

 

HTML to PDF in .NET by Playwright

很多年前我写过一篇 Html to PDF

当时的做法是在 .NET 利用 Javascript.NodeJS 调用 Puppeteer 来 convert HTML to PDF。

非常麻烦的做法。后来 .NET 有了 port to Puppeteer 的 library -- Puppeteer Sharp,至少是少了 Node.js 这个环节。

如今有了 Playwright 连 Puppeteer 都不需要了。

Playwright 支持 .NET,同时它具备 headless browser 的特性 (类似 puppeteer),所以它可以直接 convert HTML to PDF。

invoice.html

复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Invoice</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      body {
        font-family: Arial, sans-serif;
        margin: 0;
        padding: 0;
        background-color: #f4f4f4;
      }

      .invoice {
        max-width: 800px;
        margin: 20px auto;
        padding: 20px;
        background-color: #fff;
        border-radius: 8px;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
      }

      .header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 20px;
      }

      .header h1 {
        margin: 0;
      }

      .company-info {
        text-align: left;
      }

      .invoice-info {
        text-align: right;
      }

      .client-info {
        margin: 20px 0;
      }

      .items-table {
        width: 100%;
        border-collapse: collapse;
        margin-bottom: 20px;
      }

      .items-table thead {
        background-color: #f4f4f4;
      }

      .items-table th,
      .items-table td {
        padding: 10px;
        border: 1px solid #ddd;
        text-align: left;
      }

      .items-table th {
        background-color: #e0e0e0;
      }

      .text-right {
        text-align: right;
      }

      tfoot td {
        font-weight: bold;
      }

      .footer {
        text-align: center;
        margin-top: 20px;
        font-size: 0.9em;
        color: #666;
      }
    </style>
  </head>
  <body>
    <div class="invoice">
      <!-- Header Section -->
      <div class="header">
        <h1>Invoice</h1>
        <div class="company-info">
          <h2>Electronic Instruments Co., Ltd.</h2>
          <p>123 Tech Avenue, Silicon Valley, CA 94043</p>
          <p>Email: contact@eico.com | Phone: (123) 456-7890</p>
        </div>
        <div class="invoice-info">
          <p>Invoice #: 001234</p>
          <p>Date: 2025-01-14</p>
          <p>Due Date: 2025-01-31</p>
        </div>
      </div>

      <!-- Client Information -->
      <div class="client-info">
        <h3>Billed To:</h3>
        <p>Company Name: ABC Tech Solutions</p>
        <p>Address: 456 Business Road, Tech City, TX 75001</p>
        <p>Email: billing@abctech.com</p>
      </div>

      <!-- Product Items Section -->
      <table class="items-table">
        <thead>
          <tr>
            <th>Item</th>
            <th>Description</th>
            <th>Quantity</th>
            <th>Unit Price</th>
            <th>Total</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>001</td>
            <td>Precision Oscilloscope</td>
            <td>2</td>
            <td>$1,200.00</td>
            <td>$2,400.00</td>
          </tr>
          <tr>
            <td>002</td>
            <td>Digital Multimeter</td>
            <td>5</td>
            <td>$300.00</td>
            <td>$1,500.00</td>
          </tr>
          <tr>
            <td>003</td>
            <td>Signal Generator</td>
            <td>1</td>
            <td>$1,000.00</td>
            <td>$1,000.00</td>
          </tr>
        </tbody>
        <tfoot>
          <tr>
            <td colspan="4" class="text-right">Subtotal:</td>
            <td>$4,900.00</td>
          </tr>
          <tr>
            <td colspan="4" class="text-right">Tax (10%):</td>
            <td>$490.00</td>
          </tr>
          <tr>
            <td colspan="4" class="text-right"><strong>Total:</strong></td>
            <td><strong>$5,390.00</strong></td>
          </tr>
        </tfoot>
      </table>

      <!-- Footer Section -->
      <div class="footer">
        <p>Thank you for your business!</p>
        <p>Please make payment to the above address by the due date.</p>
      </div>
    </div>
  </body>
</html>
View Code
复制代码

这是要 convert to PDF 的 HTML。

安装 Playwright

dotnet add package Microsoft.Playwright

program.cs

复制代码
using Microsoft.Playwright;

var rootPath = Path.Combine(AppContext.BaseDirectory, @"..\..\..\");

string htmlFilePath = Path.Combine(rootPath, "invoice.html");
string pdfFilePath = Path.Combine(rootPath, "invoice.pdf");

string rawHtml = File.ReadAllText(htmlFilePath);

using var playwright = await Playwright.CreateAsync();

await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Headless = true });

var page = await browser.NewPageAsync();

await page.SetContentAsync(rawHtml);

await page.PdfAsync(new PagePdfOptions
{
  Path = pdfFilePath,    // Output PDF file path
  Format = "A4",         // PDF format, e.g., A4, Letter
  PrintBackground = true // To include background colors/images in the PDF
});
复制代码

代码很简单,我就不解释了。

 

总结

Playwright 真的很强大,不愧是 Microsoft 出品,有那个水准,希望微软未来能多开源这类的项目。

 

posted @   兴杰  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体
历史上的今天:
2022-01-13 Azure – Azure Active Directory
点击右上角即可分享
微信分享提示