<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CN">
    <title></title>
    <link rel="self" type="application/atom+xml" href="https://playwright.itest.info/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://playwright.itest.info"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-02-15T00:00:00+00:00</updated>
    <id>https://playwright.itest.info/atom.xml</id>
    <entry xml:lang="zh-CN">
        <title>Playwright 测试最佳实践：Fixtures + Page Object Model 完美结合</title>
        <published>2026-02-15T00:00:00+00:00</published>
        <updated>2026-02-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/advance-po-with-fixture-python-version-md/"/>
        <id>https://playwright.itest.info/blog/advance-po-with-fixture-python-version-md/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/advance-po-with-fixture-python-version-md/">&lt;p&gt;之前有分享过&lt;a href=&quot;&#x2F;blog&#x2F;building-scalable-playwright-tests-with-fixtures-and-page-object-model&#x2F;&quot;&gt;typescript的版本&lt;&#x2F;a&gt;，现在分享一下如何 python用 Fixtures 优雅实现 Page Object Model设计模式。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bei-ce-xiang-mu&quot;&gt;被测项目&lt;&#x2F;h2&gt;
&lt;p&gt;还是用&lt;a href=&quot;https:&#x2F;&#x2F;usememos.com&#x2F;&quot;&gt;memo&lt;&#x2F;a&gt;这个笔记工具，这次我们写2个用例，分别是&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;登录&lt;&#x2F;li&gt;
&lt;li&gt;创建笔记&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;xiang-mu-jie-gou&quot;&gt;项目结构&lt;&#x2F;h2&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;根目录&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;├── conftest.py          ← 定义fixtures的地方
&lt;&#x2F;span&gt;&lt;span&gt;├── pages&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;│   └── login_page.py    ← 定义page的地方
&lt;&#x2F;span&gt;&lt;span&gt;│   └── home_page.py
&lt;&#x2F;span&gt;&lt;span&gt;├── tests&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;│   └── test_xxx.py      ← 用例写在这里
&lt;&#x2F;span&gt;&lt;span&gt;└── ...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;pages&quot;&gt;pages&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;login-page-zhu-ce-he-deng-lu-ye&quot;&gt;login page(注册和登录页)&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# login_page.py
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;color:#8cdaff;&quot;&gt;LoginPage&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;__init__&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_up_btn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Sign up&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_in_btn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Sign in&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;navigate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;sign_up&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&#x2F;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;sign_up:
&lt;&#x2F;span&gt;&lt;span&gt;            url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&#x2F;auth&#x2F;signup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(url, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;wait_until&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;domcontentloaded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;sign_up&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navigate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;sign_up&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(username)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.password.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(password)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_up_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;sign_in&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navigate&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(username)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.password.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(password)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_in_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;home-page-chuang-jian-he-cha-kan-memode-ye-mian&quot;&gt;home page(创建和查看memo的页面)&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# home_page.py
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;color:#8cdaff;&quot;&gt;HomePage&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;__init__&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.textarea &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Any thoughts...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.save_btn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Save&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.more_btn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide-ellipsis-vertical&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).first
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.pin_option &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menuitem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Pin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.edit_option &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menuitem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.delete_option &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menuitem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Delete&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.unpin_btn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide-bookmark&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.published_at &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;relative-time&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.smile_btn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide.lucide-smile-plus&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).first
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.delete_btn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dialog&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Delete&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.upload &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;input[type=&amp;quot;file&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).first
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;navigate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;wait_until&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;domcontentloaded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.textarea.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;create_post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navigate&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.textarea.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(content)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.save_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;pin_post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.more_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.pin_option.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.pin_option.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;unpin_post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.unpin_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.unpin_btn.first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;reaction_btn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;reaction&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        btn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dialog&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_text&lt;&#x2F;span&gt;&lt;span&gt;(reaction)
&lt;&#x2F;span&gt;&lt;span&gt;        btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;btn
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;add_reaction&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;reaction&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.published_at.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;hover&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.smile_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.smile_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;reaction_btn&lt;&#x2F;span&gt;&lt;span&gt;(reaction).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;edit_post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.more_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.edit_option.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.edit_option.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Any thoughts...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;nth&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(content)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Save&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;nth&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;delete_post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.more_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.edit_option.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.delete_option.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.delete_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.delete_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;upload_file&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;file_path&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.upload.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;set_input_files&lt;&#x2F;span&gt;&lt;span&gt;(file_path)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.save_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;conftest-py&quot;&gt;conftest.py&lt;&#x2F;h2&gt;
&lt;p&gt;conftest.py 是 pytest 用来存放“共享 fixture、钩子函数、插件配置”的特殊文件，它能让很多测试相关的设置和准备工作在多个测试文件之间共享，而不需要重复写代码。&lt;&#x2F;p&gt;
&lt;p&gt;它是 pytest 中最重要、最常用的“全局&#x2F;局部配置与共享机制”之一。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conftest-py-de-wei-zhi-jue-ding-liao-ta-de-zuo-yong-fan-wei-fei-chang-zhong-yao&quot;&gt;conftest.py 的位置决定了它的作用范围（非常重要！）&lt;&#x2F;h3&gt;
&lt;p&gt;位置不同，影响的测试范围完全不一样：&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;文件位置&lt;&#x2F;th&gt;&lt;th&gt;作用范围&lt;&#x2F;th&gt;&lt;th&gt;典型场景&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;项目根目录下的 conftest.py&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;全局&lt;&#x2F;strong&gt;：影响项目下所有测试&lt;&#x2F;td&gt;&lt;td&gt;浏览器 fixture、日志、数据库连接&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;tests&#x2F; 目录下的 conftest.py&lt;&#x2F;td&gt;&lt;td&gt;只影响 tests&#x2F; 目录及其子目录的所有测试&lt;&#x2F;td&gt;&lt;td&gt;整个测试套件的通用 fixture&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;tests&#x2F;api&#x2F; 下的 conftest.py&lt;&#x2F;td&gt;&lt;td&gt;只影响 tests&#x2F;api&#x2F; 及其子目录&lt;&#x2F;td&gt;&lt;td&gt;只给 API 测试用的 token、mock server&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;tests&#x2F;ui&#x2F;login&#x2F; 下的 conftest.py&lt;&#x2F;td&gt;&lt;td&gt;只影响 tests&#x2F;ui&#x2F;login&#x2F; 目录&lt;&#x2F;td&gt;&lt;td&gt;登录相关的页面对象、已登录 fixture&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;xiao-jie-yi-ju-hua-ji-zhu-conftest-py-de-ben-zhi&quot;&gt;小结：一句话记住 conftest.py 的本质&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;conftest.py = pytest 的“共享配置 + 公共的数据准备 + 全局钩子”文件&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ce-shi-yong-li&quot;&gt;测试用例&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# test_memo.py
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;playwright.sync_api &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;expect
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test_login&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;login_page&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    login_page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navigate&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    login_page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sign_in&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;demo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;demo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 判断登录成功之后，页面应该跳转到http:&#x2F;&#x2F;localhost:5230&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(login_page.page).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;to_have_url&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test_create_memo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;authenticated_page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;home_page&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    before_count &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;home_page.published_at.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    home_page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;create_post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;hi, there&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(home_page.published_at).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;to_have_count&lt;&#x2F;span&gt;&lt;span&gt;(before_count &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;test_create_memo&lt;&#x2F;code&gt;的断言有点难理解。&lt;&#x2F;p&gt;
&lt;p&gt;这里的思路是先拿到创建post之前页面上的post数量，创建成功之后，页面上的post数量应该是+1的。&lt;&#x2F;p&gt;
&lt;p&gt;这里其实有个问题，如果页面上存量的post比较多的话，那么哪怕是再创建几个，页面上展示出来的post数量也是不会变化的，大家可以想想这是为什么。&lt;&#x2F;p&gt;
&lt;p&gt;解决思路其实有2个&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;每次跑用例之前清理一下数据库，保证数据库里post小于1页，这样就没有问题了&lt;&#x2F;li&gt;
&lt;li&gt;用api去拿post的数量，但是有可能存在数据写进去了，但是页面上的显示不发生变化的风险&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;kuai-su-shang-shou-checklist&quot;&gt;快速上手 checklist&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
创建 &lt;code&gt;pages&#x2F;&lt;&#x2F;code&gt; 目录，放所有 Page 类&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
创建 &lt;code&gt;conftest.py&lt;&#x2F;code&gt; 文件，定义 fixture&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
把常用前置操作（登录、导航）尽量封装成 fixture&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Page 类里多放组合方法（login、addToCart、checkout 等）&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;祝你写出优雅、可维护的 Playwright 测试！🚀&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright 测试进阶：用 Fixtures 优雅实现 Page Object Model</title>
        <published>2026-02-14T00:00:00+00:00</published>
        <updated>2026-02-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/building-scalable-playwright-tests-with-fixtures-and-page-object-model/"/>
        <id>https://playwright.itest.info/blog/building-scalable-playwright-tests-with-fixtures-and-page-object-model/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/building-scalable-playwright-tests-with-fixtures-and-page-object-model/">&lt;p&gt;在 Playwright 项目中，传统 Page Object Model 很容易导致代码重复和维护困难。&lt;&#x2F;p&gt;
&lt;p&gt;本文手把手教你如何利用 Playwright 的自定义 Fixtures，把 Page 对象自动注入测试，彻底消除每次使用page前都要手动去 &lt;code&gt;new&lt;&#x2F;code&gt; 的烦恼。&lt;&#x2F;p&gt;
&lt;p&gt;让测试代码更简洁、可复用、可扩展。&lt;&#x2F;p&gt;
&lt;p&gt;跟着做完，你的自动化测试框架将提升一个档次！特别适合用在大型的项目里面。&lt;&#x2F;p&gt;
&lt;p&gt;预计耗时：20–40 分钟&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-li-jie-liang-chong-chang-jian-xie-fa-ji-qi-wen-ti&quot;&gt;1. 理解两种常见写法及其问题&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;传统 POM 写法&lt;&#x2F;strong&gt;（大家最开始都会这么写）&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; pages&#x2F;LoginPage.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;LoginPage {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;example.com&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;login.spec.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;LoginPage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;pages&#x2F;LoginPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;应该能正常登录&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;LoginPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 每份测试都要 new 一次
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testuser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;example.com&#x2F;home&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;痛点&lt;&#x2F;strong&gt;（项目大了之后特别明显）：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;每份测试都要写 &lt;code&gt;new XXXPage(page)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;改一次构造函数 → 所有测试文件都要改&lt;&#x2F;li&gt;
&lt;li&gt;想做「已登录状态」的前置条件 → 每个测试重复写登录代码&lt;&#x2F;li&gt;
&lt;li&gt;测试文件看起来很“重”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;2-playwright-tui-jian-de-xian-dai-xie-fa-ba-page-object-zuo-cheng-fixture&quot;&gt;2. Playwright 推荐的现代写法：把 Page Object 做成 Fixture&lt;&#x2F;h3&gt;
&lt;p&gt;核心思路：&lt;br &#x2F;&gt;
&lt;strong&gt;不要在测试里手动 new Page Object，而是让 fixture 帮你创建并注入&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bu-zou-1-bao-chi-page-object-lei-bu-bian-huo-shao-zuo-diao-zheng&quot;&gt;步骤 1 – 保持 Page Object 类不变（或稍作调整）&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; pages&#x2F;LoginPage.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;LoginPage {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 保持 public 方便 fixture 里访问 page.url() 等
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locators &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    loginBtn: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;example.com&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fillCredentials&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locators&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locators&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;submit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locators&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginBtn&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 常用组合动作（可选）
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fillCredentials&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;submit&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;bu-zou-2-chuang-jian-zi-ding-yi-fixture-zui-he-xin-de-yi-bu&quot;&gt;步骤 2 – 创建自定义 fixture（最核心的一步）&lt;&#x2F;h4&gt;
&lt;p&gt;新建文件：&lt;code&gt;fixtures&#x2F;index.ts&lt;&#x2F;code&gt;（或 &lt;code&gt;utils&#x2F;fixtures.ts&lt;&#x2F;code&gt;）&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; fixtures&#x2F;index.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;LoginPage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;pages&#x2F;LoginPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 可以继续扩展其他 page 对象
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span&gt;MyFixtures &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;LoginPage;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; inventoryPage: InventoryPage;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; cartPage: CartPage;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;MyFixtures&amp;gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 普通注入（页面刚打开的状态）
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;LoginPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 交给测试使用，用完自动清理（如果有 teardown 逻辑）
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 进阶：自动完成登录的 fixture（非常实用）
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; authenticatedLoginPage: async ({ page }, use) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;   const loginPage = new LoginPage(page);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;   await loginPage.login(&amp;#39;standard_user&amp;#39;, &amp;#39;secret_sauce&amp;#39;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;   await use(loginPage);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;bu-zou-3-zai-ce-shi-wen-jian-zhong-shi-yong-chao-ji-gan-jing-de-xie-fa&quot;&gt;步骤 3 – 在测试文件中使用超级干净的写法&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;login.spec.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;fixtures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 注意路径
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;普通登录流程验证&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;loginPage &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fillCredentials&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testuser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;submit&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;home&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 更推荐的写法：把组合动作放在 Page 类里
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用封装的 login 方法&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;loginPage &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testuser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;example.com&#x2F;home&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;3-jin-jie-yong-fa-tui-jian-qiang-lie-jian-yi-zhang-wo&quot;&gt;3. 进阶用法推荐（强烈建议掌握）&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;3-1-zi-dong-deng-lu-fixture-zui-chang-yong-chang-jing&quot;&gt;3.1 自动登录 fixture（最常用场景）&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; fixtures&#x2F;index.ts 继续扩展
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;MyFixtures&amp;gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;... &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;loggedInPage&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;loginPage &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;standard_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;secret_sauce&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 可选：等待页面稳定
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;inventory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这里注入 page，也可以注入 loginPage
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;测试里直接用已登录状态：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;已登录用户能看到商品列表&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;loggedInPage &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loggedInPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.inventory_list&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;3-2-duo-ge-page-objects-yi-qi-shi-yong&quot;&gt;3.2 多个 Page Objects 一起使用&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span&gt;MyFixtures &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;LoginPage;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inventoryPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;InventoryPage;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cartPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;CartPage;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;MyFixtures&amp;gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;... &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;inventoryPage&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;InventoryPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 可以依赖其他 fixture
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;cartPage&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;inventoryPage &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 假设从 inventory 页加商品后进入 cart
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inventoryPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;addFirstItemToCart&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inventoryPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;gotoCart&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;CartPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inventoryPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;4-zong-jie-wei-shen-me-tui-jian-zhe-chong-xie-fa&quot;&gt;4. 总结：为什么推荐这种写法？&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;技能点&lt;&#x2F;th&gt;&lt;th&gt;传统 new Page() 写法&lt;&#x2F;th&gt;&lt;th&gt;使用 Fixture + POM 写法&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;重复 new&lt;&#x2F;td&gt;&lt;td&gt;每个测试都要写&lt;&#x2F;td&gt;&lt;td&gt;只写一次（在 fixture 里）&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;构造函数修改&lt;&#x2F;td&gt;&lt;td&gt;所有测试文件改&lt;&#x2F;td&gt;&lt;td&gt;只改 fixture 文件&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;前置登录&lt;&#x2F;td&gt;&lt;td&gt;每个测试重复写登录代码&lt;&#x2F;td&gt;&lt;td&gt;做成 loggedInPage fixture，一行搞定&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;可读性&lt;&#x2F;td&gt;&lt;td&gt;中等&lt;&#x2F;td&gt;&lt;td&gt;很高（测试里只看到业务动作）&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;扩展性&lt;&#x2F;td&gt;&lt;td&gt;差（文件越来越多越乱）&lt;&#x2F;td&gt;&lt;td&gt;很好（fixtures 集中管理）&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;执行速度&lt;&#x2F;td&gt;&lt;td&gt;每次都重新创建对象&lt;&#x2F;td&gt;&lt;td&gt;Playwright 缓存复用（同 worker 内）&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;5-kuai-su-shang-shou-checklist&quot;&gt;5. 快速上手 checklist&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
创建 &lt;code&gt;pages&#x2F;&lt;&#x2F;code&gt; 目录，放所有 Page 类&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
创建 &lt;code&gt;fixtures&#x2F;index.ts&lt;&#x2F;code&gt;，用 &lt;code&gt;test.extend()&lt;&#x2F;code&gt; 定义 fixture&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
测试文件里 &lt;code&gt;import { test, expect } from &#x27;..&#x2F;fixtures&#x27;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
把常用前置操作（登录、导航）尽量做成 fixture&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Page 类里多放组合方法（login、addToCart、checkout 等）&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;祝你写出优雅、可维护的 Playwright 测试！🚀&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title> 使用 playwright-smart-reporter 打造智能、带AI分析的测试报告 </title>
        <published>2026-02-14T00:00:00+00:00</published>
        <updated>2026-02-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-smart-reporter/"/>
        <id>https://playwright.itest.info/blog/playwright-smart-reporter/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-smart-reporter/">&lt;p&gt;playwright-smart-reporter 是一个专为 Playwright 测试框架打造的智能 HTML 报告工具。&lt;&#x2F;p&gt;
&lt;p&gt;它比 Playwright 自带的 HTML reporter 更现代化、更智能，主要目标是帮助团队更快定位问题、减少调试时间、提升测试稳定性。&lt;&#x2F;p&gt;
&lt;p&gt;仓库地址（持续更新）：&lt;br &#x2F;&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;qa-gary-parker&#x2F;playwright-smart-reporter&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;qa-gary-parker&#x2F;playwright-smart-reporter&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;项目支持python和javascript，我们先拿js版本来体验一下。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;di-yi-bu-an-zhuang&quot;&gt;第一步：安装&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Node.js 项目（最常用方式）
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; install&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -D&lt;&#x2F;span&gt;&lt;span&gt; playwright-smart-reporter
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 如果你使用 Python + pytest-playwright
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pip&lt;&#x2F;span&gt;&lt;span&gt; install playwright-smart-reporter-python
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# （Python 版文档请看仓库 python&#x2F;README.md，本教程主要讲 JS&#x2F;TS 版）
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;di-er-bu-zui-jian-pei-zhi-3fen-zhong-shang-shou&quot;&gt;第二步：最简配置（3分钟上手）&lt;&#x2F;h4&gt;
&lt;p&gt;在 &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;.js&lt;&#x2F;code&gt; 中添加 reporter：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ... 其他配置 ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  reporter: [
&lt;&#x2F;span&gt;&lt;span&gt;    [
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-smart-reporter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      {
&lt;&#x2F;span&gt;&lt;span&gt;        outputFile: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report&#x2F;smart-report.html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 建议放在报告目录
&lt;&#x2F;span&gt;&lt;span&gt;        historyFile: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report&#x2F;test-history.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 很重要！用于历史趋势、flaky检测
&lt;&#x2F;span&gt;&lt;span&gt;        maxHistoryRuns: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;12&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 保留最近12次运行的历史
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    ],
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 你可以同时保留官方的html reporter（可选）
&lt;&#x2F;span&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { outputFolder: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, open: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;never&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}],
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 强烈建议开启 trace（很多高级功能依赖它）
&lt;&#x2F;span&gt;&lt;span&gt;  use: {
&lt;&#x2F;span&gt;&lt;span&gt;    trace: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;retain-on-failure&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 或 &amp;#39;on&amp;#39; 如果你想每个测试都保留
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; screenshot: &amp;#39;only-on-failure&amp;#39;,  &#x2F;&#x2F; 配合使用效果更好
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;运行测试：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;结束后打开生成的报告：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright show-report playwright-report&#x2F;smart-report.html
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 或者直接浏览器打开文件
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;你应该能看到一个带有侧边栏、仪表盘、健康评分、失败聚类、趋势图的现代报告界面。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;di-san-bu-tui-jian-de-shi-yong-pei-zhi-ri-chang-tui-jian&quot;&gt;第三步：推荐的实用配置（日常推荐）&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span&gt;reporter: [
&lt;&#x2F;span&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-smart-reporter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 核心输出
&lt;&#x2F;span&gt;&lt;span&gt;    outputFile: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report&#x2F;smart-report.html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    historyFile: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report&#x2F;history.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    maxHistoryRuns: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; AI 失败分析（强烈推荐）
&lt;&#x2F;span&gt;&lt;span&gt;    enableAIRecommendations: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 稳定性 &amp;amp; 性能相关
&lt;&#x2F;span&gt;&lt;span&gt;    enableStabilityScore: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    enableRetryAnalysis: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    performanceThreshold: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.25&lt;&#x2F;span&gt;&lt;span&gt;,           &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 比历史平均慢25%报警
&lt;&#x2F;span&gt;&lt;span&gt;    enableFailureClustering: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 界面与体验
&lt;&#x2F;span&gt;&lt;span&gt;    filterPwApiSteps: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,               &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 隐藏大量 page.click() 等底层步骤，只看 test.step()
&lt;&#x2F;span&gt;&lt;span&gt;    enableTraceViewer: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    enableNetworkLogs: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    enableGalleryView: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; CI 友好
&lt;&#x2F;span&gt;&lt;span&gt;    runId: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;GITHUB_RUN_ID &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI_PIPELINE_ID &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;local&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    projectName: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;e2e-ui&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 多项目时建议区分
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 通知（可选）
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; slackWebhook: process.env.SLACK_WEBHOOK_URL,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 阈值微调（可选，根据团队实际情况调整）
&lt;&#x2F;span&gt;&lt;span&gt;    thresholds: {
&lt;&#x2F;span&gt;&lt;span&gt;      flakinessStable:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.08&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      flakinessUnstable: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.25&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      performanceRegression: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.20&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      stabilityWeightFlakiness: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.40&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      stabilityWeightPerformance: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.30&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      stabilityWeightReliability: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.30&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  }],
&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;di-si-bu-kai-qi-ai-shi-bai-fen-xi-zui-you-jia-zhi-de-gong-neng-zhi-yi&quot;&gt;第四步：开启 AI 失败分析（最有价值的功能之一）&lt;&#x2F;h4&gt;
&lt;p&gt;设置以下任一环境变量（优先级：Claude &amp;gt; OpenAI &amp;gt; Gemini）&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 推荐 Claude（分析质量较好）
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ANTHROPIC_API_KEY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;sk-ant-xxx...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 或者 OpenAI
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;OPENAI_API_KEY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;sk-xxx...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 或者 Google Gemini
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;GEMINI_API_KEY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;xxx...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;重新跑测试 → 失败的case下方会出现 AI 给出的可能原因 + 修复建议（批量分析，适合大测试套件）。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;di-wu-bu-ci-ji-cheng-rang-li-shi-shu-ju-chi-xu-ji-lei&quot;&gt;第五步：CI 集成（让历史数据持续积累）&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;strong&gt;GitHub Actions 示例&lt;&#x2F;strong&gt;（最常见场景）&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Cache history file
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;cache@v4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report&#x2F;history.json
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test-history-${{ github.ref }}
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;restore-keys&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test-history-
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run Playwright tests
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test --reporter=playwright-smart-reporter
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Save history cache (always)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;cache&#x2F;save@v4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report&#x2F;history.json
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test-history-${{ github.ref }}-${{ github.run_id }}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Upload smart report
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;upload-artifact@v4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;smart-test-report
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report&#x2F;smart-report.html
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;其他 CI（GitLab、CircleCI、Azure 等）也有类似缓存逻辑，详见官方文档 CI Integration 部分。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;di-liu-bu-chang-yong-gao-ji-ji-qiao-su-cha&quot;&gt;第六步：常用高级技巧速查&lt;&#x2F;h4&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;目标&lt;&#x2F;th&gt;&lt;th&gt;配置项 &#x2F; 命令&lt;&#x2F;th&gt;&lt;th&gt;备注&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;隐藏底层 pw:api 步骤&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;filterPwApiSteps: true&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;报告更干净&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;只看最近10次历史的趋势&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;maxHistoryRuns: 10&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;默认10&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;打开本地带 trace 的报告服务器&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;npx playwright-smart-reporter-serve smart-report.html&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;解决 file:&#x2F;&#x2F; 跨域问题&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;合并分片运行的历史&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;npx playwright-smart-reporter-merge-history ...&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;CI 分片后使用&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;严格 CSP 环境（公司内网常见）&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;cspSafe: true&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;用文件引用而非 base64&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;查看单个 trace 文件&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;npx playwright-smart-reporter-view-trace traces&#x2F;xxx.zip&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;调试用&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;性能回归报警阈值调低&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;performanceThreshold: 0.15&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;更敏感&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;只关心 UI 项目历史&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;projectName: &quot;ui-tests&quot;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;避免 api&#x2F;ui 混在一起统计&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h4 id=&quot;chang-jian-wen-ti-kuai-su-ding-wei&quot;&gt;常见问题快速定位&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;没有历史数据 &#x2F; 趋势图为空&lt;&#x2F;strong&gt; → 检查 &lt;code&gt;historyFile&lt;&#x2F;code&gt; 是否正确，CI 是否缓存成功&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;没有 AI 建议&lt;&#x2F;strong&gt; → 检查是否设置了 API Key，且失败用例存在&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;报告文件超大、浏览器卡&lt;&#x2F;strong&gt; → 试试 &lt;code&gt;cspSafe: true&lt;&#x2F;code&gt; 或减小 &lt;code&gt;maxHistoryRuns&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;flaky 判断跟官方不一样&lt;&#x2F;strong&gt; → 正常，Smart Reporter 是基于&lt;strong&gt;多轮历史&lt;&#x2F;strong&gt;而非单次重试&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;网络请求没显示&lt;&#x2F;strong&gt; → 确认 &lt;code&gt;trace: &#x27;retain-on-failure&#x27;&lt;&#x2F;code&gt; 或 &lt;code&gt;&#x27;on&#x27;&lt;&#x2F;code&gt; 已开启&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-smart-reporter&#x2F;2026-02-14-19-33-57.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;zong-jie-jian-yi-de-qi-bu-lu-jing&quot;&gt;总结 — 建议的起步路径&lt;&#x2F;h4&gt;
&lt;ol&gt;
&lt;li&gt;先用最简配置跑一次，感受现代仪表盘和健康评分&lt;&#x2F;li&gt;
&lt;li&gt;加上 AI Key，体验自动失败分析&lt;&#x2F;li&gt;
&lt;li&gt;把 &lt;code&gt;history.json&lt;&#x2F;code&gt; 纳入 CI 缓存 → 几天后趋势图和 flaky 检测开始生效&lt;&#x2F;li&gt;
&lt;li&gt;根据团队痛点逐步打开 network logs、gallery、step timeline 等功能&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Happy testing! 🚀&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>新的Agent Skills! Playwright CLI 初体验</title>
        <published>2026-02-01T00:00:00+00:00</published>
        <updated>2026-02-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/try-playwright-cli/"/>
        <id>https://playwright.itest.info/blog/try-playwright-cli/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/try-playwright-cli/">&lt;p&gt;Playwright最新推出了&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright-cli&quot;&gt;playwright cli&lt;&#x2F;a&gt;工具和相应的skills，可以适配大部分的AI coding agent。&lt;&#x2F;p&gt;
&lt;p&gt;这是1个官方提供的轻量级命令行工具，主要用来在终端直接控制浏览器执行自动化操作，而不需要自己写任何一行代码。&lt;&#x2F;p&gt;
&lt;p&gt;简单来说，它就是把 Playwright 最常用的几十个操作封装成了命令行指令，让AI agent可以用非常简短的命令来操控浏览器。&lt;&#x2F;p&gt;
&lt;p&gt;经过简单的试用，我发现时代真的变了，现在让agent干活基本就是几行像敲命令一样的短句，token省一大堆，调试也不用自己亲自动手。&lt;&#x2F;p&gt;
&lt;p&gt;估计80%简单的日常网页任务都可以直接搞定。&lt;&#x2F;p&gt;
&lt;p&gt;下面来看一下Playwright cli的真正实力吧。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-pin-jiao-cheng&quot;&gt;视频教程&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;bzhan&quot;&gt;B站&lt;&#x2F;h3&gt;
&lt;iframe src=&quot;&#x2F;&#x2F;player.bilibili.com&#x2F;player.html?isOutside=true&amp;aid=115994936743793&amp;bvid=BV11t64BJEDZ&amp;cid=35751857376&amp;p=1&quot; scrolling=&quot;no&quot; border=&quot;0&quot; frameborder=&quot;no&quot; framespacing=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;h3 id=&quot;youtube&quot;&gt;YouTube&lt;&#x2F;h3&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;vska068YCs4?si=n3jgUZ36FEz253QZ&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;h2 id=&quot;qian-zhi-tiao-jian&quot;&gt;前置条件&lt;&#x2F;h2&gt;
&lt;p&gt;我使用的是&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;anthropics&#x2F;claude-code&quot;&gt;claude code&lt;&#x2F;a&gt;和 &lt;a href=&quot;https:&#x2F;&#x2F;docs.bigmodel.cn&#x2F;cn&#x2F;guide&#x2F;models&#x2F;text&#x2F;glm-4.7&quot;&gt;GLM4.7&lt;&#x2F;a&gt;模型。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-zhuang-plawyright-cli&quot;&gt;安装plawyright cli&lt;&#x2F;h2&gt;
&lt;p&gt;plawyright cli的安装非常方便，直接用npm就好了。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;npm install -g @playwright&#x2F;cli@latest
&lt;&#x2F;span&gt;&lt;span&gt;playwright-cli --help
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;an-zhuang-skills&quot;&gt;安装skills&lt;&#x2F;h2&gt;
&lt;p&gt;在cluade code里，我们可以直接使用plgin来安装&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;&#x2F;plugin marketplace add microsoft&#x2F;playwright-cli
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;plugin install playwright-cli
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;shi-yong-playwright-clilai-ce-shi-memo&quot;&gt;使用playwright cli来测试memo&lt;&#x2F;h2&gt;
&lt;p&gt;我们的测试对象是memo。&lt;&#x2F;p&gt;
&lt;p&gt;这是1个简单的笔记工具，之前的视频里有介绍过，这里就不展开了，就把它看成是一个自建的微博就好了。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;try-playwright-cli&#x2F;2026-02-01-15-58-38.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ju-ti-guo-cheng&quot;&gt;具体过程&lt;&#x2F;h2&gt;
&lt;p&gt;在命令行里运行&lt;code&gt;claude&lt;&#x2F;code&gt;命令，然后输入下面的提示词。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;使用playwright skills 完成 http:&#x2F;&#x2F;localhost:5230&#x2F; 的自动化操作。
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;这是memo平台，一个笔记系统。
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;首先进行注册流程的自动化操作。
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;对所有成功和失败的场景进行截图
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;截图的文件放在当前目录，方便我查看。
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;另外用中文进行输出。
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;运行后claude会自己去读取playwright cli给的skill信息，属于一边运行一边进化了。&lt;&#x2F;p&gt;
&lt;p&gt;我本来是想让claude code完成一个简单的注册操作的，而不是写测试用例。&lt;&#x2F;p&gt;
&lt;p&gt;因为用例写起来场景相对比较多，会比较费token，呃，不对，会比较难一点。&lt;&#x2F;p&gt;
&lt;p&gt;结果claude不忘初心，毕竟playwright是用来测试的，直接给我生成了一连串的用例，让我大开眼界。&lt;&#x2F;p&gt;
&lt;p&gt;接着就是自动生成代码的传统艺能了。&lt;&#x2F;p&gt;
&lt;p&gt;经过了一系列紧张的测试，claude生成了测试报告，并且给出了一些测试发现。&lt;&#x2F;p&gt;
&lt;p&gt;我没去仔细验证啊，不过一眼看上去好像还挺靠谱的。&lt;&#x2F;p&gt;
&lt;p&gt;相当惊艳的探索性测试结论。&lt;&#x2F;p&gt;
&lt;p&gt;因为playwright cli默认是以无头的方式后台运行的，所以需要把测试的结果截图保存一下。&lt;&#x2F;p&gt;
&lt;p&gt;验证一下截图，看上去也没啥问题。&lt;&#x2F;p&gt;
&lt;p&gt;playwright cli的命令是可以重放的，所以我让ai直接把用到的命令都放到1个shell脚本里。这样下一次再进行测试的时候直接执行shell脚本就可以了，高效且准确，还能省token，获得稳定的结果。&lt;&#x2F;p&gt;
&lt;p&gt;ai不仅照做，还给了一些注释和命令行的输出，又一次超出预期了。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;try-playwright-cli&#x2F;2026-02-01-15-59-51.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;接着再让ai生成readme文件，方便我们人类和ai以后进行查看和理解。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;try-playwright-cli&#x2F;2026-02-01-16-00-26.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;最后再增加输出html格式报告的功能，增强项目的可视化能力。&lt;&#x2F;p&gt;
&lt;p&gt;手动运行一下生成的shell脚本，执行的很顺利，没有问题，最后打开报告。&lt;&#x2F;p&gt;
&lt;p&gt;这个报告的样式还行，比较清爽，不算惊艳，中规中矩吧。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;try-playwright-cli&#x2F;2026-02-01-15-59-17.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h2&gt;
&lt;p&gt;一句话总结：claude code + glm4.7 + playwright cli真的非常强，让人印象相当深刻。&lt;&#x2F;p&gt;
&lt;p&gt;2026年了，用自然语言去编写自动化测试用例这个目标看来真的已经实现了。&lt;&#x2F;p&gt;
&lt;p&gt;当然，这仅限于简单的场景，在复杂场景下的表现我没有测试，所以大家理性看待就好。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>playwright 2026 实操教程</title>
        <published>2026-01-31T00:00:00+00:00</published>
        <updated>2026-01-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-tutorial-2026/"/>
        <id>https://playwright.itest.info/blog/playwright-tutorial-2026/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-tutorial-2026/">&lt;p&gt;2026年初，浏览器自动化基本上都是AI工具帮我们代劳了。&lt;&#x2F;p&gt;
&lt;p&gt;Selenium 系方案在面对现代网站的指纹检测、行为分析、WAF 时越来越吃力，而 &lt;strong&gt;Playwright&lt;&#x2F;strong&gt; 凭借原生多内核支持、极致 API 友好度、社区活跃，已经稳坐头把交椅。&lt;&#x2F;p&gt;
&lt;p&gt;今天我们就反其道而行之，从零开始，手写一套&lt;strong&gt;针对真实应用的自动化流程&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;我们的测试对象是开源自托管笔记服务 &lt;strong&gt;Memos&lt;&#x2F;strong&gt;（https:&#x2F;&#x2F;github.com&#x2F;usememos&#x2F;memos）。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bzhan-shi-pin&quot;&gt;B站视频&lt;&#x2F;h2&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;&#x2F;&#x2F;player.bilibili.com&#x2F;player.html?isOutside=true&amp;aid=115977303887884&amp;bvid=BV1L66FB4EiV&amp;cid=35683306523&amp;p=1&quot; scrolling=&quot;no&quot; border=&quot;0&quot; frameborder=&quot;no&quot; framespacing=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;h2 id=&quot;youtubeshi-pin&quot;&gt;YouTube视频&lt;&#x2F;h2&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;dorzYojypAA?si=3a_YGIFF72O2r_vV&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;h2 id=&quot;wei-shen-me-xuan-memos-zuo-demo&quot;&gt;为什么选 Memos 做 demo？&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;到2026年1月，它已经有 &lt;strong&gt;56k+ stars&lt;&#x2F;strong&gt;，社区非常活跃（最新稳定版 v0.25.3，2025年11月发布）&lt;&#x2F;li&gt;
&lt;li&gt;极简、隐私优先、自托管、无遥测、无广告、MIT 开源&lt;&#x2F;li&gt;
&lt;li&gt;部署简单（Docker 一行搞定），界面现代、响应式、支持手机&lt;&#x2F;li&gt;
&lt;li&gt;有完备的 &lt;strong&gt;REST API&lt;&#x2F;strong&gt;，但我们这次主要用 Playwright 走&lt;strong&gt;浏览器端&lt;&#x2F;strong&gt;操作，模拟真人用户行为（后续可对比 API 方式）&lt;&#x2F;li&gt;
&lt;li&gt;功能覆盖典型笔记场景：登录、写 memo、加标签、@提及、搜索、公开&#x2F;私有切换、附件上传等，非常适合拿来练手和展示 Playwright 的各种能力&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;memos-he-xin-gong-neng-kuai-su-guo-yi-bian-wo-men-jiang-zi-dong-hua-qi-zhong-de-yi-xie-cao-zuo&quot;&gt;Memos 核心功能快速过一遍（我们将自动化其中的一些操作）&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;原子化短笔记（memo）：每条像数字便签，支持 Markdown（代码、表格、任务列表、链接预览）&lt;&#x2F;li&gt;
&lt;li&gt;标签（#work #idea）、@提及、内部链接，方便知识联网&lt;&#x2F;li&gt;
&lt;li&gt;可见性控制：公开 &#x2F; 仅自己 &#x2F; 保护（密码）&lt;&#x2F;li&gt;
&lt;li&gt;搜索 + 标签过滤 + 日历视图&lt;&#x2F;li&gt;
&lt;li&gt;附件上传（图片、文件）&lt;&#x2F;li&gt;
&lt;li&gt;归档、置顶、删除、恢复&lt;&#x2F;li&gt;
&lt;li&gt;暗黑模式 + 移动端友好&lt;&#x2F;li&gt;
&lt;li&gt;自托管后数据完全本地掌控（SQLite&#x2F;MySQL&#x2F;PostgreSQL）&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;an-zhuang-memo&quot;&gt;安装memo&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;快速自建一个 Memos 测试环境&lt;&#x2F;strong&gt;（推荐 Docker，5 分钟内搞定）：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docker&lt;&#x2F;span&gt;&lt;span&gt; run&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -d &lt;&#x2F;span&gt;&lt;span&gt;\
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;  --name&lt;&#x2F;span&gt;&lt;span&gt; memos-demo \
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;  -p&lt;&#x2F;span&gt;&lt;span&gt; 5230:5230 \
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;  -v &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;memos-data:&#x2F;var&#x2F;opt&#x2F;memos \
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;  --restart&lt;&#x2F;span&gt;&lt;span&gt; unless-stopped \
&lt;&#x2F;span&gt;&lt;span&gt;  neosmemo&#x2F;memos:stable
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;启动后访问 &lt;code&gt;http:&#x2F;&#x2F;localhost:5230&lt;&#x2F;code&gt;，注册第一个账号，就能得到一个干净的实例。我们后续所有自动化代码都针对这个本地 Memos 来写。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-zhuang-playwright-python-ban-2026nian-tui-jian-fang-shi&quot;&gt;安装 Playwright（Python 版，2026年推荐方式）&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 1. 升级 pip（规避很多隐蔽问题）
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pip&lt;&#x2F;span&gt;&lt;span&gt; install&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --upgrade&lt;&#x2F;span&gt;&lt;span&gt; pip
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 2. 安装核心 + pytest 插件（写测试用很方便）
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pip&lt;&#x2F;span&gt;&lt;span&gt; install playwright pytest-playwright
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 3. 下载浏览器（约 300–500MB，建议开代理&#x2F;科学网络）
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt; install
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 省空间只装 chromium：
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# playwright install chromium
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;快速验证安装成功：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;playwright.sync_api &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;sync_playwright
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sync_playwright&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;p:
&lt;&#x2F;span&gt;&lt;span&gt;    browser &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;p.chromium.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;headless&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;browser.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;new_page&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 改成你的 Memos 地址
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;())                 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 应该看到 &amp;quot;Memos&amp;quot; 或类似标题
&lt;&#x2F;span&gt;&lt;span&gt;    browser.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;浏览器弹出并能访问本地 Memos → 环境就绪。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;deng-lu-yu-zhu-ce&quot;&gt;登录与注册&lt;&#x2F;h2&gt;
&lt;p&gt;注册和登录用到了几乎相同的元素，所以我们直接用Page Object模式来实现。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;jie-mian&quot;&gt;界面&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tutorial-2026&#x2F;2026-01-31-15-53-08.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;html&quot;&gt;html&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tutorial-2026&#x2F;2026-01-31-15-53-58.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-xian-dai-ma&quot;&gt;实现代码&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;re
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;playwright.sync_api &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Playwright, sync_playwright, expect
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;time
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;color:#8cdaff;&quot;&gt;AuthPage&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;__init__&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_up_btn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Sign up&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_in_btn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Sign in&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&#x2F;auth&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(username)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.password.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(password)
&lt;&#x2F;span&gt;&lt;span&gt;        time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_in_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_in_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;signup&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&#x2F;auth&#x2F;signup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(username)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.password.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(password)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_up_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;: Playwright) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;None&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    browser &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;playwright.chromium.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;headless&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;browser.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;new_context&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;new_page&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;sample&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;demo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    auth_page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;AuthPage&lt;&#x2F;span&gt;&lt;span&gt;(page)
&lt;&#x2F;span&gt;&lt;span&gt;    auth_page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;signup&lt;&#x2F;span&gt;&lt;span&gt;(username, password)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# auth_page.login(username, password)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Press Enter...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# ---------------------
&lt;&#x2F;span&gt;&lt;span&gt;    context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    browser.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sync_playwright&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;playwright:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(playwright)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;mian-deng-lu&quot;&gt;免登录&lt;&#x2F;h3&gt;
&lt;p&gt;免登录的原理是在浏览器里设置一个cookie，下面是示例代码，依然是po模式。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login_with_session&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;session&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;login....&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&#x2F;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;wait_until&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;domcontentloaded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;add_cookies&lt;&#x2F;span&gt;&lt;span&gt;([
&lt;&#x2F;span&gt;&lt;span&gt;        {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;user_session&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: session,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 必须与页面同源
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    ])
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;wait_until&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;domcontentloaded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;调用方式如下。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;: Playwright) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;None&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    browser &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;playwright.chromium.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;headless&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;browser.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;new_context&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;new_page&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;sample&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;demo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    auth_page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;AuthPage&lt;&#x2F;span&gt;&lt;span&gt;(page)
&lt;&#x2F;span&gt;&lt;span&gt;    auth_page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;login_with_session&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;在浏览器的Application -&amp;gt; Cookies里面能找到&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Press Enter...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# ---------------------
&lt;&#x2F;span&gt;&lt;span&gt;    context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    browser.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sync_playwright&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;playwright:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(playwright)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;chuang-jian-memo&quot;&gt;创建memo&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;jie-mian-1&quot;&gt;界面&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tutorial-2026&#x2F;2026-01-31-16-01-10.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;代码如下。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;create_memo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# create memo
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Any thoughts...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Any thoughts...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(content)
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Save&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这里要注意的是我们需要等待输入框出现之后再进行操作。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pin-he-unpin-di-1tiao-memo&quot;&gt;pin 和 unpin 第1条 memo&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;jie-mian-2&quot;&gt;界面&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tutorial-2026&#x2F;2026-01-31-16-02-44.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;代码如下&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;pin_and_unpin_memo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# pin
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide-ellipsis-vertical&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menuitem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Pin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# unpin
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide.lucide-bookmark&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这里如何定位3个小竖点比较困难，我是用的&lt;code&gt;css selector&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;也就是这行代码&lt;code&gt;page.locator(&#x27;.lucide-ellipsis-vertical&#x27;).first.click()&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;另外弹出的menu也要等待，毕竟代码运行的速度比浏览器动画的速度要快。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zeng-jia-reaction&quot;&gt;增加 reaction&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;jie-mian-3&quot;&gt;界面&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tutorial-2026&#x2F;2026-01-31-16-05-54.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;代码如下。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;add_reaction&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;reaction&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;relative-time&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;hover&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide.lucide-smile-plus&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide.lucide-smile-plus&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dialog&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dialog&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_text&lt;&#x2F;span&gt;&lt;span&gt;(reaction).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;使用的时候是这样的&lt;code&gt;add_reaction(page, &quot;❤️&quot;)&lt;&#x2F;code&gt;，可以点击任意的表情。&lt;&#x2F;p&gt;
&lt;p&gt;这里的难点是先要hover到一个元素上，然后再等待弹出框。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bian-ji-di-1tiao-memo&quot;&gt;编辑第1条 memo&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;jie-mian-4&quot;&gt;界面&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tutorial-2026&#x2F;2026-01-31-16-08-42.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;代码如下。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;edit_memo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide-ellipsis-vertical&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menuitem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Any thoughts...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;nth&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(content)
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Save&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;nth&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;page.get_by_role(&quot;textbox&quot;, name=&quot;Any thoughts...&quot;).nth(1)&lt;&#x2F;code&gt;这行代码是定位页面上第2个文本框，也就是编辑的那个多行文本框。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shan-chu&quot;&gt;删除&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;jie-mian-5&quot;&gt;界面&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tutorial-2026&#x2F;2026-01-31-16-10-17.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;删除是会弹出对话框的，要等待这个对话框出现才能继续操作。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;delete_memo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide-ellipsis-vertical&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menuitem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Delete&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dialog&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Are you sure you want to&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Delete&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;shang-chuan-wen-jian&quot;&gt;上传文件&lt;&#x2F;h2&gt;
&lt;p&gt;这里的原理是找到页面上隐藏的&lt;code&gt;input[type=&quot;file&quot;]&lt;&#x2F;code&gt;的控件，然后直接设置文件的绝对路径。&lt;&#x2F;p&gt;
&lt;p&gt;上传文件的时候会遇到系统对话框，这个处理起来比较麻烦，所以不要跟界面纠缠，直接操作隐藏控件就好。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;input[type=&amp;quot;file&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;set_input_files&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;pic.jpg&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Save&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;wan-zheng-dai-ma&quot;&gt;完整代码&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;re
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;playwright.sync_api &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Playwright, sync_playwright, expect
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;time
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;color:#8cdaff;&quot;&gt;AuthPage&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;__init__&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_up_btn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Sign up&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_in_btn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Sign in&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&#x2F;auth&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(username)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.password.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(password)
&lt;&#x2F;span&gt;&lt;span&gt;        time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_in_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_in_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;signup&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&#x2F;auth&#x2F;signup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.username.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(username)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.password.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(password)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.sign_up_btn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login_with_session&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;session&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;login....&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&#x2F;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;wait_until&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;domcontentloaded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;add_cookies&lt;&#x2F;span&gt;&lt;span&gt;([
&lt;&#x2F;span&gt;&lt;span&gt;            {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;user_session&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: session,
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 必须与页面同源
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        ])
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;wait_until&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;domcontentloaded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;create_memo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# create memo
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:5230&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Any thoughts...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Any thoughts...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(content)
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Save&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;pin_and_unpin_memo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# pin
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide-ellipsis-vertical&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menuitem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Pin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# unpin
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide.lucide-bookmark&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;add_reaction&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;reaction&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;relative-time&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;hover&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide.lucide-smile-plus&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide.lucide-smile-plus&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dialog&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dialog&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_text&lt;&#x2F;span&gt;&lt;span&gt;(reaction).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;edit_memo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide-ellipsis-vertical&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menuitem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Any thoughts...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;nth&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(content)
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Save&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;nth&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;delete_memo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.lucide-ellipsis-vertical&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;menuitem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Delete&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dialog&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Are you sure you want to&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;wait_for&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Delete&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;: Playwright) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;None&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    browser &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;playwright.chromium.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;headless&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;browser.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;new_context&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;new_page&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;sample&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;demo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    auth_page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;AuthPage&lt;&#x2F;span&gt;&lt;span&gt;(page)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# auth_page.signup(username, password)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# auth_page.login(username, password)
&lt;&#x2F;span&gt;&lt;span&gt;    auth_page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;login_with_session&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;7-0baac506-9071-4d9b-ac72-158f83081c43&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# create_memo(page, &amp;quot;my sample&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# edit_memo(page, &amp;quot;✍️&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# edit_memo(page)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# add_reaction(page, &amp;quot;❤️&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;input[type=&amp;quot;file&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).first.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;set_input_files&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;pic.jpg&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get_by_role&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Save&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Press Enter...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# ---------------------
&lt;&#x2F;span&gt;&lt;span&gt;    context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    browser.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sync_playwright&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;playwright:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(playwright)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright v1.52 和 v1.53 有哪些新功能：AI 自动修复、可描述的定位器等！</title>
        <published>2025-06-27T00:00:00+00:00</published>
        <updated>2025-06-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/whats-new-in-playwright-v152-v153-fix-with-ai-describable-locators-and-more-dl9/"/>
        <id>https://playwright.itest.info/blog/whats-new-in-playwright-v152-v153-fix-with-ai-describable-locators-and-more-dl9/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/whats-new-in-playwright-v152-v153-fix-with-ai-describable-locators-and-more-dl9/">&lt;p&gt;来快速了解一下 Playwright v1.52 和 v1.53 的新特性吧！本次更新带来了一些强大的功能，特别是在调试和报告方面，大大提升了测试体验：&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;🧠 &lt;strong&gt;VS Code 中的 AI 自动修复功能&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 现在可以与 GitHub Copilot 深度集成，帮助你在 VS Code 中直接修复测试失败。当测试失败时，只需点击错误信息旁边的 ✨ 图标，或在测试资源管理器中悬停测试名称时点击图标。Playwright 会将足够的上下文信息提供给 Copilot，Copilot 会生成有针对性的修复建议。你可以查看、接受并重新运行，整个流程快速、高效，而且非常实用。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;media2.dev.to&#x2F;dynamic&#x2F;image&#x2F;width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto&#x2F;https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg8shs35tp2e33mtmejkm.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;想尝试这个功能？确保你已经安装并启用了 Playwright 的 VS Code 插件。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;🔎 &lt;strong&gt;locator.describe()：增强 Trace 和报告可读性&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;现在你可以通过 &lt;code&gt;.describe()&lt;&#x2F;code&gt; 为任何定位器添加更具可读性的描述：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newTodo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByPlaceholder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;What needs to be done?&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;新待办输入框&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newTodo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;买牛奶&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这些描述会出现在：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Trace Viewer 中&lt;&#x2F;li&gt;
&lt;li&gt;UI 模式下&lt;&#x2F;li&gt;
&lt;li&gt;HTML 测试报告中&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;media2.dev.to&#x2F;dynamic&#x2F;image&#x2F;width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto&#x2F;https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjsqapaixb7b0s7r6jdj.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这一小改动在调试复杂 UI 或团队协作时可以带来巨大的帮助。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;📊 &lt;strong&gt;HTML 报告支持自定义标题&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;你可以为测试报告添加更清晰的标题，配置如下：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  reporter: [[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { title: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;自定义测试运行 #1028&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}]],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;非常适合用于团队仪表板、CI 输出，或用于区分多个测试运行。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;✅ &lt;strong&gt;新增断言：&lt;code&gt;toContainClass&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;想验证某个元素是否包含特定 class？现在可以使用：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;listitem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Ship v1.52&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContainClass&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;done&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这种方式语法简洁、表达精准，尤其适用于基于 class 的 UI 状态检查。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;🧪 &lt;strong&gt;快照增强：支持 &lt;code&gt;children&lt;&#x2F;code&gt; 和 &lt;code&gt;url&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;ARIA 快照（通过 &lt;code&gt;toMatchAriaSnapshot&lt;&#x2F;code&gt;）现在支持：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&#x2F;children: equal&lt;&#x2F;code&gt; —— 确保子元素被包含在快照中&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&#x2F;url: &quot;https:&#x2F;&#x2F;playwright.dev&quot;&lt;&#x2F;code&gt; —— 匹配特定 URL&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;示例：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toMatchAriaSnapshot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;  - list
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    - &#x2F;children: equal
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    - listitem: Feature A
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    - listitem:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;      - link &amp;quot;Feature B&amp;quot;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;        - &#x2F;url: &amp;quot;https:&#x2F;&#x2F;playwright.dev&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这让 UI 快照测试在处理复杂或动态组件时更加可靠。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;⚙️ &lt;strong&gt;测试运行器更新&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;新增了以下便捷功能：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;testProject.workers&lt;&#x2F;code&gt; —— 每个测试项目可自定义并发数&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;failOnFlaky&lt;&#x2F;code&gt; —— 一旦检测到测试不稳定，可自动判定为失败&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;🆙 &lt;strong&gt;如何升级？&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;安装最新版本：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -D&lt;&#x2F;span&gt;&lt;span&gt; @playwright&#x2F;test@latest
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;同时别忘了将 VS Code 插件更新到最新版本，以便启用 AI 修复等新功能。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>为什么 Playwright 的追踪功能比日志更适合调试 UI 测试用例</title>
        <published>2025-06-11T00:00:00+00:00</published>
        <updated>2025-06-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/why-playwright-tracing-beats-logging-for-debugging-ui-tests-3ebe38d3dd61/"/>
        <id>https://playwright.itest.info/blog/why-playwright-tracing-beats-logging-for-debugging-ui-tests-3ebe38d3dd61/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/why-playwright-tracing-beats-logging-for-debugging-ui-tests-3ebe38d3dd61/">&lt;p&gt;当你的 Python UI 测试失败时，日志往往让你一头雾水。而 Playwright 的追踪功能能清晰展示发生了什么——包括截图、DOM 快照和完整上下文。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;why-playwright-tracing-beats-logging-for-debugging-ui-tests-3ebe38d3dd61&#x2F;img&#x2F;1750986296.19884591*ZxSXM0L8e9psCO4kUI2FIw.png&quot; alt=&quot;图像 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;本文内容包括：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;为什么 Playwright 日志不够用&lt;&#x2F;li&gt;
&lt;li&gt;追踪功能能带来什么&lt;&#x2F;li&gt;
&lt;li&gt;为什么 &lt;code&gt;--tracing=on&lt;&#x2F;code&gt; 不生效&lt;&#x2F;li&gt;
&lt;li&gt;如何在 Python 中手动启用追踪&lt;&#x2F;li&gt;
&lt;li&gt;GitHub Actions 的坑点（.开头的文件夹）&lt;&#x2F;li&gt;
&lt;li&gt;macOS 解压导致的奇怪问题&lt;&#x2F;li&gt;
&lt;li&gt;我的心得体会——以及进阶课程推荐&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;不久前，我在 LinkedIn 上做了一项关于 Web UI 测试工具的投票。&lt;&#x2F;p&gt;
&lt;p&gt;与此同时，我开始使用 Playwright，并很快发现了一个出奇有效的调试方式：&lt;strong&gt;内置追踪功能&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;乍一听可能觉得没什么，但相信我，我记得当年为了能在测试环境中实现基础的实时视频录制，付出了多少努力。直到现在，很多团队依然不使用视频或可追踪记录。最多只是在测试步骤中偶尔截图或输出一些日志。&lt;&#x2F;p&gt;
&lt;p&gt;在那之前，我花了很多时间使用 Selenium，甚至写过一篇关于如何用 Python 描述符清理 PageObject 的文章：&lt;&#x2F;p&gt;
&lt;p&gt;👉 &lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@sergey.shukalovich&#x2F;how-python-descriptors-clean-up-pageobjects-even-in-2025-12faa11b05b0&quot;&gt;Python 描述符如何清理 PageObject，即使到了 2025&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;但用了 Playwright 后我很快意识到：&lt;strong&gt;光靠日志已经远远不够了&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wei-shen-me-playwright-python-zhong-de-biao-zhun-ri-zhi-bu-gou-yong&quot;&gt;&lt;strong&gt;为什么 Playwright（Python）中的标准日志不够用？&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;我们来看一个简单的 UI 测试：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;@pytest.mark.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;smoke
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test_get_started_link&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    start_page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;StartPage&lt;&#x2F;span&gt;&lt;span&gt;(page)
&lt;&#x2F;span&gt;&lt;span&gt;    start_page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    start_page.get_started_link.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    api_reference_page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ApiReferencePage&lt;&#x2F;span&gt;&lt;span&gt;(page)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(api_reference_page.heading_label).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;to_be_visible&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;当这段测试运行时，Playwright 默认输出的日志如下：&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;2025-06-10T21:36:20.707Z pw:api navigating to &amp;quot;https:&#x2F;&#x2F;playwright.dev&#x2F;&amp;quot;, waiting until &amp;quot;load&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;2025-06-10T21:36:21.252Z pw:api locator resolved to &amp;lt;h1&amp;gt;Installation&amp;lt;&#x2F;h1&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;看起来很详细？但&lt;strong&gt;你试试看用这个来调试一个不稳定的 UI 测试&lt;&#x2F;strong&gt;：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;没有上下文&lt;&#x2F;strong&gt;说明哪一步逻辑失败了&lt;&#x2F;li&gt;
&lt;li&gt;信息都非常底层：滚动、DOM 事件、加载状态……&lt;&#x2F;li&gt;
&lt;li&gt;不知道&lt;strong&gt;断言失败点&lt;&#x2F;strong&gt;，也不知道涉及哪个 page object&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;没有截图、没有 DOM 快照、没有测试名称、没有可追溯性&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;比如你的测试失败在这句：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(api_reference_page.heading_label).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;to_be_visible&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;日志只会显示：&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;LocatorAssertions.to_be_visible with timeout 5000ms
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;除此之外——&lt;strong&gt;一无所知&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;你完全不知道：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;页面当时实际长啥样&lt;&#x2F;li&gt;
&lt;li&gt;是否导航完成&lt;&#x2F;li&gt;
&lt;li&gt;page object 是否初始化成功&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;zhui-zong-gong-neng-neng-dai-lai-shen-me&quot;&gt;&lt;strong&gt;追踪功能能带来什么？&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;我们再运行一次上面的测试——但这次启用 &lt;strong&gt;Playwright Tracing&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;你将获得：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;测试全过程的可交互时间线&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;每个操作的截图&lt;&#x2F;li&gt;
&lt;li&gt;每步执行前后的 DOM 快照&lt;&#x2F;li&gt;
&lt;li&gt;控制台输出、网络请求及响应信息&lt;&#x2F;li&gt;
&lt;li&gt;可视化标记&lt;strong&gt;哪一步失败&lt;&#x2F;strong&gt;，并给出完整上下文&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;你可以通过以下命令打开 trace：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt; show-trace traces&#x2F;trace.zip
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;或在线打开：&lt;&#x2F;p&gt;
&lt;p&gt;👉 &lt;a href=&quot;https:&#x2F;&#x2F;trace.playwright.dev&#x2F;&quot;&gt;https:&#x2F;&#x2F;trace.playwright.dev&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;效果大致如下：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;why-playwright-tracing-beats-logging-for-debugging-ui-tests-3ebe38d3dd61&#x2F;img&#x2F;1750986298.9059061*JhC_uc_KbcCxHVgPi2PwtQ.png&quot; alt=&quot;图像 3&quot; &#x2F;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;why-playwright-tracing-beats-logging-for-debugging-ui-tests-3ebe38d3dd61&#x2F;img&#x2F;1750986301.3168081*_KNtJLmxZ1NTMp8-5WeEMg.png&quot; alt=&quot;图像 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-bai-shi-de-zhui-zong-chang-zhe-yang&quot;&gt;&lt;strong&gt;失败时的追踪长这样&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;举个刻意失败的例子：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;@pytest.mark.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;smoke
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test_get_started_link&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    start_page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;StartPage&lt;&#x2F;span&gt;&lt;span&gt;(page)
&lt;&#x2F;span&gt;&lt;span&gt;    start_page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    start_page.get_started_link.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    api_reference_page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ApiReferencePage&lt;&#x2F;span&gt;&lt;span&gt;(page)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(api_reference_page.heading_label).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;not_to_be_visible&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;输出信息如下：&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;E AssertionError: Locator expected not to be visible
&lt;&#x2F;span&gt;&lt;span&gt;E Actual value: visible
&lt;&#x2F;span&gt;&lt;span&gt;E ...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这还是看不出：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;浏览器到底展示了什么&lt;&#x2F;li&gt;
&lt;li&gt;当前页面是哪一个&lt;&#x2F;li&gt;
&lt;li&gt;是什么导致了失败&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;而在 Playwright trace viewer 中，同样的失败是这样的：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;why-playwright-tracing-beats-logging-for-debugging-ui-tests-3ebe38d3dd61&#x2F;img&#x2F;1750986303.479961*TBcTO9FycGjPWKxAKfBeqw.png&quot; alt=&quot;图像 5&quot; &#x2F;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;why-playwright-tracing-beats-logging-for-debugging-ui-tests-3ebe38d3dd61&#x2F;img&#x2F;1750986305.7333881*PwepsfAGhhcZS-u61E9uig.png&quot; alt=&quot;图像 6&quot; &#x2F;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;why-playwright-tracing-beats-logging-for-debugging-ui-tests-3ebe38d3dd61&#x2F;img&#x2F;1750986307.997021*5sz_Njicm3yCWQMlaX423w.png&quot; alt=&quot;图像 7&quot; &#x2F;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;why-playwright-tracing-beats-logging-for-debugging-ui-tests-3ebe38d3dd61&#x2F;img&#x2F;1750986310.23279791*0Xa3vBTU4Zq-F-B5T-XL5w.png&quot; alt=&quot;图像 8&quot; &#x2F;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;why-playwright-tracing-beats-logging-for-debugging-ui-tests-3ebe38d3dd61&#x2F;img&#x2F;1750986312.39691111*0ygEVQaJJIKmmozuux44_w.png&quot; alt=&quot;图像 9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;不用再去啃那 20 多行模糊日志，你可以&lt;strong&gt;直观看到浏览器看到的内容，以及何时出现问题&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wei-shen-me-tracing-on-mei-you-sheng-cheng-trace-yi-ji-ru-he-jie-jue&quot;&gt;&lt;strong&gt;为什么 &lt;code&gt;--tracing=on&lt;&#x2F;code&gt; 没有生成 trace（以及如何解决）&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright 支持追踪功能，&lt;code&gt;pytest-playwright&lt;&#x2F;code&gt; 也提供了 CLI 参数：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pytest&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --tracing&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;on
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;看起来很简单？&lt;&#x2F;p&gt;
&lt;p&gt;但你运行后却发现：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;没有文件&lt;&#x2F;li&gt;
&lt;li&gt;没有警告&lt;&#x2F;li&gt;
&lt;li&gt;没有 trace&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;发生了什么？&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;原因是 &lt;code&gt;--tracing=on&lt;&#x2F;code&gt; 只是告诉 Playwright &lt;strong&gt;开始追踪&lt;&#x2F;strong&gt;，却&lt;strong&gt;没有告诉它停止并保存&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;除非你用的是 JavaScript 的测试 runner，&lt;strong&gt;Python 不会自动调用：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;context.tracing.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;stop&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;trace.zip&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;所以 trace 根本不会保存到磁盘。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhen-shi-de-bug-bao-gao&quot;&gt;&lt;strong&gt;真实的 bug 报告&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;不仅你遇到过这个问题，还有很多其他开发者：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;actions&#x2F;upload-artifact&#x2F;issues&#x2F;447&quot;&gt;actions&#x2F;upload-artifact#447&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright-pytest&#x2F;issues&#x2F;179&quot;&gt;playwright-pytest#179&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright-pytest&#x2F;issues&#x2F;129&quot;&gt;playwright-pytest#129&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;jie-jue-fang-fa-xie-yi-ge-zi-ji-de-fixture&quot;&gt;&lt;strong&gt;解决方法：写一个自己的 fixture&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;你可以在 &lt;code&gt;conftest.py&lt;&#x2F;code&gt; 中添加如下 fixture：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;TRACES_DIR_PATH &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;pathlib.Path.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cwd&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;joinpath&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;joinpath&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;traces&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@pytest.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fixture&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;scope&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;autouse&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;trace_browser_context&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;browser_context&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    ui_config &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;config[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    save_trace &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;ui_config.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getboolean&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;save_trace&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;save_trace:
&lt;&#x2F;span&gt;&lt;span&gt;        browser_context.tracing.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;start&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;screenshots&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;snapshots&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;sources&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;yield
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;save_trace:
&lt;&#x2F;span&gt;&lt;span&gt;        browser_context.tracing.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;stop&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;TRACES_DIR_PATH.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;joinpath&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;trace-&lt;&#x2F;span&gt;&lt;span&gt;{uuid.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;uuid4&lt;&#x2F;span&gt;&lt;span&gt;()}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.zip&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;确保你有 &lt;code&gt;config.ini&lt;&#x2F;code&gt; 配置，内容包含 &lt;code&gt;[ui] save_trace = true&lt;&#x2F;code&gt;，并且已定义 &lt;code&gt;browser_context&lt;&#x2F;code&gt; fixture。&lt;&#x2F;p&gt;
&lt;p&gt;这个 fixture 会：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;在每个测试开始时启动追踪&lt;&#x2F;li&gt;
&lt;li&gt;在测试结束后（不论成功或失败）保存为唯一命名的 &lt;code&gt;.zip&lt;&#x2F;code&gt; 文件&lt;&#x2F;li&gt;
&lt;li&gt;可在本地和 CI 环境中使用&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;bonus-ke-yi-jin-yi-bu-zi-ding-yi-trace-ce-lue&quot;&gt;&lt;strong&gt;Bonus：可以进一步自定义 trace 策略&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;你还可以扩展逻辑，比如：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;只在失败时保存 trace&lt;&#x2F;li&gt;
&lt;li&gt;按测试场次分文件夹保存&lt;&#x2F;li&gt;
&lt;li&gt;附加到 CI artifacts（见下文）&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;完整代码参考 GitHub 仓库：&lt;&#x2F;p&gt;
&lt;p&gt;👉 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;shukal94&#x2F;pwtracing&quot;&gt;shukal94&#x2F;pwtracing&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ci-ji-cheng-github-actions-pei-zhi&quot;&gt;&lt;strong&gt;CI 集成：GitHub Actions 配置&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;你希望 CI 能够：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;运行测试&lt;&#x2F;li&gt;
&lt;li&gt;收集追踪文件&lt;&#x2F;li&gt;
&lt;li&gt;上传为可下载的 artifacts&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;code&gt;.github&#x2F;workflows&#x2F;playwright.yml&lt;&#x2F;code&gt; 示例：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright Tests
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pull_request&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;timeout-minutes&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;60
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;checkout@v4
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Set up Python
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;setup-python@v4
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;python-version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;3.11&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Install dependencies
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          python -m pip install --upgrade pip
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          pip install -r requirements.txt
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Ensure browsers are installed
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;python -m playwright install --with-deps
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run tests
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pytest -m smoke tests&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;upload-artifact@v4
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ !cancelled() }}
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-traces
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;artifacts&#x2F;traces
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;⚠️ 注意：不要使用 &lt;code&gt;.test-results&#x2F;&lt;&#x2F;code&gt; 这类点开头的目录，否则 GitHub 会悄悄忽略。改用 &lt;code&gt;test-results&#x2F;&lt;&#x2F;code&gt; 或 &lt;code&gt;artifacts&#x2F;&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;详见：&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;actions&#x2F;upload-artifact&#x2F;issues&#x2F;447&quot;&gt;upload-artifact#447&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;macos-xiao-tie-shi-fang-zhi-trace-wen-jian-bei-zi-dong-jie-ya-po-pi&quot;&gt;&lt;strong&gt;macOS 小贴士：防止 trace 文件被自动解压破坏&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;macOS 中，Safari 和 Finder 会&lt;strong&gt;自动解压 zip 文件&lt;&#x2F;strong&gt;，这会破坏 trace 文件结构。&lt;&#x2F;p&gt;
&lt;p&gt;🛑 问题：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;你从 GitHub 下载 trace.zip → Safari 自动解压&lt;&#x2F;li&gt;
&lt;li&gt;解压文件夹中还有 zip → Finder 再次解压&lt;&#x2F;li&gt;
&lt;li&gt;最终文件零散，无法识别&lt;&#x2F;li&gt;
&lt;li&gt;viewer 提示错误：&lt;em&gt;“无法加载 trace. 请上传有效文件”&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;✅ 解决：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;关闭 Safari 的自动解压：Safari → 设置 → 通用 → 取消勾选“下载后打开安全文件”&lt;&#x2F;li&gt;
&lt;li&gt;使用终端手动解压&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;仅上传原始 &lt;code&gt;.zip&lt;&#x2F;code&gt; 文件至&lt;&#x2F;strong&gt; &lt;a href=&quot;https:&#x2F;&#x2F;trace.playwright.dev&#x2F;&quot;&gt;trace.playwright.dev&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;zong-jie-tracing-logging-er-zhe-shi-jian-hao-shi&quot;&gt;&lt;strong&gt;总结：Tracing ≠ Logging，而这是件好事&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;调试现代 UI 测试并不容易。&lt;&#x2F;p&gt;
&lt;p&gt;你可以花上几个小时啃日志、截图、反复猜测问题点 —— 或者，你可以&lt;strong&gt;直接打开 trace，看清一切&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 的追踪功能带来完整视角：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;浏览器看到什么&lt;&#x2F;li&gt;
&lt;li&gt;测试代码做了什么&lt;&#x2F;li&gt;
&lt;li&gt;哪一步出了问题&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;前提是你要&lt;strong&gt;正确开启和保存&lt;&#x2F;strong&gt;——现在你知道该怎么做了。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ri-zhi-yi-ran-zhong-yao&quot;&gt;日志依然重要&lt;&#x2F;h3&gt;
&lt;p&gt;追踪帮助你理解&lt;strong&gt;行为&lt;&#x2F;strong&gt;，但若你在构建&lt;strong&gt;自定义测试框架&lt;&#x2F;strong&gt;或希望工具更具可观测性，结构化、可读性强的日志仍然不可或缺。&lt;&#x2F;p&gt;
&lt;p&gt;这也是我新课程的主题：&lt;&#x2F;p&gt;
&lt;p&gt;👉 &lt;a href=&quot;https:&#x2F;&#x2F;www.udemy.com&#x2F;course&#x2F;logging-test-automation&#x2F;&quot;&gt;Python 测试自动化框架日志设计实战&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;适合所有使用 &lt;code&gt;pytest&lt;&#x2F;code&gt;、构建测试平台、或不想再用 print 语句 debug 的同学：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;学会设计清晰可调试的日志体系&lt;&#x2F;li&gt;
&lt;li&gt;为每条测试加上结构化上下文&lt;&#x2F;li&gt;
&lt;li&gt;将日志集成进框架、CI 与日常开发流程中&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;👉 &lt;a href=&quot;https:&#x2F;&#x2F;www.udemy.com&#x2F;course&#x2F;logging-test-automation&#x2F;?referralCode=EC3293CB22BD37543C41&quot;&gt;查看课程&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>使用 Grafana Loki 实现 Playwright API 日志集中管理</title>
        <published>2025-05-27T00:00:00+00:00</published>
        <updated>2025-05-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/centralized-playwright-api-logs-with-grafana-loki-368c21a76aba/"/>
        <id>https://playwright.itest.info/blog/centralized-playwright-api-logs-with-grafana-loki-368c21a76aba/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/centralized-playwright-api-logs-with-grafana-loki-368c21a76aba/">&lt;p&gt;&lt;strong&gt;Playwright&lt;&#x2F;strong&gt; 是一款强大的端到端和 API 测试工具，但在 CI 流水线中，有时我们写好的场景会出现失败，而搞清楚失败的原因却令人头疼。试想一下，如果你能将每一次请求和响应都记录到像 &lt;strong&gt;Grafana Loki&lt;&#x2F;strong&gt; 这样可搜索的集中系统中，会不会更容易调试？&lt;br &#x2F;&gt;
这篇文章将向你展示如何将 Loki 日志集成进你的 Playwright API 测试中，让你无需延长测试时间也能更快定位问题。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wei-shen-me-yao-ji-lu-api-ce-shi-ri-zhi&quot;&gt;为什么要记录 API 测试日志？&lt;&#x2F;h2&gt;
&lt;p&gt;API 测试失败的原因多种多样，比如授权头无效、请求体格式错误，或是后端本身出现波动。在本地单个运行场景时，我们可以很容易地定位问题；但一旦进入流水线并行执行，问题就变得难以排查了。&lt;br &#x2F;&gt;
因此，&lt;strong&gt;记录 API 测试数据显得尤为重要&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;通过记录这些日志，我们可以更快识别错误并解决它，提升调试效率。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;da-jian-loki-he-grafana&quot;&gt;搭建 Loki 和 Grafana&lt;&#x2F;h2&gt;
&lt;p&gt;Loki 是一个专为日志聚合设计的系统，由 Grafana Labs 开发，因此与 Grafana 的集成非常顺畅。Grafana 负责可视化和查询存储在 Loki 中的日志。&lt;br &#x2F;&gt;
在更新自动化测试中的日志器之前，我们先搭建 Loki 和 Grafana。&lt;br &#x2F;&gt;
我们可以使用 Docker 来启动 Loki 和 Grafana，官方文档中已经提供了 &lt;a href=&quot;https:&#x2F;&#x2F;grafana.com&#x2F;docs&#x2F;loki&#x2F;latest&#x2F;setup&#x2F;install&#x2F;docker&#x2F;&quot;&gt;docker-compose&lt;&#x2F;a&gt; 示例。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;loki&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;grafana&#x2F;loki:2.9.4
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;ports&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;3100:3100&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;-config.file=&#x2F;etc&#x2F;loki&#x2F;local-config.yaml
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;grafana&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;grafana&#x2F;grafana:10.3.3
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;ports&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;3000:3000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GF_SECURITY_ADMIN_USER=admin
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GF_SECURITY_ADMIN_PASSWORD=admin
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;depends_on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;loki
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;创建 &lt;code&gt;docker-compose.yml&lt;&#x2F;code&gt; 文件后，运行 &lt;code&gt;docker compose up -d&lt;&#x2F;code&gt; 即可以后台模式启动容器。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;centralized-playwright-api-logs-with-grafana-loki-368c21a76aba&#x2F;img&#x2F;1750994862.2839621*G7dZWO2dVunvpaQnZMEGVg.png&quot; alt=&quot;Image 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zai-grafana-tian-jia-shu-ju-yuan&quot;&gt;在 Grafana 添加数据源&lt;&#x2F;h3&gt;
&lt;p&gt;打开浏览器访问 &lt;em&gt;localhost:3000&lt;&#x2F;em&gt;，使用在 compose 文件中设定的用户名和密码登录。
进入 “Connection → Data Source”，点击“Add New Connection”，选择 &lt;strong&gt;Loki&lt;&#x2F;strong&gt; 作为数据源，URL 填写 &lt;a href=&quot;http:&#x2F;&#x2F;loki:3100&quot;&gt;http:&#x2F;&#x2F;loki:3100&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;请注意，这里的 &lt;code&gt;loki&lt;&#x2F;code&gt; 是 Docker 容器的名称，如果你修改了名字，记得也一起调整。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;chuang-jian-ri-zhi-ji-lu-gong-ju&quot;&gt;创建日志记录工具&lt;&#x2F;h2&gt;
&lt;p&gt;接下来我们为 Playwright 项目创建日志记录工具，项目示例可以在作者仓库中找到。&lt;&#x2F;p&gt;
&lt;p&gt;我们之前直接使用 Playwright 的 &lt;code&gt;request&lt;&#x2F;code&gt; 进行请求，需要每次手动添加日志逻辑，重复性太高。现在我们将封装成日志工具函数，并结合请求发送逻辑统一管理：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pendingLogs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;[] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;enqueueLog&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;promise&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pendingLogs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;promise&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;flushLogs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Promise&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;allSettled&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pendingLogs&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pendingLogs&lt;&#x2F;span&gt;&lt;span&gt;.length &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;日志内容会过滤敏感字段（如 authorization、token、apikey 等），并打上 &lt;code&gt;REDACTED&lt;&#x2F;code&gt; 标记：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;removeAuthOnHeader&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Record&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Record&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;redactedKeys &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;authorization&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api-key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sanitized&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Record&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{};
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sanitized&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;redactedKeys&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;toLowerCase&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[REDACTED]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;sanitized&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;通过 &lt;code&gt;logApiCallToLoki&lt;&#x2F;code&gt; 函数将请求和响应发送到 Loki，并支持添加标签（labels），例如环境变量和 job 名称：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createLoggedApiContext&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;APIRequestContext&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;originalFetch &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fetch &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;reqData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      method: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span&gt;?.method &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      url: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span&gt;?.headers,
&lt;&#x2F;span&gt;&lt;span&gt;      body: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span&gt;?.data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span&gt;{},
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;originalFetch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;call&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{ status: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;() };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseData&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;catch &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseData&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{};
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logging &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;logApiCallToLoki&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;reqData&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseData&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      environment: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ENV &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;local&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    }).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;catch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;e&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;warn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Loki failed to log the error:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;enqueueLog&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logging&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;tian-jia-quan-ju-teardown-que-bao-ri-zhi-fa-song-wan-cheng&quot;&gt;添加全局 Teardown 确保日志发送完成&lt;&#x2F;h2&gt;
&lt;p&gt;在测试结束时，我们需要确保所有日志已发送完毕，因此设置 &lt;code&gt;globalTeardown&lt;&#x2F;code&gt;：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;flushLogs &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;utils&#x2F;logQueue&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;globalTeardown&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Make sure all logs were sent to Loki&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;flushLogs&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;globalTeardown&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;别忘了在 &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; 中设置该 teardown。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zai-ce-shi-zhong-shi-yong-ri-zhi-ji-lu-gong-ju&quot;&gt;在测试中使用日志记录工具&lt;&#x2F;h2&gt;
&lt;p&gt;现在我们在测试中引入 &lt;code&gt;createLoggedApiContext&lt;&#x2F;code&gt; 替代原始 &lt;code&gt;request&lt;&#x2F;code&gt;：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;旧版代码：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;创建用户&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createUser&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;valid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;public&#x2F;v2&#x2F;users&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, { headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;header&lt;&#x2F;span&gt;&lt;span&gt;, data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;新版代码：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;创建用户&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ }&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createUser&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;valid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createLoggedApiContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;public&#x2F;v2&#x2F;users&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, { headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;header&lt;&#x2F;span&gt;&lt;span&gt;, data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;通过引入 helper，测试请求在发送后会自动构造日志并推送到 Loki，无需手动干预。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zai-grafana-zhong-ke-shi-hua-ri-zhi&quot;&gt;在 Grafana 中可视化日志&lt;&#x2F;h2&gt;
&lt;p&gt;一旦测试执行完成，即可在 Grafana 的 &lt;em&gt;Explore&lt;&#x2F;em&gt; 或 &lt;em&gt;Dashboard&lt;&#x2F;em&gt; 页面查看日志：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;centralized-playwright-api-logs-with-grafana-loki-368c21a76aba&#x2F;img&#x2F;1750994864.8593591*9wg4j-5c53BI-6Kw3hKb_g.png&quot; alt=&quot;Image 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;日志中包含完整的请求与响应信息，有助于排查错误。&lt;&#x2F;p&gt;
&lt;p&gt;目前示例只添加了 job 和环境名标签，你可以根据需要扩展更多元数据，比如测试用例 ID、构建号、版本等。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;调试缺乏可见性是非常痛苦的。&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;通过将 &lt;strong&gt;Grafana Loki&lt;&#x2F;strong&gt; 集成进 &lt;strong&gt;Playwright API 测试&lt;&#x2F;strong&gt;，你可以实现集中、结构化、可搜索的日志系统，而不会影响测试性能或执行时间。&lt;&#x2F;p&gt;
&lt;p&gt;无论你在本地还是 CI 中运行测试，这种方式都能帮助你更快定位问题、分析失败原因，并构建更具可观测性的测试流程。&lt;&#x2F;p&gt;
&lt;p&gt;测试，不再只是通过或失败，而是变得可视、可查、可控。&lt;&#x2F;p&gt;
&lt;p&gt;感谢阅读，祝测试顺利！&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>playwright1.50.0新特性介绍</title>
        <published>2025-02-15T00:00:00+00:00</published>
        <updated>2025-02-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/whats-new-in-playwright-v1-50-0-7026fac00401/"/>
        <id>https://playwright.itest.info/blog/whats-new-in-playwright-v1-50-0-7026fac00401/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/whats-new-in-playwright-v1-50-0-7026fac00401/">&lt;p&gt;Playwright v1.50.0 引入了多项值得关注的功能和改进，旨在提升测试体验。以下是这些新增功能的详细概述，并附有示例以说明其使用方法。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;runner-zeng-qiang&quot;&gt;Runner 增强&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;1-bu-zou-chao-shi-pei-zhi&quot;&gt;1. 步骤超时配置&lt;&#x2F;h3&gt;
&lt;p&gt;现在可以通过 timeout 选项为单个测试步骤指定最大运行时间。如果某个步骤的运行时间超过此限制，测试将失败。&lt;&#x2F;p&gt;
&lt;p&gt;示例：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;example test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;step with timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 此步骤必须在 1000 毫秒内完成
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#some-button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    { timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1000 &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;2-tiao-guo-ce-shi-bu-zou&quot;&gt;2. 跳过测试步骤&lt;&#x2F;h3&gt;
&lt;p&gt;新增的&lt;code&gt;test.step.skip()&lt;&#x2F;code&gt;方法允许跳过特定的测试步骤，这在某些条件未满足或功能尚未实现的情况下非常有用。&lt;&#x2F;p&gt;
&lt;p&gt;示例：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;some test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;before running step&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 正常步骤
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;skip&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;not yet ready&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 此步骤将被跳过
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;after running step&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 即使上一步被跳过，此步骤仍会运行
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;3-aria-kuai-zhao-cun-chu-yu-dan-du-wen-jian&quot;&gt;3. ARIA 快照存储于单独文件&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;expect(locator).toMatchAriaSnapshot()&lt;&#x2F;code&gt;方法已扩展，允许将 ARIA 快照存储在单独的 YAML 文件中，便于更好地组织和版本控制。&lt;&#x2F;p&gt;
&lt;p&gt;示例：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ARIA snapshot test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;example.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toMatchAriaSnapshot&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在此示例中，ARIA 快照将存储在对应的 YAML 文件中。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-ke-fang-wen-xing-cuo-wu-xiao-xi-duan-yan&quot;&gt;4. 可访问性错误消息断言&lt;&#x2F;h3&gt;
&lt;p&gt;新增方法&lt;code&gt;expect(locator).toHaveAccessibleErrorMessage()&lt;&#x2F;code&gt;，可用于断言某个定位器指向的元素具有特定的 ARIA 错误消息。&lt;&#x2F;p&gt;
&lt;p&gt;示例：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;accessible error message test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;example.com&#x2F;form&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#submit-button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveAccessibleErrorMessage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Username is required&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;5-kuai-zhao-geng-xin-pei-zhi&quot;&gt;5. 快照更新配置&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;testConfig.updateSnapshots&lt;&#x2F;code&gt;选项现在包含一个新的枚举值。将此选项设置为&lt;code&gt;changed&lt;&#x2F;code&gt;时，仅更新已更改的快照；设置为&lt;code&gt;all&lt;&#x2F;code&gt;时，无论是否有差异，都会更新所有快照。&lt;&#x2F;p&gt;
&lt;p&gt;命令行示例：&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;npx playwright test --update-snapshots=changed
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;6-yuan-dai-ma-geng-xin-fang-fa-pei-zhi&quot;&gt;6. 源代码更新方法配置&lt;&#x2F;h3&gt;
&lt;p&gt;新增选项&lt;code&gt;testConfig.updateSourceMethod&lt;&#x2F;code&gt;，定义在配置&lt;code&gt;testConfig.updateSnapshots&lt;&#x2F;code&gt;时如何更新源代码。可用模式包括 overwrite、3-way 和 patch。&lt;&#x2F;p&gt;
&lt;p&gt;命令行示例：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;npx playwright test --update-snapshots=changed --update-source-method=3way
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;7-web-fu-wu-qi-de-you-ya-guan-bi&quot;&gt;7. Web 服务器的优雅关闭&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;testConfig.webServer&lt;&#x2F;code&gt;选项现在包含一个&lt;code&gt;gracefulShutdown&lt;&#x2F;code&gt;字段，允许你指定一个进程终止信号，而不是默认的&lt;code&gt;SIGKILL&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;配置示例：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; playwright.config.js
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;exports &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  webServer: {
&lt;&#x2F;span&gt;&lt;span&gt;    command: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run start&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    port: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    gracefulShutdown: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;SIGTERM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;8-fang-wen-ce-shi-bu-zou-fu-jian&quot;&gt;8. 访问测试步骤附件&lt;&#x2F;h3&gt;
&lt;p&gt;现在在报告器 API 中公开了&lt;code&gt;testStep.attachments&lt;&#x2F;code&gt;属性，允许检索特定测试步骤创建的所有附件。&lt;&#x2F;p&gt;
&lt;p&gt;示例：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;CustomReporter {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;onStepEnd&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;attachments &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;attachments&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 按需处理附件
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;yong-hu-jie-mian-geng-xin&quot;&gt;用户界面更新&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;1-zeng-qiang-de-html-bao-gao-qi&quot;&gt;1. 增强的 HTML 报告器&lt;&#x2F;h3&gt;
&lt;p&gt;默认的 HTML 报告器已更新，改进了附件的显示方式，提供更直观和信息丰富的布局。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-aria-kuai-zhao-yuan-su-xuan-ze-qi&quot;&gt;2. ARIA 快照元素选择器&lt;&#x2F;h3&gt;
&lt;p&gt;UI 中新增了一个按钮，用于选择生成 ARIA 快照的元素，简化了创建无障碍性测试的流程。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-xiang-xi-de-xing-dong-xin-xi&quot;&gt;3. 详细的行动信息&lt;&#x2F;h3&gt;
&lt;p&gt;现在在追踪中会显示额外的细节，例如在行动中按下的按键，这些信息会与 API 调用一起显示，为测试执行提供更深入的洞察。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-mo-ren-jin-yong-canvas-nei-rong-xian-shi&quot;&gt;4. 默认禁用 Canvas 内容显示&lt;&#x2F;h3&gt;
&lt;p&gt;由于可能引发错误，现在默认禁用在追踪中显示&lt;code&gt;&amp;lt;canvas&amp;gt;&lt;&#x2F;code&gt;内容。可以通过 UI 中的“显示 Canvas 内容”设置启用。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-zeng-qiang-de-ji-shi-xin-xi&quot;&gt;5. 增强的计时信息&lt;&#x2F;h3&gt;
&lt;p&gt;“调用”和“网络”面板现在显示额外的计时信息，有助于性能分析和调试。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;po-pi-xing-bian-geng&quot;&gt;破坏性变更&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;1-ke-bian-ji-yuan-su-duan-yan&quot;&gt;1. 可编辑元素断言&lt;&#x2F;h3&gt;
&lt;p&gt;如果目标元素不是&lt;code&gt;&amp;lt;input&amp;gt;&lt;&#x2F;code&gt;、&lt;code&gt;&amp;lt;select&amp;gt;&lt;&#x2F;code&gt;或其他已识别的可编辑元素，&lt;code&gt;expect(locator).toBeEditable()&lt;&#x2F;code&gt;和&lt;code&gt;locator.isEditable()&lt;&#x2F;code&gt;方法现在会抛出错误。&lt;&#x2F;p&gt;
&lt;p&gt;示例：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;element &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#non-editable-element&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;element&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeEditable&lt;&#x2F;span&gt;&lt;span&gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 抛出错误
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;快照更新行为&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;将&lt;code&gt;testConfig.updateSnapshots&lt;&#x2F;code&gt;设置为&lt;code&gt;all&lt;&#x2F;code&gt;时，现在会更新所有快照，而不仅仅是失败或已更改的快照。要保留之前仅更新已更改快照的行为，请使用新的&lt;code&gt;changed&lt;&#x2F;code&gt;枚举值。&lt;&#x2F;p&gt;
&lt;p&gt;命令行示例：&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;npx playwright test --update-snapshots=changed
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;liu-lan-qi-ban-ben-geng-xin&quot;&gt;浏览器版本更新&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright v1.50.0 包含以下浏览器版本：&lt;&#x2F;p&gt;
&lt;p&gt;• Chromium 133.0.6943.16&lt;&#x2F;p&gt;
&lt;p&gt;• Mozilla Firefox 134.0&lt;&#x2F;p&gt;
&lt;p&gt;• WebKit 18.2&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-lun&quot;&gt;结论&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright v1.50.0 带来了显著的改进，提升了测试效率、无障碍性验证和调试能力。新的步骤超时配置、步骤跳过和 ARIA 快照增强功能提高了测试执行的健壮性。Web 服务器的优雅关闭和追踪中详细的行动信息为开发者提供了更友好的测试体验。此外，诸如更严格的可编辑元素断言等破坏性变更，进一步增强了测试的可靠性。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>告别不稳定的测试：每个测试自动化工程师必须掌握的Playwright最佳实践</title>
        <published>2025-01-28T00:00:00+00:00</published>
        <updated>2025-01-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/say-goodbye-to-flaky-tests-playwright-best-practices-every-test-automation-engineer-must-know-9dfeb9bb5017/"/>
        <id>https://playwright.itest.info/blog/say-goodbye-to-flaky-tests-playwright-best-practices-every-test-automation-engineer-must-know-9dfeb9bb5017/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/say-goodbye-to-flaky-tests-playwright-best-practices-every-test-automation-engineer-must-know-9dfeb9bb5017/">&lt;p&gt;测试用例集就像降落伞——只有在每次都能正常工作时，你才会信任它。如果你的测试今天通过了，但明天却莫名其妙地失败了，那么它们带来的坏处比好处更多。稳定性是测试价值的关键。在本文中，我们将探讨 Playwright 创建稳定、可靠测试的顶级策略，让你能够自信地发布代码，而不用担心测试的不稳定性。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;playwright-ce-shi-zui-jia-shi-jian&quot;&gt;Playwright 测试最佳实践&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用可靠的定位器&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;&#x2F;strong&gt;：确保测试对频繁的 UI 变化具有弹性。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;优先使用数据或基于角色的定位器&lt;&#x2F;strong&gt;：使用&lt;code&gt;data-testid&lt;&#x2F;code&gt;或&lt;code&gt;getByRole&lt;&#x2F;code&gt;，而不是脆弱的 CSS 类或自动生成的 ID。这种方法关注的是很少变化的稳定属性。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;避免动态属性&lt;&#x2F;strong&gt;：不要依赖包含时间戳、会话令牌或随机字符串的定位器。这些内容可能会在每次构建或测试运行时发生变化。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;处理复杂的 DOM 结构&lt;&#x2F;strong&gt;：对于 Shadow DOM 或 iframe，使用&lt;code&gt;page.frame(...)&lt;&#x2F;code&gt;或嵌套的&lt;code&gt;locator(...)&lt;&#x2F;code&gt;。始终确保你指向正确的文档上下文。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;强制执行命名标准&lt;&#x2F;strong&gt;：保持测试特定属性的一致性。例如，始终使用&lt;code&gt;data-testid=&quot;submit-button&quot;&lt;&#x2F;code&gt;或类似的命名模式，以减少混淆。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;利用自动等待和显式条件&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;&#x2F;strong&gt;：让 Playwright 智能处理时间问题，消除由于延迟导致的不稳定性。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;使用定位器自动等待&lt;&#x2F;strong&gt;：使用&lt;code&gt;page.locator(&#x27;selector&#x27;)&lt;&#x2F;code&gt;并链接操作，如&lt;code&gt;.click()&lt;&#x2F;code&gt;。Playwright 会自动重试，直到元素准备就绪。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;使用基于断言的等待&lt;&#x2F;strong&gt;：避免使用&lt;code&gt;waitForTimeout&lt;&#x2F;code&gt;，优先使用&lt;code&gt;expect(locator).toBeVisible()&lt;&#x2F;code&gt;、&lt;code&gt;expect(locator).toHaveText()&lt;&#x2F;code&gt;等。每个断言都包含在默认或配置的超时内的重试机制。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;最小化手动暂停&lt;&#x2F;strong&gt;：只有在外部服务引入实际延迟时，才使用显式的&lt;code&gt;page.waitForTimeout()&lt;&#x2F;code&gt;。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;期望轮询&lt;&#x2F;strong&gt;：对于更复杂的条件（如验证列表计数），使用&lt;code&gt;expect.poll(...)&lt;&#x2F;code&gt;。这减少了自定义循环或重复检查的需要。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;隔离测试并避免共享状态&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;&#x2F;strong&gt;：使每个测试自包含，确保一个测试的失败或残留数据不会影响下一个测试。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;独立的设置和清理&lt;&#x2F;strong&gt;：无论是通过 Playwright 的 fixture 还是&lt;code&gt;beforeEach&#x2F;afterEach&lt;&#x2F;code&gt;钩子，只为每个测试重新创建所需的内容。不要依赖前一个测试的状态。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;使用 Playwright 的 fixture 模型&lt;&#x2F;strong&gt;：利用内置的 fixture 来初始化页面、上下文或用户状态，确保并行运行的一致性。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;参考专用的数据策略&lt;&#x2F;strong&gt;：如果你需要临时环境或容器化的数据库，请参阅&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@samuel.sperling&#x2F;say-goodbye-to-flaky-tests-playwright-best-practices-every-test-automation-engineer-must-know-9dfeb9bb5017#&quot;&gt;&lt;strong&gt;管理测试数据和数据库&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;文档，了解更深入的策略。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;保持测试的原子性和专注性&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;&#x2F;strong&gt;：减少调试复杂性和部分失败的风险。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;每个测试只测试一个功能&lt;&#x2F;strong&gt;：只测试一个主要场景（例如，“用户可以提交有效表单”）。大型端到端流程更容易出现间歇性中断。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;可重用性&lt;&#x2F;strong&gt;：对于重复的步骤（如登录），使用辅助函数或 fixture。不要在每个测试中重复复杂的登录流程。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;步骤注释&lt;&#x2F;strong&gt;：使用&lt;code&gt;test.step(...)&lt;&#x2F;code&gt;来清晰地描述每个阶段的操作。这有助于准确识别测试失败的位置。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;正确处理异步操作&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;&#x2F;strong&gt;：消除与网络响应或异步进程相关的竞态条件和时间问题。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;等待网络响应&lt;&#x2F;strong&gt;：当操作触发 API 调用时，确认 UI 状态或使用&lt;code&gt;page.waitForResponse(...)&lt;&#x2F;code&gt;等待相关响应。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;模拟外部服务&lt;&#x2F;strong&gt;：使用 Playwright 的请求拦截功能来控制或模拟外部依赖。这避免了由于第三方服务中断或速率限制导致的不稳定性。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;超时配置&lt;&#x2F;strong&gt;：在&lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt;中设置标准超时。确保它们足够长以适应实际条件，但不要太长以至于失败无法被检测到。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;控制测试环境&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;&#x2F;strong&gt;：确保测试在一致的条件下运行，以减少变异性。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;容器化&#x2F;CI&lt;&#x2F;strong&gt;：将你的设置容器化（Docker）或标准化你的 CI 环境。不匹配的操作系统或浏览器版本可能会引入难以调试的错误。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;稳定的构建&lt;&#x2F;strong&gt;：始终在通过 linting、单元测试和集成测试的代码上运行端到端测试。在进入端到端测试之前，尽早发现小问题。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;重置快照&#x2F;日志&lt;&#x2F;strong&gt;：在每次运行前清除旧的快照或日志，以避免过时的工件生成误报或混淆。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;实施健壮的断言&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;&#x2F;strong&gt;：确保成功条件清晰，并可靠地检测正面和负面条件。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;内置匹配器&lt;&#x2F;strong&gt;：使用&lt;code&gt;toHaveText&lt;&#x2F;code&gt;、&lt;code&gt;toBeVisible&lt;&#x2F;code&gt;、&lt;code&gt;toHaveCount&lt;&#x2F;code&gt;等。这些断言包含重试机制，有助于减少手动休眠。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;负面断言&lt;&#x2F;strong&gt;：当期望没有结果时，使用&lt;code&gt;toBeHidden()&lt;&#x2F;code&gt;或&lt;code&gt;toHaveCount(0)&lt;&#x2F;code&gt;。这些断言也包含基于时间的重试，以处理缓慢的 UI 更新。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;细粒度检查&lt;&#x2F;strong&gt;：如果一个测试检查多个子功能，将它们分解为单独的&lt;code&gt;expect&lt;&#x2F;code&gt;调用。更细粒度的断言会产生更清晰的错误报告。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用跟踪和调试功能&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;&#x2F;strong&gt;：快速诊断根本原因，尤其是在 CI 或并行化环境中。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Playwright 跟踪&lt;&#x2F;strong&gt;：对失败的测试使用&lt;code&gt;--trace on&lt;&#x2F;code&gt;或&lt;code&gt;--trace retain-on-failure&lt;&#x2F;code&gt;。在 Trace Viewer 中重放跟踪，查看逐步的截图、日志和网络调用。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;截图和视频&lt;&#x2F;strong&gt;：在失败时捕获截图或完整视频。这在无头 CI 运行中非常有用，因为你无法实时查看浏览器。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;控制台&#x2F;网络日志&lt;&#x2F;strong&gt;：监听&lt;code&gt;page.on(&#x27;console&#x27;, ...)&lt;&#x2F;code&gt;或网络事件。了解控制台错误或请求状态可以快速定位不稳定性。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;编写清晰且可维护的代码&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;&#x2F;strong&gt;：保持测试代码易于长期演进和调试。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;使用页面对象&#x2F;抽象&lt;&#x2F;strong&gt;：将常见的定位器和操作集中在页面对象或辅助模块中。单个更改可以更新所有引用该组件的测试。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;避免硬编码字符串&lt;&#x2F;strong&gt;：将重复的 URL、凭据或文本存储在配置或常量中。这减少了重复和拼写错误的风险。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;描述性命名&lt;&#x2F;strong&gt;：为测试命名时明确结果（例如，&lt;code&gt;test(&#x27;提交有效表单显示成功消息&#x27;, ...)&lt;&#x2F;code&gt;），以便在报告中清晰可见。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;优化并行执行（和性能）&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;&#x2F;strong&gt;：在不引入跨测试干扰的情况下加速测试运行。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;独立的浏览器上下文&lt;&#x2F;strong&gt;：在每个测试中启动自己的上下文（或页面）。跨测试的共享状态是导致不稳定的常见原因。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;明智地设置并发性&lt;&#x2F;strong&gt;：在&lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt;中，根据可用的 CPU 和内存调整&lt;code&gt;workers&lt;&#x2F;code&gt;。过载环境可能会减慢或破坏运行。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;平衡性能&lt;&#x2F;strong&gt;：并行运行数十个测试是快速的，但要注意系统限制。如果测试突然变慢或失败，降低并发性或优化环境资源。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;快速监控和分类不稳定的测试&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标&lt;&#x2F;strong&gt;：通过解决不稳定的根源，保持对测试用例集的信任。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;即时可见性&lt;&#x2F;strong&gt;：在 CI 仪表板上及时显示测试结果。不要将不稳定的失败隐藏在静默重试背后。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;隔离策略&lt;&#x2F;strong&gt;：如果已知不稳定的测试阻塞了构建，暂时将它们移到隔离套件中。但请将此视为短期措施。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;根本原因分析&lt;&#x2F;strong&gt;：调查并修复根本问题——通常是异步竞态或环境不匹配——而不是仅仅重新运行以获得通过的结果。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;其他注意事项&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;使用 Playwright 测试运行器&lt;&#x2F;strong&gt;：依赖 Playwright 的原生运行器，以实现简化的并行化、fixture 和调试工具。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;认证流程&lt;&#x2F;strong&gt;：如果你的网站需要登录，考虑存储已登录的&lt;code&gt;storageState&lt;&#x2F;code&gt;，以跳过多个测试中的缓慢登录步骤。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;测试组织&lt;&#x2F;strong&gt;：按功能分组测试（例如，&lt;code&gt;login&lt;&#x2F;code&gt;、&lt;code&gt;profile&lt;&#x2F;code&gt;、&lt;code&gt;checkout&lt;&#x2F;code&gt;）。这使得运行或维护测试子集变得容易。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h3&gt;
&lt;p&gt;Playwright 提供了强大的功能来&lt;strong&gt;最小化不稳定性&lt;&#x2F;strong&gt;并&lt;strong&gt;最大化可靠性&lt;&#x2F;strong&gt;，但成功取决于：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;坚实的基础&lt;&#x2F;strong&gt;——使用稳定的定位器、利用自动等待和隔离测试。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;良好的工程实践&lt;&#x2F;strong&gt;——页面对象、健壮的断言和正确的并行化。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;主动调试&lt;&#x2F;strong&gt;——跟踪、日志和即时分类不稳定的测试。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;say-goodbye-to-flaky-tests-playwright-best-practices-every-test-automation-engineer-must-know-9dfeb9bb5017&#x2F;img&#x2F;1739583563.2712410*XGF50YwM7DLkWmAE&quot; alt=&quot;Image 13&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;guan-li-duan-dao-duan-ce-shi-de-ce-shi-shu-ju-he-shu-ju-ku&quot;&gt;管理端到端测试的测试数据和数据库&lt;&#x2F;h3&gt;
&lt;p&gt;稳定的测试只有在它们所依赖的数据一致时才有价值。即使是最健壮的测试，如果底层数据或数据库设置不一致或不可靠，也可能失败。这就是为什么管理测试数据和数据库至关重要——它确保你的测试有一个一致的环境，以提供准确的结果。让我们探讨如何有效地做到这一点。&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用专用（且可丢弃的）测试数据库&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;切勿使用生产或共享数据库&lt;&#x2F;strong&gt;：避免污染真实环境或冒数据损坏的风险。专用的测试数据库（例如，&lt;code&gt;myapp_test&lt;&#x2F;code&gt;）确保数据完全在测试控制之下。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;启动新环境&lt;&#x2F;strong&gt;：使用 Docker 或其他基于容器的解决方案来启动测试数据库。测试后将其拆除。这种方法保证了每次运行都有一个干净的状态。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;数据库快照&lt;&#x2F;strong&gt;：如果你的模式很大，可以在每次运行前从已知的“干净”快照恢复，以避免从头开始重建。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用脚本创建已知的良好数据&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;最小化所需的种子数据&lt;&#x2F;strong&gt;：只包含绝对必要的记录（用户、产品参考等）。过大的数据集会减慢测试速度并使调试复杂化。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;自动化种子脚本&lt;&#x2F;strong&gt;：将种子逻辑保存在版本控制中（SQL 文件或 Node 脚本）。这确保了本地和 CI 环境的一致性。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;环境特定的种子数据&lt;&#x2F;strong&gt;：如果你有多个测试环境（例如，QA、staging），为每个环境维护种子数据，以确保清晰性和可维护性。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;隔离并清理每个测试的数据（在可行的情况下）&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;每个测试的设置&#x2F;清理&lt;&#x2F;strong&gt;：创建每个测试所需的数据，然后删除或回滚。这确保没有残留数据污染后续测试。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;事务回滚&lt;&#x2F;strong&gt;：一些框架允许你在数据库事务中运行测试，并在完成后回滚。这在端到端设置中可能很棘手，但对于集成级测试非常有效。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;唯一标识符&lt;&#x2F;strong&gt;：如果你不能完全隔离数据，生成唯一的测试用户信息（例如，&lt;code&gt;test-user-{timestamp}@example.com&lt;&#x2F;code&gt;）以避免冲突。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;与并行或 CI 环境协调&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;多个数据库实例&lt;&#x2F;strong&gt;：如果你并行运行测试，每个工作线程可以指向不同的测试数据库（例如，&lt;code&gt;myapp_test_1&lt;&#x2F;code&gt;、&lt;code&gt;myapp_test_2&lt;&#x2F;code&gt;），以防止覆盖。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;每个工作线程的专用容器&lt;&#x2F;strong&gt;：为了完全隔离，为每个并行工作线程启动一个完整的数据库容器。这可能会消耗大量资源，但几乎消除了跨测试污染。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;性能与并发性&lt;&#x2F;strong&gt;：监控 CI 资源使用情况。如果你看到巨大的减速或不稳定性，减少并行工作线程或优化数据访问模式。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模拟或存档数据库调用&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;何时模拟&lt;&#x2F;strong&gt;：如果你的测试关注的是 UI 流程而不是实际的数据库验证，模拟数据库层可以减少复杂性和测试时间。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;关键端到端测试使用真实数据库&lt;&#x2F;strong&gt;：对于真正的端到端覆盖（例如，验证模式约束、索引或复杂的数据库逻辑），避免模拟。真实数据会暴露真实问题。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;持续监控数据库健康&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;迁移&lt;&#x2F;strong&gt;：在运行测试之前，始终将测试数据库迁移到最新的模式版本。模式版本不匹配是导致不稳定的主要原因。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;定期清理&lt;&#x2F;strong&gt;：如果无法使用临时容器，安排定期清理（截断表或删除旧数据），以防止测试环境随着时间的推移膨胀。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;性能跟踪&lt;&#x2F;strong&gt;：密切关注测试运行时间。如果你注意到逐渐变慢，调查数据膨胀或缺失的索引。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;zui-hou-si-kao&quot;&gt;最后思考&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;测试数据管理&lt;&#x2F;strong&gt;和数据库设置的健壮方法可以放大你对&lt;strong&gt;Playwright&lt;&#x2F;strong&gt;的投资。结合本文涵盖的通用最佳实践（定位器、自动等待、并行执行），你的团队可以实现：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;最小化不稳定性&lt;&#x2F;strong&gt;：即使面对复杂的有状态数据，测试也是可重复且稳定的。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;增加信心&lt;&#x2F;strong&gt;：真实的端到端覆盖确保应用程序从 UI 到数据库都能正常工作。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;更大的业务价值&lt;&#x2F;strong&gt;：更快的反馈循环和更可靠的构建转化为更高的生产力和更少的生产错误。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;yuan-wen-di-zhi&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@samuel.sperling&#x2F;say-goodbye-to-flaky-tests-playwright-best-practices-every-test-automation-engineer-must-know-9dfeb9bb5017&quot;&gt;原文地址&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright自动化中的动态页面对象类加载</title>
        <published>2025-01-22T00:00:00+00:00</published>
        <updated>2025-01-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/optimizing-playwright-tests-with-dynamic-page-object-loading-dfda67be81e4/"/>
        <id>https://playwright.itest.info/blog/optimizing-playwright-tests-with-dynamic-page-object-loading-dfda67be81e4/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/optimizing-playwright-tests-with-dynamic-page-object-loading-dfda67be81e4/">&lt;h3 id=&quot;dong-tai-lei-jia-zai&quot;&gt;动态类加载&lt;&#x2F;h3&gt;
&lt;p&gt;页面对象模型（&lt;strong&gt;POM&lt;&#x2F;strong&gt;）是 UI 测试自动化中最常用的设计模式之一。它在组织测试脚本、增强可维护性和促进可重用性方面非常有效。在深入探讨这个话题之前，我们先讨论一个实际用例，以了解其优势。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;chang-jing-dian-zi-shang-wu-wang-zhan-ce-shi&quot;&gt;场景：电子商务网站测试&lt;&#x2F;h4&gt;
&lt;p&gt;假设你正在为一个电子商务网站项目工作。客户有一个独特的需求：同一套测试脚本应该在多个本地化站点上执行。乍一看，这似乎很简单且易于实现。但等等……真的是这样吗？&lt;&#x2F;p&gt;
&lt;p&gt;让我们探讨一些可能的方法来满足这一需求：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;维护独立分支&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
为每个本地化站点编写独立的脚本，并在 Git 中为每个地区维护不同的分支。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;在页面对象类中使用条件方法&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
在页面对象类中定义方法，通过条件逻辑处理本地化，例如：&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;localization &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;en&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 英文代码 }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 其他语言代码
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;使用 OR 条件的动态定位器&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;创建支持多种本地化需求的 Playwright 定位器，使用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locator#locator-or&quot;&gt;OR&lt;&#x2F;a&gt;条件匹配多个元素。&lt;&#x2F;p&gt;
&lt;p&gt;这些方法中的每一种都可能满足当前的需求。然而，当你退一步考虑更大的图景时——考虑到添加更多测试场景、支持更多本地化、维护代码库以及与其他服务集成等参数——你可能会注意到显著的缺点。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;代码重复&lt;&#x2F;strong&gt;：每个本地化的独立分支导致代码重复。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;复杂性&lt;&#x2F;strong&gt;：随着应用程序的扩展，条件逻辑变得难以管理。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;可维护性差&lt;&#x2F;strong&gt;：添加新的本地化或更新现有脚本需要大量工作，使框架变得笨重。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;为了解决这些挑战，你可以利用&lt;strong&gt;装饰器&lt;&#x2F;strong&gt;、&lt;strong&gt;反射&lt;&#x2F;strong&gt;或&lt;strong&gt;动态类加载&lt;&#x2F;strong&gt;等高级概念。这些技术允许你的测试框架根据本地化设置动态引用不同的页面对象类，从而减少重复并增强灵活性。&lt;&#x2F;p&gt;
&lt;p&gt;在本演示中，我们将重点介绍&lt;strong&gt;动态类加载&lt;&#x2F;strong&gt;，展示相同的测试脚本如何在多个本地化站点上无缝工作。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dong-tai-lei-jia-zai-ben-di-hua-zhan-dian-de-jie-jue-fang-an&quot;&gt;动态类加载：本地化站点的解决方案&lt;&#x2F;h3&gt;
&lt;p&gt;动态类加载允许你根据本地化语言在运行时加载适当的页面对象类。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;注意&lt;&#x2F;em&gt;&lt;&#x2F;strong&gt;：为了简单起见，我将保持示例代码简洁并专注于概念。在实际项目中实现时，请确保遵循所有 POM 最佳实践，并避免在页面对象方法中硬编码数据。&lt;&#x2F;p&gt;
&lt;p&gt;首先，我们需要通过在控制台中执行 JavaScript 代码来识别网页的本地化设置。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pageLanguage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;document.documentElement.lang &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Not set&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pageLanguage&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;# &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;输出 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;en&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;或 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;de&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;其次，为每个站点创建独立的页面对象类。我们创建一个接口类来定义方法，这将帮助你在代码补全和类型检查时使用。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;interface &lt;&#x2F;span&gt;&lt;span&gt;PomTemplate {
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;searchAnItem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitToOpenInNewTab&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;buyItNow&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;enterShippingAddressAndSubmit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;为英文本地化区域创建一个页面对象类。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;EnLocalisedRegion &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;implements &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;font-style:italic;color:#8cdaff;&quot;&gt;PomTemplate &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;Page
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;searchAnItem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByPlaceholder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Search for anything&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByPlaceholder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Search for anything&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;press&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;heading&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;+ results for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;item &lt;&#x2F;span&gt;&lt;span&gt;})).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;clickAnyItem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;New Listing Pair of Vintage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitToOpenInNewTab&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;[ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page1 &lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Promise&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;([
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForEvent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Mattel JAL Uniform Barbie doll Japan Airlines Stewardess Flight Attendant Opens&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        ])
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page1
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;bringToFront&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;buyItNow&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Buy It Now&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Check out as guest&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;enterShippingAddressAndSubmit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;First name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Thanan&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Last name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Rahase&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Street address&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { exact: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Kamraj road perumal perr&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;City&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Chennai&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;State&#x2F;Province&#x2F;Region&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;selectOption&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;TN&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { exact: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test@gmail.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Confirm email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test@gmail.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Phone number (required)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;8765423891&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-test-id=&amp;quot;ADD_ADDRESS_SUBMIT&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;为德文本地化区域创建一个页面对象类。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;DeLocalisedRegion &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;implements &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;font-style:italic;color:#8cdaff;&quot;&gt;PomTemplate&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;Page
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;searchAnItem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByPlaceholder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Bei eBay finden&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByPlaceholder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Bei eBay finden&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;press&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;+ Ergebnisse für ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitToOpenInNewTab&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;[ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page1 &lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Promise&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;([
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForEvent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Sparkle Beach Barbie Puppe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        ])
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page1
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;bringToFront&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;buyItNow&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Sofort-Kaufen&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Als Gast kaufen&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;enterShippingAddressAndSubmit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Vorname&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Thanan&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Nachname&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Rahase&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Straße und Hausnummer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;pressSequentially&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;2812&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addrDisplay &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.auto-address-cntr&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addrDisplay&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addrDisplay&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;E-Mail&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { exact: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test@gmail.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;E-Mail bestätigen&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test@gmail.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Telefon (erforderlich)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;09852 34578&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-test-id=&amp;quot;ADD_ADDRESS_SUBMIT&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在我的 Playwright 测试中，我使用 fixture 来处理所有页面对象类实例的创建。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;PageFixture&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;PomTemplate  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;pages&#x2F;pomTemplate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span&gt;Pages &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;PomTemplate
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;PageFixture&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt; Pages &amp;gt;({
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    dynamicPage: [ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;baseURL &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseURL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;获取网站语言信息
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pageLanguage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;document.documentElement.lang &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Not set&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;flowPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;loadclass&lt;&#x2F;span&gt;&lt;span&gt;( &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pageLanguage&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;flowPage&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    }, { scope: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;}],
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;loadclass &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;lang&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;module &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await import&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;..&#x2F;pages&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lang&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}_flow.page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newClass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;PomTemplate} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return new &lt;&#x2F;span&gt;&lt;span&gt;newClass(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;代码非常简单且易于理解，我将这些页面对象类保存在一个单独的&lt;code&gt;pages&lt;&#x2F;code&gt;文件夹中。在运行时，这些类模块会以懒加载的方式加载，并实例化相应的对象以供测试方法使用。现在看看你的测试方法。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;..&#x2F;init&#x2F;pageFixture&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Dynamic loading of class files&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;eBay - Add address flow&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;dynamicPage &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dynamicPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;searchAnItem&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Barbie Doll&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dynamicPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitToOpenInNewTab&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dynamicPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;buyItNow&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dynamicPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;enterShippingAddressAndSubmit&lt;&#x2F;span&gt;&lt;span&gt;({})
&lt;&#x2F;span&gt;&lt;span&gt;    })
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;现在将&lt;code&gt;baseURL&lt;&#x2F;code&gt;设置为&lt;code&gt;https:&#x2F;&#x2F;ebay.com&lt;&#x2F;code&gt;或&lt;code&gt;https:&#x2F;&#x2F;ebay.de&lt;&#x2F;code&gt;，在执行时你可以清楚地看到正确的类被加载，脚本成功执行。&lt;&#x2F;p&gt;
&lt;p&gt;但请注意，动态加载需要仔细设计以确保正确的类型推断或显式类型转换。确保在运行时抛出任何错误时进行健壮的错误处理。&lt;&#x2F;p&gt;
&lt;p&gt;所有源代码可以在这里找到。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;jie-lun&quot;&gt;结论&lt;&#x2F;h3&gt;
&lt;p&gt;动态类加载适用于需要灵活性和运行时适应性的场景。我们可以轻松地封装和解耦代码，以实现更好的设计和维护目的。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;yuan-wen-di-zhi&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@thananjayan1988&#x2F;optimizing-playwright-tests-with-dynamic-page-object-loading-dfda67be81e4&quot;&gt;原文地址&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>使用 Playwright JavaScript 进行 API 测试 </title>
        <published>2025-01-18T00:00:00+00:00</published>
        <updated>2025-01-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/master-api-testing-with-playwright-javascript-27337dd44c4d/"/>
        <id>https://playwright.itest.info/blog/master-api-testing-with-playwright-javascript-27337dd44c4d/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/master-api-testing-with-playwright-javascript-27337dd44c4d/">&lt;p&gt;首先，让我们从 API 测试的基础知识开始。我们将探讨 API 测试中使用的基本方法，然后深入探讨设计有效 API 测试用例的最佳实践。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;master-api-testing-with-playwright-javascript-27337dd44c4d&#x2F;img&#x2F;1739584247.688651*2qh9ovPGLW8RvcDrrqUcMQ.jpeg&quot; alt=&quot;Image 14&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-http-fang-fa-jin-xing-api-ce-shi&quot;&gt;使用 HTTP 方法进行 API 测试&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;GET&lt;&#x2F;strong&gt;: 用于获取数据。&lt;br &#x2F;&gt;
示例：获取 ID 为 &lt;code&gt;&#x27;1&#x27;&lt;&#x2F;code&gt; 的书籍的价格、作者姓名和书籍类别。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;POST&lt;&#x2F;strong&gt;: 用于创建新资源。&lt;br &#x2F;&gt;
示例：通过提供书籍名称和 ID 等详细信息来创建一本新书。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;PUT&lt;&#x2F;strong&gt;: 用于通过完全替换数据来更新现有资源的所有字段。&lt;br &#x2F;&gt;
示例：更新 ID 为 &lt;code&gt;&#x27;1&#x27;&lt;&#x2F;code&gt; 的书籍的价格、作者姓名和书籍类别。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;PATCH&lt;&#x2F;strong&gt;: 用于更新现有资源的特定字段，而不替换整个数据。&lt;br &#x2F;&gt;
示例：仅更新 ID 为 &lt;code&gt;&#x27;1&#x27;&lt;&#x2F;code&gt; 的书籍的价格。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;DELETE&lt;&#x2F;strong&gt;: 用于删除特定资源。&lt;br &#x2F;&gt;
示例：删除 ID 为 &lt;code&gt;&#x27;1&#x27;&lt;&#x2F;code&gt; 的书籍的所有详细信息。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;blockquote&gt;
&lt;p&gt;现在，激动人心的部分来了！！Playwright JavaScript 如何发起请求。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;项目设置：&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
假设您已经在本地环境中安装了 Node.js，您可以通过以下命令安装 Playwright。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; init @playwright&#x2F;test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;如何在 Playwright 中发起 API 请求：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;使用 &lt;code&gt;直接使用 HTTP 方法的请求&lt;&#x2F;code&gt;：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用不同 HTTP 方法的示例
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;不同的 HTTP 方法&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; GET 请求
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    params: { id: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;123 &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这些会成为查询参数
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; POST 请求
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;postResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    data: { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 请求体
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; PUT 请求
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    data: { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;updated&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; PATCH 请求
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;patchResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;patch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    data: { status: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;active&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; DELETE 请求
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;deleteResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;delete&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; HEAD 请求
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;head&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;使用 &lt;code&gt;newContext&lt;&#x2F;code&gt;：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 创建一个具有共享设置的上下文
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  baseURL: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  extraHTTPHeaders: {
&lt;&#x2F;span&gt;&lt;span&gt;    Authorization: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Bearer token123&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    Accept: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;application&#x2F;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用该上下文进行多个请求
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;usersResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;users&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;postsResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;posts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;使用 &lt;code&gt;newContext&lt;&#x2F;code&gt; 的主要好处：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;可重用的配置：&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 一次性设置，随处使用
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  baseURL: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  extraHTTPHeaders: {
&lt;&#x2F;span&gt;&lt;span&gt;    Authorization: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Bearer token123&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    Accept: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;application&#x2F;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;30000&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 所有这些请求都使用相同的配置
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;users&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;orders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;profile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;灵活性&lt;&#x2F;strong&gt;：当您在同一测试中需要不同的配置时&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试多个 API&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;playwright &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 用于 API 版本 1 的上下文
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;v1Context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    baseURL: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.v1.example.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    extraHTTPHeaders: { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;API-Version&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 用于 API 版本 2 的上下文
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;v2Context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    baseURL: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.v2.example.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    extraHTTPHeaders: { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;API-Version&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;2.0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在同一测试中使用两个上下文
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;v1Response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;v1Context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;v2Response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;v2Context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 清理
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;v1Context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;dispose&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;v2Context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;dispose&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;strong&gt;会话管理：&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;详细的会话处理示例&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 1. 初始尝试访问受保护的路由（应该失败）
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;initialResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;protected-data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;initialResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;401&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 未授权
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 2. 发起登录请求
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    data: {
&lt;&#x2F;span&gt;&lt;span&gt;      username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testuser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testpass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 3. 服务器通常会返回类似的内容：
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Set-Cookie: sessionId=abc123; Path=&#x2F;; HttpOnly
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; apiContext 会自动捕获这些内容
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果需要，您可以检查 cookies
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;响应头:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 4. 下一个请求会自动包含 cookie
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;protectedResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;protected-data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;protectedResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;ok&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeTruthy&lt;&#x2F;span&gt;&lt;span&gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 现在它可以工作了！
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 5. 您可以发起多个请求，所有请求共享同一个会话
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;profileResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;profile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ordersResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;orders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;settingsResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;settings&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 所有这些请求都自动包含了会话 cookie
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;何时使用直接请求和 &lt;code&gt;newContext&lt;&#x2F;code&gt;：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;master-api-testing-with-playwright-javascript-27337dd44c4d&#x2F;img&#x2F;1739584249.870321*rmls6FOBXBgfoV0lHg0zpQ.png&quot; alt=&quot;Image 15&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;然而，如果您不需要在测试用例之间共享会话，并且更倾向于为每个测试使用全新的会话，我建议使用 &lt;code&gt;test.use()&lt;&#x2F;code&gt; 进行全局设置的直接请求 API，例如配置 API 头信息。&lt;code&gt;test.use()&lt;&#x2F;code&gt; 方法会自动处理清理工作，默认情况下确保测试隔离，并通过一致的配置使测试更易于维护。然而，当使用 &lt;code&gt;request.newContext&lt;&#x2F;code&gt; 时，它会跨测试维护会话状态（例如 cookies 和 localStorage），直到您显式调用 &lt;code&gt;dispose()&lt;&#x2F;code&gt; 函数。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;使用 &lt;code&gt;test.use()&lt;&#x2F;code&gt; 的示例代码：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用 test.use 进行测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    extraHTTPHeaders: {
&lt;&#x2F;span&gt;&lt;span&gt;      Authorization: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Bearer token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;第一次登录&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;api&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: { username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;user1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pass1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这会创建一个 cookie 会话
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;profileResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;api&#x2F;profile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 此请求使用登录后的 cookie
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;第二次登录&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 此测试完全重新开始！
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 前一个测试的 cookies 不会被携带过来
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;profileResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;api&#x2F;profile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这会失败，因为我们没有会话
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;使用 &lt;code&gt;newContext&lt;&#x2F;code&gt; 的示例代码：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用 request.newContext 进行测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;      extraHTTPHeaders: {
&lt;&#x2F;span&gt;&lt;span&gt;        Authorization: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Bearer token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;第一次登录&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;api&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: { username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;user1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pass1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这会创建一个 cookie 会话
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;profileResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;api&#x2F;profile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 此请求使用登录后的 cookie
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;第二次登录&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 此测试将能够访问前一个测试的 cookies！
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;profileResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;api&#x2F;profile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这会成功，因为会话仍然有效
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;afterAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;dispose&lt;&#x2F;span&gt;&lt;span&gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 清理资源
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;让我们看看 Playwright 请求选项的完整结构：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;演示所有请求选项&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;endpoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 1. 头信息
&lt;&#x2F;span&gt;&lt;span&gt;    headers: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Content-Type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;application&#x2F;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      Authorization: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Bearer token123&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Custom-Header&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 2. 请求体 - 可以作为 &amp;#39;data&amp;#39; 发送
&lt;&#x2F;span&gt;&lt;span&gt;    data: {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Test User&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test@example.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 3. 查询参数
&lt;&#x2F;span&gt;&lt;span&gt;    params: {
&lt;&#x2F;span&gt;&lt;span&gt;      page: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      limit: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 4. 超时设置
&lt;&#x2F;span&gt;&lt;span&gt;    timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;30000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 30 秒
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 5. 表单数据 (multipart&#x2F;form-data)
&lt;&#x2F;span&gt;&lt;span&gt;    multipart: {
&lt;&#x2F;span&gt;&lt;span&gt;      file: {
&lt;&#x2F;span&gt;&lt;span&gt;        name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test.txt&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        mimeType: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text&#x2F;plain&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        buffer: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Buffer&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;      field: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 6. 忽略 HTTPS 错误
&lt;&#x2F;span&gt;&lt;span&gt;    ignoreHTTPSErrors: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 7. 最大重定向次数
&lt;&#x2F;span&gt;&lt;span&gt;    maxRedirects: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 8. 在非 200 状态码时失败
&lt;&#x2F;span&gt;&lt;span&gt;    failOnStatusCode: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 9. 重试失败的请求
&lt;&#x2F;span&gt;&lt;span&gt;    maxRetries: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;关于 Playwright 请求 API 的关键知识点：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;常见选项：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;data&lt;&#x2F;code&gt;: 请求体（自动为 JSON 字符串化）&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;headers&lt;&#x2F;code&gt;: 请求头&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;params&lt;&#x2F;code&gt;: 查询参数&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;timeout&lt;&#x2F;code&gt;: 请求超时时间（毫秒）&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;failOnStatusCode&lt;&#x2F;code&gt;: 是否在非 200 响应时抛出错误&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;ignoreHTTPSErrors&lt;&#x2F;code&gt;: 是否忽略 HTTPS 错误&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;form&lt;&#x2F;code&gt;: 用于表单提交&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;multipart&lt;&#x2F;code&gt;: 用于文件上传&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;此外，如果您想对与重定向端点相关的 API 进行安全测试，可以使用 &lt;code&gt;followRedirects: false&lt;&#x2F;code&gt; 选项。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;未认证访问时应重定向到登录页面&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 创建一个没有任何默认头信息的新上下文
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cleanContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 仅使用重定向头信息发起请求，并确保这是一个全新的请求
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cleanContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;BASE_URL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&#x2F;admin&#x2F;inventory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: {
&lt;&#x2F;span&gt;&lt;span&gt;        Accept: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;application&#x2F;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;X-Handle-Redirect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;      followRedirects: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      maxRedirects: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseHeaders &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseStatus &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 验证重定向
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseStatus&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;302&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseHeaders&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;auth&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 手动跟随重定向
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseHeaders&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cleanContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;BASE_URL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;]}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        {
&lt;&#x2F;span&gt;&lt;span&gt;          headers: { Accept: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;application&#x2F;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;      );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;请登录以继续&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;redirected&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;finally &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cleanContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;dispose&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果没有 &lt;code&gt;followRedirects: false&lt;&#x2F;code&gt;，如果服务器返回 302 重定向，Playwright 会自动跟随重定向到 &lt;code&gt;&#x2F;auth&#x2F;login&lt;&#x2F;code&gt;，并且 &lt;code&gt;response&lt;&#x2F;code&gt; 将来自登录页面（状态码 200）。我们将无法验证是否发生了重定向。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;API 响应中的常见检查：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用此上下文发起请求
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;users&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 重要的响应检查：
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 1. 状态码检查
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 2. Content-Type 头信息检查
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;content-type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;application&#x2F;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cache-control&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeDefined&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 3. 响应结构检查
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveProperty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;users&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 模式验证
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toMatchObject&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  id: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Number&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;  name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;  createdAt: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 4. 响应时间检查（性能）
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;timing &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;timing&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;timing&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseEnd&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeLessThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 2 秒
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 5. 错误处理
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errorResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;non-existent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errorResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;ok&lt;&#x2F;span&gt;&lt;span&gt;()) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errorBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errorResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errorBody&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveProperty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errorBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveProperty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;要在 Playwright 中发起 API 请求，重要的是要理解 JavaScript 概念，如 &lt;code&gt;async&#x2F;await&lt;&#x2F;code&gt;、&lt;code&gt;Promise.all()&lt;&#x2F;code&gt;、&lt;code&gt;Promise.race()&lt;&#x2F;code&gt;、&lt;code&gt;Promise.allSettled()&lt;&#x2F;code&gt; 和 Promise 链式调用。以下是每种类型在 API 请求中的使用示例：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Promise.all()&lt;&#x2F;code&gt; - 当您需要并行发起多个请求并且所有请求都必须成功时使用：&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Promise.all 示例&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 当您需要所有请求都成功时使用
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;usersResponse&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;postsResponse&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;commentsResponse&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Promise&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;([
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;users&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;posts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;comments&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    ]);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果任何一个请求失败，整个 Promise.all 都会失败
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;users &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;usersResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;posts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;postsResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;comments &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;commentsResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 现在您可以处理所有数据
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;users&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;posts&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;comments&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;catch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;其中一个请求失败:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;code&gt;Promise.race()&lt;&#x2F;code&gt; - 当您想要获取最先完成的请求的结果时使用：&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Promise.race 示例&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 用于测试故障转移场景或实现超时
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;timeoutPromise &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;Promise(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;reject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;reject&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;Error(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;请求超时&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5000&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在实际请求和超时之间进行竞争
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Promise&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;race&lt;&#x2F;span&gt;&lt;span&gt;([
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;timeoutPromise&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    ]);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeDefined&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;catch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;请求超时或失败:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;code&gt;Promise.allSettled()&lt;&#x2F;code&gt; - 当您想知道所有请求的结果，无论成功还是失败时使用：&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;当您发起普通请求时，如果任何请求失败，它会立即抛出错误。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;普通请求 vs Promise.allSettled&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 普通请求方法
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果其中任何一个失败，代码将在该点停止执行
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;orders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: { id: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;001&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, item: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;有效项目&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;orders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: { id: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;002&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, item: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 无效 - 项目为空
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response3 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;orders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: { id: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;003&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, item: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;另一个有效项目&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果 response2 失败，此代码不会运行
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;所有订单已创建&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;catch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;发生错误，停止所有操作&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Promise.allSettled 方法
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;orderRequests &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[
&lt;&#x2F;span&gt;&lt;span&gt;    { id: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;001&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, item: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;有效项目&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    { id: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;002&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, item: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 无效 - 项目为空
&lt;&#x2F;span&gt;&lt;span&gt;    { id: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;003&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, item: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;另一个有效项目&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  ];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;results &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Promise&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;allSettled&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;orderRequests&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;order&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;orders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;        data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;order&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      })
&lt;&#x2F;span&gt;&lt;span&gt;    )
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 无论是否有失败，此代码都会运行
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 您可以看到哪些订单成功，哪些失败
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;summary &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    successful: [],
&lt;&#x2F;span&gt;&lt;span&gt;    failed: [],
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;results&lt;&#x2F;span&gt;&lt;span&gt;.length; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;results&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;order &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;orderRequests&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;.status &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;fulfilled&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;.value.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;ok&lt;&#x2F;span&gt;&lt;span&gt;()) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;successful&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;        id: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;order&lt;&#x2F;span&gt;&lt;span&gt;.id,
&lt;&#x2F;span&gt;&lt;span&gt;        response: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;.value.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;      });
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;failed&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;        id: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;order&lt;&#x2F;span&gt;&lt;span&gt;.id,
&lt;&#x2F;span&gt;&lt;span&gt;        error:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;.status &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;rejected&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;reason
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;.value.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;      });
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;总结:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这可能会显示：
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 总结: {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;   successful: [
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;     { id: &amp;#39;001&amp;#39;, response: {...} },
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;     { id: &amp;#39;003&amp;#39;, response: {...} }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;   ],
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;   failed: [
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;     { id: &amp;#39;002&amp;#39;, error: &amp;#39;项目不能为空&amp;#39; }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;   ]
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 您仍然可以处理成功的订单
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;successful&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;failed&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;关键区别：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;master-api-testing-with-playwright-javascript-27337dd44c4d&#x2F;img&#x2F;1739584251.81127291*D44l8F8bOHJTDp9Gb5qtHQ.png&quot; alt=&quot;Image 16&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;&lt;strong&gt;Promise 链式调用&lt;&#x2F;strong&gt; - 当操作需要按顺序进行并且依赖于先前的结果时使用：&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Promise 链式调用示例&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 第一种方法 - 使用 .then()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;users&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;John&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    })
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;users&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.id}&#x2F;orders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;        data: { product: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;item1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;      })
&lt;&#x2F;span&gt;&lt;span&gt;    )
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;order&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;order&lt;&#x2F;span&gt;&lt;span&gt;.status).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;created&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    })
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;catch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;错误:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 第二种方法 - 使用 async&#x2F;await（更易读）
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;userResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;users&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;John&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;userResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;orderResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.example.com&#x2F;users&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.id}&#x2F;orders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      {
&lt;&#x2F;span&gt;&lt;span&gt;        data: { product: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;item1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;order &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;orderResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;order&lt;&#x2F;span&gt;&lt;span&gt;.status).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;created&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;catch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;错误:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;感谢阅读，希望您觉得这篇文章有帮助。&lt;&#x2F;p&gt;
&lt;p&gt;如果您想了解更多关于使用 Playwright 进行 API 测试的最佳实践，我建议您阅读这篇博客。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;yuan-wen-di-zhi&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wisdomgoody.medium.com&#x2F;master-api-testing-with-playwright-javascript-27337dd44c4d&quot;&gt;原文地址&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>《playwright自动化测试精剪全套视频》-拿走不谢！</title>
        <published>2024-11-08T00:00:00+00:00</published>
        <updated>2024-11-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            虫师
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/new-video-2024/"/>
        <id>https://playwright.itest.info/blog/new-video-2024/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/new-video-2024/">&lt;h2 id=&quot;2024-zui-xin-lu-zhi&quot;&gt;2024 最新录制&lt;&#x2F;h2&gt;
&lt;p&gt;今年 B 站直播的录制的，基于最新的 playwright 录制，内容非常丰富，除了官方 API 讲解，还提供大量的练习示例，还有 Playwright 的各种实用配置细节。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wu-fei-hua-jin-cou-ban&quot;&gt;无废话紧凑版&lt;&#x2F;h2&gt;
&lt;p&gt;我们平时在脱稿讲话的时候，会有重复，停顿，废话、错误排查、程序运行等待等。紧凑版就是把这些全部剪掉，只保留精华的部分。录制一个小时的视频，最终剪辑完可能不到 30 分钟。这套视频我剪了整整一周，手都快废了。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ji-yu-node-js-ban&quot;&gt;基于 node.js 版&lt;&#x2F;h2&gt;
&lt;p&gt;首先，Python 版的我之前已经录制过了,B 站可以找到。&lt;&#x2F;p&gt;
&lt;p&gt;其次，node.js 版的 Playwright 的才能真正体现出 Playwright 的真正实力。其他版本都是残血版，即便如此，也能打 selenium。然而，Node.js 版更是提升一个维度。&lt;&#x2F;p&gt;
&lt;p&gt;最后，我可以负责任的说，如果你会 Python，那么这套 Node.js 版对你来说基本无门槛。作为一个多年 Pythonista ，如果要使用 Playwright，在没有特殊的要求，那么我优先会选 Node.js 版，因为确实太高效了。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;huo-de-wan-zheng-shi-pin&quot;&gt;获得完整视频&lt;&#x2F;h2&gt;
&lt;p&gt;B 站地址：&lt;a href=&quot;https:&#x2F;&#x2F;www.bilibili.com&#x2F;video&#x2F;BV1hNDMYgEU6&quot;&gt;https:&#x2F;&#x2F;www.bilibili.com&#x2F;video&#x2F;BV1hNDMYgEU6&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;最后，完整视频总共 25 节，视频还有 10 节内容没有上传 B 站，想要的加 wx: zhiheng20170501。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;new-video-2024&#x2F;wx.webp&quot; alt=&quot;微信&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;new-video-2024&#x2F;course_list.webp&quot; alt=&quot;课程列表&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright 使用技巧与诀窍  #4</title>
        <published>2024-10-20T00:00:00+00:00</published>
        <updated>2024-10-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-tips-and-trick-4/"/>
        <id>https://playwright.itest.info/blog/playwright-tips-and-trick-4/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-tips-and-trick-4/">&lt;p&gt;继续我们的提示与技巧系列，在&lt;a href=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tips-and-trick-4&#x2F;img&#x2F;1730512646.597784&quot;&gt;#1&lt;&#x2F;a&gt;、&lt;a href=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tips-and-trick-4&#x2F;img&#x2F;1730512648.933496&quot;&gt;#2&lt;&#x2F;a&gt;和&lt;a href=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tips-and-trick-4&#x2F;img&#x2F;1730512644.012545&quot;&gt;#3&lt;&#x2F;a&gt;取得成功后，我们推出了最新的&lt;a href=&quot;#&quot;&gt;#4&lt;&#x2F;a&gt;。希望你喜欢，并且别忘了阅读代码片段中的注释。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-ru-he-lan-jie-duo-ge-xiang-tong-lu-you-de-qing-qiu&quot;&gt;1. 如何拦截多个相同路由的请求？&lt;&#x2F;h2&gt;
&lt;p&gt;我记得在 Cypress 中，这个功能非常容易实现，但在 Playwright 中，找到这种特定场景的细节却并不简单。想象一下，你在网页应用中导航，并希望验证多个具有相同路由的请求。比如，当你打开某个产品页面时，API 会在&lt;code&gt;api&#x2F;id&#x2F;1&lt;&#x2F;code&gt;发起请求以获取数据，然后你需要再次拦截同样的请求。如何拦截这两个请求？并且验证它们的数据？&lt;&#x2F;p&gt;
&lt;p&gt;你可以使用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page?ref=blog.martioli.com#page-wait-for-request&quot;&gt;waitForRequest()&lt;&#x2F;a&gt;来尝试，但还有一种更灵活的方法，这将为你打开更多的可能性。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;验证多个相同路由的调用&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;[] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用路由来区分你的网络调用
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;**&#x2F;api&#x2F;**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用url().includes来过滤所需的确切调用
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;id&#x2F;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;continue&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;())?.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requests&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;continue&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 上面的代码像是一个你设置的监听器。将其放在触发网络调用之前
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;www.yourapp.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 想象这一步触发了第一个id&#x2F;1调用
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;product&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这一步触发了第二个id&#x2F;1调用
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productExtraDetails&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 现在你等待它们并执行任何所需的断言
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requests&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toPass&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    intervals: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1_000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2_000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5_000&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;15_000&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 下面是你需要的额外步骤
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 因为有时你的测试完成得比应用响应要快，你需要取消路由。
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;afterEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;取消所有路由&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;unrouteAll&lt;&#x2F;span&gt;&lt;span&gt;({ behavior: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;wait&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我见过这个场景的最常见用例是验证 trackers。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-zai-wei-duan-yan-yuan-su-shu-liang-zhi-qian-bu-yao-shi-yong-all&quot;&gt;2. 在未断言元素数量之前不要使用.all()&lt;&#x2F;h2&gt;
&lt;p&gt;假设你有一个选择器会返回多个元素，而你想遍历这些元素以断言某些值。如果你没有先断言数量，你的测试可能会出现假阳性。这就是原因。&lt;&#x2F;p&gt;
&lt;p&gt;如果你有 3 个元素，并执行以下代码：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;在.all()上假阳性&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expectedValues &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 其他操作
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;allElements &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;item &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;of &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;allElements&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveAttribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any-attribute&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expectedValues&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;测试将如预期工作。然而，如果由于某种原因你的选择器在页面上找不到任何元素，测试将&lt;strong&gt;仍然通过&lt;&#x2F;strong&gt;。因为&lt;code&gt;.all()&lt;&#x2F;code&gt;在数量为零时不会报错，如果没有元素，forEach 将不会运行。&lt;&#x2F;p&gt;
&lt;p&gt;要正确处理此情况，你必须在遍历元素之前断言元素的数量。记得使用自动重试的&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions?ref=blog.martioli.com#auto-retrying-assertions&quot;&gt;toHaveCount&lt;&#x2F;a&gt;，而不是像(allElements.length).toBe(3)这样的语句。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;正确使用.all()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expectedValues &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 其他操作
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveCount&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;allElements &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;item &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;of &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;allElements&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveAttribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any-attribute&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expectedValues&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;即使是&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locator?ref=blog.martioli.com#locator-all&quot;&gt;Playwright 官方文档&lt;&#x2F;a&gt;也警告你关于&lt;code&gt;.all()&lt;&#x2F;code&gt;的使用，但现在你知道在未断言数量的情况下，这将不会正常工作。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;专业提示：在 Playwright 的 async&#x2F;await 风格中，不建议使用 forEach。你需要使用 for...of 来避免问题。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-dian-ji-zhi-qian-wu-xu-duan-yan-ke-jian-xing&quot;&gt;3. 点击之前无需断言可见性&lt;&#x2F;h2&gt;
&lt;p&gt;我觉得有必要提到这一点，因为我看到这种情况太频繁了。人们习惯在与元素交互之前断言其可见性，这是在任何教程或课堂上学到的行为，过去每个人都被迫学习的关键步骤。然而在 Playwright 中，有一种特定情况你不需要这样做。这种情况是对&lt;code&gt;click()&lt;&#x2F;code&gt;操作。以下是 Playwright 在点击元素之前所做的事情：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tips-and-trick-4&#x2F;img&#x2F;1730512642.314689clickme.png&quot; alt=&quot;Image 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这些被称为&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;actionability?ref=blog.martioli.com&quot;&gt;可操作性检查&lt;&#x2F;a&gt;，而且不仅限于&lt;code&gt;click()&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个关键点是，不要将其与 toHaveText 的断言混淆。当你使用最常见的断言方法之一&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locatorassertions?ref=blog.martioli.com#locator-assertions-to-have-text&quot;&gt;toHaveText&lt;&#x2F;a&gt;时要小心，因为 toHaveText 不会在元素可见之前等待。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-ru-he-zhi-yun-xing-wo-zheng-zai-chu-li-de-ce-shi&quot;&gt;4. 如何只运行我正在处理的测试？&lt;&#x2F;h2&gt;
&lt;p&gt;对于大多数关注&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;release-notes?ref=blog.martioli.com&quot;&gt;Playwright 发布说明&lt;&#x2F;a&gt;的你来说，这可能已经很熟悉，但有些人可能不知道，你可以使用一个特殊的 CLI 命令，只运行那些已更改的测试。&lt;&#x2F;p&gt;
&lt;p&gt;想象一下你执行了&lt;code&gt;git pull&lt;&#x2F;code&gt;，然后创建自己的分支开始处理某些测试，但你修复了多个测试中的一些错误或甚至创建了新的多个测试。那么你可以这样运行测试：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;npx playwright test path&#x2F;to&#x2F;test1.spec.ts path&#x2F;to&#x2F;test2.spec.ts path&#x2F;to&#x2F;test3.spec.ts path&#x2F;to&#x2F;test4.spec.ts path&#x2F;to&#x2F;test5.spec.ts path&#x2F;to&#x2F;test6.spec.ts path&#x2F;to&#x2F;test7.spec.ts&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;你可以简化为：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;npx playwright test --only-changed=main&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这将比较你的分支与主分支，并运行所有在你的分支中被修改的测试。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-ru-he-zai-playwright-zhong-yan-zheng-biao-ge-shu-ju&quot;&gt;5. 如何在 Playwright 中验证表格数据？&lt;&#x2F;h2&gt;
&lt;p&gt;假设你有一个表格，需要验证渲染的值。我不是在这里验证某个单元格中是否存在某个文本，而是要验证一个确切的值是否在特定的单元格中，该单元格对应确切的列和行。&lt;&#x2F;p&gt;
&lt;p&gt;你可以通过创建一个辅助函数来实现。为了方便你自己运行下面的代码并轻松测试，我将所有内容放在同一个文件中，当然，你可以根据自己的方式存储工具&#x2F;辅助函数。&lt;&#x2F;p&gt;
&lt;p&gt;那么这个辅助方法是如何工作的呢？你给它三个参数，第一个是标题（列）的文本，第二个是你要验证的行，第三个是你希望在该单元格中看到的期望文本。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;WebTableHelper {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateCellValueReferenceToHeader&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;headerText&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;rowToValidate&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;expectedValue
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;elementsOfHeader &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cell&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;elementsOfNRow &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;nth&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;rowToValidate&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cell&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;elementsOfHeader&lt;&#x2F;span&gt;&lt;span&gt;.length; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headerValueFound &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;elementsOfHeader&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;innerText&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headerValueFound&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;toLowerCase&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headerText&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;trim&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;toLowerCase&lt;&#x2F;span&gt;&lt;span&gt;()) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cellValue &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;elementsOfNRow&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;innerText&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cellValue&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expectedValue&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;throw new &lt;&#x2F;span&gt;&lt;span&gt;Error(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;未找到文本为 &amp;quot;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headerText&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;quot; 的标题&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试表格&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;cosmocode.io&#x2F;automation-practice-webtable&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;helper &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;WebTableHelper(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveCount&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;197&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;helper&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateCellValueReferenceToHeader&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Ben&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;你也可以像这样扩展它以验证更多的行，但你现在知道这段代码是如何运行的。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;希望你喜欢这个小技巧和提示系列。每当你遇到复杂的场景或需要执行的特定操作时，记得看看 Playwright 的文档，因为它总是提供你需要的信息。也许你会找到一些你不知道的非常实用的功能。&lt;&#x2F;p&gt;
&lt;p&gt;关注我们的下一篇文章，看看我们会分享哪些内容！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-tips-and-trick-4&#x2F;&quot;&gt;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-tips-and-trick-4&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>playwright自动化的一些最佳实践</title>
        <published>2024-09-27T00:00:00+00:00</published>
        <updated>2024-09-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-to-get-best-from-playwright-in-automation/"/>
        <id>https://playwright.itest.info/blog/how-to-get-best-from-playwright-in-automation/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-to-get-best-from-playwright-in-automation/">&lt;p&gt;Playwright 是一个强大的网页自动化和测试工具，支持多种浏览器和平台。为了充分发挥 Playwright 的优势，你需要遵循一些最佳实践，以确保测试代码易于管理、可靠且具备可扩展性。以下是一些提升 Playwright 测试自动化的关键建议。&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;使用页面对象模型（POM）&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;页面对象模型（POM）是一种设计模式，通过将页面交互与测试逻辑分离来组织测试代码。采用 POM 可以集中管理与页面的交互逻辑，若界面发生变化，只需更新一次页面对象，而无需修改多个测试。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;LoginPage {  
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{}
&lt;&#x2F;span&gt;&lt;span&gt;  
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{  
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;);  
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;);  
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#login-button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);  
&lt;&#x2F;span&gt;&lt;span&gt;  }  
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;避免硬编码值&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;尽量避免在测试中硬编码敏感信息或网址。使用环境变量或配置文件可以使测试更加灵活，并便于在不同环境中运行。这种做法提高了测试的可移植性，并使其能够适应环境的变化。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{  
&lt;&#x2F;span&gt;&lt;span&gt;  use: {  
&lt;&#x2F;span&gt;&lt;span&gt;    baseURL: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;BASE_URL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&amp;lt;your app URL&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,  
&lt;&#x2F;span&gt;&lt;span&gt;  }  
&lt;&#x2F;span&gt;&lt;span&gt;};  
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;strong&gt;利用fixture&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Fixture允许你设置一些公共状态，例如已登录用户或浏览器配置，这些状态可以在多个测试中重用。这样可以减少代码重复，提高测试的整洁性和执行速度。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;({  
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;loggedInPage&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{  
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);  
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;demo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);  
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test123&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);  
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#login-button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);  
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);  
&lt;&#x2F;span&gt;&lt;span&gt;  }  
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;带有登录用户的测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;loggedInPage &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在这里编写你的登录代码
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;&lt;strong&gt;并行运行测试&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;并行运行测试可以节省时间，尤其是当测试用例较多时。可以通过 &lt;code&gt;workers&lt;&#x2F;code&gt; 设置来配置同时运行的测试数量。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{  
&lt;&#x2F;span&gt;&lt;span&gt;  workers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;,  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 将同时运行 4 个测试用例  
&lt;&#x2F;span&gt;&lt;span&gt;};  
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;&lt;strong&gt;处理不稳定测试的重试机制&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;有时测试会因为网络问题或时间问题而失败。你可以配置测试在失败时自动重试，这样可以避免虚假失败并减少不稳定测试的数量。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{  
&lt;&#x2F;span&gt;&lt;span&gt;  retries: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这将在失败时重试两次  
&lt;&#x2F;span&gt;&lt;span&gt;};  
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;&lt;strong&gt;使用内置断言&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Playwright 提供内置断言，帮助你验证条件，例如文本内容或元素的可见性。利用 Playwright 的断言可以提高测试性能，并提供更好的错误报告。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;google.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);  
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#header&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Google!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;&lt;strong&gt;跨浏览器测试&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Playwright 支持在不同浏览器（如 Chromium、Firefox 和 WebKit）中运行测试。确保配置测试套件在所有支持的浏览器中运行，以捕捉特定浏览器的问题。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{  
&lt;&#x2F;span&gt;&lt;span&gt;  projects: [  
&lt;&#x2F;span&gt;&lt;span&gt;    { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;chromium&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, use: { browserName: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;chromium&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;} },  
&lt;&#x2F;span&gt;&lt;span&gt;    { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;firefox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, use: { browserName: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;firefox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;} },  
&lt;&#x2F;span&gt;&lt;span&gt;    { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;webkit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, use: { browserName: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;webkit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;} },  
&lt;&#x2F;span&gt;&lt;span&gt;  ],  
&lt;&#x2F;span&gt;&lt;span&gt;};  
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;8&quot;&gt;
&lt;li&gt;&lt;strong&gt;避免测试间的依赖&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;测试应该是独立的，能够任意顺序执行。避免编写依赖于其他测试结果或状态的测试。独立的测试更容易维护，也更可靠。&lt;&#x2F;p&gt;
&lt;ol start=&quot;9&quot;&gt;
&lt;li&gt;&lt;strong&gt;使用显式等待而非硬等待&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;与其使用固定延迟（&lt;code&gt;page.waitForTimeout()&lt;&#x2F;code&gt;），不如使用显式等待，例如 &lt;code&gt;page.waitForSelector()&lt;&#x2F;code&gt; 或 Playwright 的内置断言。显式等待确保在满足条件时立即继续执行测试，从而提高速度和可靠性。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForSelector&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#element&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { state: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;10&quot;&gt;
&lt;li&gt;&lt;strong&gt;利用浏览器上下文进行测试隔离&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;浏览器上下文允许你在同一浏览器内的隔离环境中运行测试，这样可以确保测试之间不会相互干扰，例如共享 cookies 或会话，从而提高可靠性和效率。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();  
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;11&quot;&gt;
&lt;li&gt;&lt;strong&gt;优化定位器&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;使用稳定且唯一的定位器与网页元素进行交互。避免使用容易失效的定位器，如类名或索引，应该选择更可靠的选项，例如文本或 &lt;code&gt;data-testid&lt;&#x2F;code&gt; 属性。稳定的定位器减少了用户界面变化时需要更新测试的频率。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button:has-text(&amp;quot;Submit&amp;quot;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);  
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-testid=&amp;quot;login-button&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;12&quot;&gt;
&lt;li&gt;&lt;strong&gt;合理配置超时&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;根据应用程序的性能调整全局或单个测试的超时设置。这可以避免由于超时而导致的测试失败，并确保测试能够灵活应对较慢的环境，例如 CI 流水线。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{  
&lt;&#x2F;span&gt;&lt;span&gt;  timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;30000&lt;&#x2F;span&gt;&lt;span&gt;,  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 超时设置为 30 * 1000 秒  
&lt;&#x2F;span&gt;&lt;span&gt;};  
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;遵循这些最佳实践可以帮助你构建一个强大且可扩展的 Playwright 测试自动化套件。通过编写清晰、可靠且易于维护的测试，你将提升测试的速度和质量，使应用程序更加稳定，并让开发过程更为高效。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Timeouts Against Flaky Tests: True Cases with Playwright</title>
        <published>2024-09-04T00:00:00+00:00</published>
        <updated>2024-09-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/timeouts-against-flaky-tests-true-cases-with-playwright-9f4f28d2c391/"/>
        <id>https://playwright.itest.info/blog/timeouts-against-flaky-tests-true-cases-with-playwright-9f4f28d2c391/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/timeouts-against-flaky-tests-true-cases-with-playwright-9f4f28d2c391/">&lt;h2 id=&quot;playwright-is-a-highly-reliable-tool-for-ui-testing-but-there-are-cases-when-some-tests-can-be-flaky-and-the-timeout-anti-pattern-fixes-this&quot;&gt;Playwright is a highly reliable tool for UI testing, but there are cases when some tests can be flaky, and the timeout anti-pattern fixes this!&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;timeouts-against-flaky-tests-true-cases-with-playwright-9f4f28d2c391&#x2F;img&#x2F;1727601491.753761&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fill:88:88&#x2F;2*bQ4xmdoPtrdMchCQCia7iQ.png&quot; alt=&quot;Image 1: Andrey Enin&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Flaky tests are unreliable tests that occasionally fail, but the reason for the failures is unclear. These problems may be in the application, the environment, the testing tools used, or all of them together. Due to their complexity when interacting with the browser, UI testing tools have long been considered quite unreliable instruments among QA engineers.&lt;&#x2F;p&gt;
&lt;p&gt;However, when comparing tools based on &lt;a href=&quot;https:&#x2F;&#x2F;www.selenium.dev&#x2F;documentation&#x2F;webdriver&#x2F;&quot;&gt;Selenium WebDriver&lt;&#x2F;a&gt; and those based on &lt;a href=&quot;https:&#x2F;&#x2F;chromedevtools.github.io&#x2F;devtools-protocol&#x2F;&quot;&gt;Crome DevTools Protocol&lt;&#x2F;a&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;pptr.dev&#x2F;&quot;&gt;Puppeteer&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;&quot;&gt;Playwright&lt;&#x2F;a&gt;), the last one turns out to be more accurate.&lt;&#x2F;p&gt;
&lt;p&gt;Automation engineers can write bad and good tests with both tools depending on their grades. But no matter how good the tool is, the application itself and the test environment significantly impact autotests.&lt;&#x2F;p&gt;
&lt;p&gt;Even adhering to &lt;a href=&quot;https:&#x2F;&#x2F;adequatica.medium.com&#x2F;principles-of-writing-automated-tests-a2b72218264c&quot;&gt;the best practices in writing autotests&lt;&#x2F;a&gt; &lt;strong&gt;to prevent false falls, unreliable results, and unwanted errors&lt;&#x2F;strong&gt; sometimes requires breaking established principles and setting exceptions in the code.&lt;&#x2F;p&gt;
&lt;p&gt;Below are a few cases that I encountered during the automation of several production services with Playwright (actually, these cases can be attributed to any other tool, too):&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;adequatica.medium.com&#x2F;timeouts-against-flaky-tests-true-cases-with-playwright-9f4f28d2c391#d65c&quot;&gt;UI Animations&lt;&#x2F;a&gt;;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;adequatica.medium.com&#x2F;timeouts-against-flaky-tests-true-cases-with-playwright-9f4f28d2c391#41b5&quot;&gt;Drawing on Canvas&lt;&#x2F;a&gt;;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;adequatica.medium.com&#x2F;timeouts-against-flaky-tests-true-cases-with-playwright-9f4f28d2c391#851d&quot;&gt;Sleep Timeouts in CI in the Cloud&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;&#x2F;strong&gt; The following may be very controversial. If you want to adopt the suggestions in the text, you should be aware of what you are doing.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;1-ui-animations&quot;&gt;1. UI Animations&lt;&#x2F;h2&gt;
&lt;p&gt;To prevent flakiness, Playwright has built-in auto-witing machinery inside appropriate actions&#x2F;commands — it «&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;actionability&quot;&gt;performs a range of actionability checks on the elements before making actions to ensure these actions behave as expected&lt;&#x2F;a&gt;».&lt;&#x2F;p&gt;
&lt;p&gt;But sometimes, &lt;strong&gt;very rarely,&lt;&#x2F;strong&gt; the Playwright may not click (or misclick) on truly visible and accessible elements.&lt;&#x2F;p&gt;
&lt;p&gt;The Playwright’s auto-waiting for actions is great and makes developing autotests easy. In 99%, it is absolutely accurate, but for the remaining percentages for highly customized interfaces, it may make a mistake — not because it is slow or unreliable but because it is fast and too precise.&lt;&#x2F;p&gt;
&lt;p&gt;This may suddenly appear during some animations of menus, &lt;a href=&quot;https:&#x2F;&#x2F;m2.material.io&#x2F;components&#x2F;navigation-drawer&quot;&gt;drawers&lt;&#x2F;a&gt;, popovers, overlays, and other pop-up elements with transitions and appearance animations. When the element is already on the screen, it is already interactable, but the animation is not over yet, and the click on the element has to go to another place. The difference in a few milliseconds&#x2F;pixels makes the test flaky.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;The dropdown menu is already interactable, but clicking on the menu item may lead to a misclick&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;&#x2F;strong&gt; Add unconditional timeout before or after (depending on a test scenario) flaky click.&lt;&#x2F;p&gt;
&lt;p&gt;Yes, this is an anti-pattern, and &lt;a href=&quot;https:&#x2F;&#x2F;adequatica.medium.com&#x2F;principles-of-writing-automated-tests-a2b72218264c#b9cd&quot;&gt;you should avoid pauses inside tests&lt;&#x2F;a&gt;, but sometimes, you have to sacrifice a clear code in favor of reliability, especially in CI.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-drawing-on-canvas&quot;&gt;2. Drawing on Canvas&lt;&#x2F;h2&gt;
&lt;p&gt;Interaction with &lt;code&gt;&amp;lt;[canvas](https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTML&#x2F;Element&#x2F;canvas)&amp;gt;&lt;&#x2F;code&gt; element can also be tricky, especially if it is interactive and contains logic. In my example, I have a map (of course, as a canvas) from a third-party vendor and need to draw an object on it by making a certain number of clicks.&lt;&#x2F;p&gt;
&lt;p&gt;The problem is that the element is actionable for Playwright from the moment it appears on the page, but the actual actionable state of the element is indefinable by its locator.&lt;&#x2F;p&gt;
&lt;p&gt;That is a timeline for loading the element on a page:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Canvas element is attached and visible on the page (the element is actionable for Playwright), but nothing is rendered inside:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;canvas class=&quot;map-canvas&quot; tabindex=&quot;0&quot; aria-label=&quot;Map&quot; role=&quot;region&quot; width=&quot;1470&quot; height=&quot;956&quot; style=&quot;width: 1470px; height: 956px; cursor: crosshair;&quot;&amp;gt;&amp;lt;&#x2F;canvas&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;2. Canvas element has rendered content but is not yet actionable for internal logic:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;canvas class=&quot;map-canvas&quot; tabindex=&quot;0&quot; aria-label=&quot;Map&quot; role=&quot;region&quot; width=&quot;1470&quot; height=&quot;956&quot; style=&quot;width: 1470px; height: 956px; cursor: crosshair;&quot;&amp;gt;&amp;lt;&#x2F;canvas&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;3. Canvas element is ready for action:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;canvas class=&quot;map-canvas&quot; tabindex=&quot;0&quot; aria-label=&quot;Map&quot; role=&quot;region&quot; width=&quot;1470&quot; height=&quot;956&quot; style=&quot;width: 1470px; height: 956px; cursor: crosshair;&quot;&amp;gt;&amp;lt;&#x2F;canvas&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As you can notice, &lt;strong&gt;the HTML element is the same for all states&lt;&#x2F;strong&gt;, but when Playwright is already getting ready to perform clicks, the element itself is not. The final state varies depending on the environment (local or CI run), machine (CI runner) performance, and network bandwidth.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;&#x2F;strong&gt; Add unconditional timeout before the first click.&lt;&#x2F;p&gt;
&lt;p&gt;However, this test case does not imply having any mocks, and it is much easier to add a single timeout rather than develop a highly complicated (and potentially fragile) solution for determining a single element’s state. Here, a few seconds in one test is sacrificed in favor of &lt;em&gt;relative&lt;&#x2F;em&gt; reliability.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-sleep-timeouts-in-ci-in-the-cloud&quot;&gt;3. Sleep Timeouts in CI in the Cloud&lt;&#x2F;h2&gt;
&lt;p&gt;If your frontend application has its own timeouts (actual business logic may require freaky periods of «sleep»), then your tests may be affected by it.&lt;&#x2F;p&gt;
&lt;p&gt;If you test UI with a logic that contains &lt;code&gt;[setTimeout()](https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;setTimeout)&lt;&#x2F;code&gt; in the code like this:&lt;&#x2F;p&gt;
&lt;p&gt;async function sleep(customTimeout: number): Promise&amp;lt;void&amp;gt; {&lt;br &#x2F;&gt;
return new Promise(resolve =&amp;gt; setTimeout(resolve, customTimeout));&lt;br &#x2F;&gt;
}&lt;&#x2F;p&gt;
&lt;p&gt;Then, you may encounter the problem that &lt;code&gt;setTimeout()&lt;&#x2F;code&gt; can be executed longer than the specified time. It sounds crazy, but this behavior is most pronounced in CI runners (from GitHub Actions to custom runners on dedicated servers) during testing and does not reproduce in production!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;&#x2F;strong&gt; Add unconditional timeout that is at least three times longer than the logic under test.&lt;&#x2F;p&gt;
&lt;p&gt;If it waits for a timeout, it waits&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, that is not a bug. I even consulted with DevOps and CI’s tech support, who confirmed this behavior:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;JavaScript does not guarantee that the setTimeout will work on time. Therefore, there may be many reasons…&lt;&#x2F;p&gt;
&lt;p&gt;This may be due to the different virtualization technologies. If GitHub has virtual machines, then processor time is distributed more justifiably than bare metal ones…&lt;&#x2F;p&gt;
&lt;p&gt;Perhaps the JavaScript runtime detects virtualization technology and cuts the minimum timeout to prevent exploits like [Spectre](https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Spectre_&#x2F;(security_vulnerability) and &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Meltdown_&#x2F;(security_vulnerability&#x2F;)&quot;&gt;Meltdown&lt;&#x2F;a&gt;…&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;timeout-tips&quot;&gt;Timeout Tips&lt;&#x2F;h2&gt;
&lt;p&gt;Here are a few principles of organizing timeouts inside test infrastructure:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Make timeout’s value as constant.&lt;&#x2F;strong&gt; In my experience, 300 ms is the optimal time waiting for something on the UI.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;export const UNCONDITIONAL_TIMEOUT = 300;&lt;&#x2F;p&gt;
&lt;p&gt;Eventually, you can divide or multiply the constant depending on the test’s context.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Add a comment for each case of using timeout.&lt;&#x2F;strong&gt; This shows for all developers that using such a method is an exception to the «autotesting rules» and names the reason.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&#x2F;&#x2F; Wait for input to be applied in the select&lt;br &#x2F;&gt;
await page.waitForTimeout(UNCONDITIONAL_TIMEOUT);&lt;&#x2F;p&gt;
&lt;p&gt;Official Playwright’s documentation recommends you «&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page#page-wait-for-timeout&quot;&gt;never wait for timeout in production&lt;&#x2F;a&gt;» and use it only for debugging because tests that wait for time are inherently flaky. But here, I use &lt;code&gt;waitForTimeout()&lt;&#x2F;code&gt; exactly for the opposite, despite the fact that &lt;strong&gt;tests with unconditional waits are truthfully flaky.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;But how often have I been allowed to do this in production? In a recent project, I had 50 tests with more than 200 test steps and more than 120 clicks. Two tests had seven waits for timeouts, and three page objects had six timeouts. Not so much, although, of course, I would like to have zero.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Read more:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.browserstack.com&#x2F;test-observability&#x2F;features&#x2F;test-reporting&#x2F;what-is-flaky-test&quot;&gt;What is Flaky Test&lt;&#x2F;a&gt;?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;semaphoreci.com&#x2F;blog&#x2F;flaky-tests-playwright&quot;&gt;How to Avoid Flaky Tests in Playwright&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何使用Playwright的用户优先选择器提升端到端测试的稳定性</title>
        <published>2024-08-20T00:00:00+00:00</published>
        <updated>2024-08-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-user-first-selectors/"/>
        <id>https://playwright.itest.info/blog/playwright-user-first-selectors/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-user-first-selectors/">&lt;p&gt;在使用Playwright进行网站端到端测试和监控时,选择恰当的定位器至关重要。优秀的定位器能帮助我们创建更稳定、更可靠的测试。本文将深入探讨用户优先定位器,并介绍如何通过筛选定位器来打造更健壮的测试。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;chang-jing-shi-li&quot;&gt;场景示例&lt;&#x2F;h2&gt;
&lt;p&gt;让我们从一个简单的网页开始,它包含一个标题、一个按钮和一个状态更新框。当用户点击按钮时,状态会更新并伴随彩纸效果。下面我们来看看如何测试这个页面。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-user-first-selectors&#x2F;img&#x2F;1727600229.247249ZsVShkaF0TcGJItT_ScreenShot2024-08-20at7.22.22PM.png&quot; alt=&quot;图1:简单的演示页面&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wei-shen-me-wo-men-yao-bi-mian-shi-yong-cssxuan-ze-qi-ding-wei-ye-mian-yuan-su&quot;&gt;为什么我们要避免使用CSS选择器定位页面元素&lt;&#x2F;h3&gt;
&lt;p&gt;如果你习惯于使用jQuery等传统的自动化和页面操作工具,你可能会倾向于使用CSS选择器。确实,如果我们检查这个页面,会发现按钮上有一个可以用于选择的class。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-user-first-selectors&#x2F;img&#x2F;1727600231.109538ZsVUOEaF0TcGJIvU_ScreenShot2024-08-20at7.32.25PM.png&quot; alt=&quot;图2:打开检查器的演示页面&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我们可以用&lt;code&gt;await page.locator(&quot;button.button-frontpage-style&quot;).click()&lt;&#x2F;code&gt;这样的代码来点击按钮,这种方法虽然可行,但并不推荐。&lt;&#x2F;p&gt;
&lt;p&gt;那么,这种方法有什么问题呢?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;用户看页面是为了找按钮,而不是CSS类。使用CSS选择器的测试无法真实模拟用户行为。&lt;&#x2F;li&gt;
&lt;li&gt;如果前端开发者出于样式考虑将类名改为&lt;code&gt;button-hero-panel-style&lt;&#x2F;code&gt;,我们的测试就会出现假阳性,在实际没有问题的情况下报错。&lt;&#x2F;li&gt;
&lt;li&gt;如果按钮文本来自CMS且出现问题,变成了&quot;HEROBUTTON_TXT&quot;,我们的测试仍会通过,尽管用户界面已经损坏,这就是假阴性。&lt;&#x2F;li&gt;
&lt;li&gt;基于以上原因,Playwright项目建议避免使用CSS定位器。遵循项目设定的标准是明智之选!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;pao-qi-cssxuan-ze-qi-yong-bao-yong-hu-you-xian-ding-wei-qi&quot;&gt;抛弃CSS选择器,拥抱用户优先定位器&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright提供了一系列基于页面角色的&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators&quot;&gt;定位器&lt;&#x2F;a&gt;,这比基于CSS或HTML的查找方式更贴近实际功能。使用内置的&lt;code&gt;getByRole&lt;&#x2F;code&gt;、&lt;code&gt;getByText&lt;&#x2F;code&gt;和&lt;code&gt;getByLabel&lt;&#x2F;code&gt;等定位器,它们在测试中的表现与用户实际操作一致,无论用户使用的是小众浏览器、移动设备,还是屏幕阅读器。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yong-hu-you-xian-ding-wei-qi-shi-zhan&quot;&gt;用户优先定位器实战&lt;&#x2F;h2&gt;
&lt;p&gt;让我们用&lt;code&gt;getByRole&lt;&#x2F;code&gt;替换&lt;code&gt;page.locator&lt;&#x2F;code&gt;,通过按钮的角色和可访问名称来定位:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;await page.getByRole(&#x27;button&#x27;, { name: &#x27;click me&#x27; }).click();&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;你可以使用Chrome开发者工具来检查元素,找出它们的可访问性角色和名称。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-user-first-selectors&#x2F;img&#x2F;1727600232.792445ZsVaWEaF0TcGJI3E_ScreenShot2024-08-20at8.08.53PM.png&quot; alt=&quot;图3:Chrome开发者工具的可访问性区域&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这种方法确保了即使类名或HTML结构发生变化,测试仍然稳定可靠。&lt;&#x2F;p&gt;
&lt;p&gt;值得一提的是,Playwright的名称匹配不区分大小写,并支持子字符串匹配。这意味着你可以将选择器简化为&lt;code&gt;{ name: &#x27;click&#x27; }&lt;&#x2F;code&gt;,即使营销团队将按钮文本改为&quot;Click me now!&quot;,测试也能通过。&lt;&#x2F;p&gt;
&lt;p&gt;这种改进使得当按钮文本变成&quot;HEROBUTTON_TXT&quot;这样的无意义内容时,测试会如预期那样失败。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dang-getbyrolehe-getbylabelshi-xiao-shi-gai-zen-me-ban&quot;&gt;当&lt;code&gt;getByRole&lt;&#x2F;code&gt;和&lt;code&gt;getByLabel&lt;&#x2F;code&gt;失效时该怎么办&lt;&#x2F;h3&gt;
&lt;p&gt;我之所以推荐用户优先定位器,部分原因在于当你发现这些选择器在页面上不起作用时会发生什么。如果页面上的许多元素没有设置角色和标签,或者所有元素都使用相同的标签和角色,这表明你的页面存在严重的可访问性问题。没有唯一标识的元素意味着屏幕阅读器和键盘导航工具将无法正常工作,导致很多用户无法顺利浏览你的网站。这应该成为开发团队和质量保证团队讨论如何改善页面结构的契机,不仅能解决测试问题,还能提升所有用户的使用体验。&lt;&#x2F;p&gt;
&lt;p&gt;短期内,你可以查看Playwright提供的所有定位器,肯定能找到合适的方法定位任何元素。但我建议你将此作为未来讨论技术债务的起点。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ru-he-chu-li-duo-zhong-pi-pei-yuan-su&quot;&gt;如何处理多重匹配元素&lt;&#x2F;h2&gt;
&lt;p&gt;当一个定位器匹配到多个元素时,Playwright的严格模式会触发失败。例如,使用&lt;code&gt;getByRole(&#x27;button&#x27;)&lt;&#x2F;code&gt;可能会匹配到多个按钮。你可能会看到这样的错误信息:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;Error: locator.click: Error: strict mode violation: getByRole(&#x27;button&#x27;, { name: &#x27;click&#x27; }) resolved to 2 elements:&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;注意,在严格模式下,即使第一个结果或所有结果都能通过测试的其他部分,这个测试也会失败。解决方法是指定位置或筛选元素。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ji-yu-wei-zhi-de-xuan-ze&quot;&gt;基于位置的选择&lt;&#x2F;h3&gt;
&lt;p&gt;当首次遇到这个错误时,最简单的解决方案是指定列表中的位置。最常见的做法是选择第一个结果:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;await page.getByRole(&#x27;button&#x27;).first().click();&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我经常遇到上述错误,以至于养成了习惯,几乎每次写定位器时都会添加&lt;code&gt;first()&lt;&#x2F;code&gt;。😅&lt;&#x2F;p&gt;
&lt;p&gt;如果你想更精确地指定列表位置,可以使用:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;await page.getByRole(&#x27;button&#x27;).nth(3).click();&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;.nth()&lt;&#x2F;code&gt;函数是从0开始索引的,所以&lt;code&gt;.nth(3)&lt;&#x2F;code&gt;会选择结果中的第四个项目。位置选择器虽然有效,但可能不够稳定。如果页面内容是动态的,选择第一个以外的结果可能会导致假阳性。为了确保在动态列表中准确定位到正确的元素,我们需要进行更细致的元素筛选。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwrightzhong-de-yuan-su-shai-xuan-ji-qiao&quot;&gt;Playwright中的元素筛选技巧&lt;&#x2F;h2&gt;
&lt;p&gt;想象一个电商网站界面,展示了多个商品,但只有部分可购买。在这种情况下,仅依赖位置选择器进行测试显然不够可靠。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-user-first-selectors&#x2F;img&#x2F;1727600234.549546ZsViYUaF0TcGJI6m_ScreenShot2024-08-20at8.42.30PM.png&quot; alt=&quot;图4:只有一个商品标记为&amp;quot;可购买&amp;quot;的电商页面&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;检查页面后,我们发现这些是列表项。以下是一个更智能的选择器,它首先确保商品可购买,然后再定位到&quot;购买&quot;按钮:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;listitem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;({ hasText: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;available&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;buy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个测试现在能够在用户预期的情况下失败:当所有商品都显示为售罄时。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ding-wei-qi-ce-lue-zui-jia-shi-jian&quot;&gt;定位器策略最佳实践&lt;&#x2F;h2&gt;
&lt;p&gt;对于定位器,没有放之四海而皆准的解决方案。以下是一些实用建议:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;避免依赖实现细节&lt;&#x2F;strong&gt;: 使用反映用户行为而非HTML结构的定位器。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;善用内置定位器&lt;&#x2F;strong&gt;: &lt;code&gt;getByRole&lt;&#x2F;code&gt;、&lt;code&gt;getByText&lt;&#x2F;code&gt;等定位器能显著提高测试的稳定性和可维护性。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;合理使用数据测试ID&lt;&#x2F;strong&gt;: 在复杂场景中,使用数据测试ID可以简化定位器并提高测试可靠性。但要注意,仅用于调试的页面内容应该谨慎使用。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在k8s上跑playwright</title>
        <published>2024-07-28T00:00:00+00:00</published>
        <updated>2024-07-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/containerized-browser-testing-with-playwright-on-kubernetes/"/>
        <id>https://playwright.itest.info/blog/containerized-browser-testing-with-playwright-on-kubernetes/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/containerized-browser-testing-with-playwright-on-kubernetes/">&lt;p&gt;在DevOps实践中，代码通常部署在多个环境中，如开发、QA、预发布等。当使用Kubernetes在不同命名空间管理这些环境时，复杂度会迅速增加。为解决这个问题，可扩展的全面测试方案变得至关重要。本文将探讨Playwright和Kubernetes的强大组合，它将容器化浏览器测试带到现代DevOps实践的前沿。让我们一起来实践一下。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;she-zhi&quot;&gt;设置&lt;&#x2F;h2&gt;
&lt;p&gt;要在Kubernetes中启动容器化的Playwright测试，请按以下步骤操作。本演示使用了一个简单的Playwright UI脚本，可参考&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;thananauto&#x2F;playwright-ui-typescript&quot;&gt;这个仓库&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;chuang-jian-dockerfile&quot;&gt;创建Dockerfile&lt;&#x2F;h3&gt;
&lt;p&gt;让我们创建一个包含所有浏览器和Playwright依赖的Dockerfile：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;dockerfile&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-dockerfile &quot;&gt;&lt;code class=&quot;language-dockerfile&quot; data-lang=&quot;dockerfile&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 使用任意Node基础镜像
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; node:20-bookworm
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 复制当前工作目录，请添加.dockerignore以忽略不必要的文件
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;WORKDIR &lt;&#x2F;span&gt;&lt;span&gt;&#x2F;app
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span&gt; . &#x2F;app
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;RUN &lt;&#x2F;span&gt;&lt;span&gt;npm cache clean --force
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 仅安装Chromium浏览器及其依赖
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;RUN &lt;&#x2F;span&gt;&lt;span&gt;npm install
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;RUN &lt;&#x2F;span&gt;&lt;span&gt;npx -y playwright install --with-deps chromium
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;ENTRYPOINT &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这里我们只添加了Chromium浏览器的依赖。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;gou-jian-bing-tui-song-jing-xiang-dao-docker-hub&quot;&gt;构建并推送镜像到Docker Hub&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;#登录Docker Hub
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docker&lt;&#x2F;span&gt;&lt;span&gt; login&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -u&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;DOCKER_USERNAME&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;DOCKER_PASSWORD&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 构建Docker镜像
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docker&lt;&#x2F;span&gt;&lt;span&gt; build&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -t&lt;&#x2F;span&gt;&lt;span&gt; playwright-ui-typescript .
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 为容器仓库标记镜像
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docker&lt;&#x2F;span&gt;&lt;span&gt; tag playwright-ui-typescript &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;DOCKER_USERNAME&lt;&#x2F;span&gt;&lt;span&gt;}&#x2F;playwright-ui-typescript:latest
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 推送镜像到容器仓库
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docker&lt;&#x2F;span&gt;&lt;span&gt; push &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;DOCKER_USERNAME&lt;&#x2F;span&gt;&lt;span&gt;}&#x2F;playwright-ui-typescript:latest
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;镜像推送到Docker Hub后，请在&lt;a href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;&quot;&gt;hub&lt;&#x2F;a&gt;上检查。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;containerized-browser-testing-with-playwright-on-kubernetes&#x2F;_attachments&#x2F;1723010955.41450121*ATbWSCLxF-NxyLhpXJ40Cw.png&quot; alt=&quot;Image 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;chuang-jian-kubernetesqing-dan-wen-jian&quot;&gt;创建Kubernetes清单文件&lt;&#x2F;h3&gt;
&lt;p&gt;为了在Pod内执行测试，我们需要一个存储空间来获取和收集测试结果，即使节点或Pod不可用。为此，我们需要设置持久卷（PV）和持久卷声明（PVC）。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;1-chi-jiu-juan-pv&quot;&gt;1. 持久卷（PV）&lt;&#x2F;h4&gt;
&lt;p&gt;持久卷清单支持多种卷映射选项，包括CSI、iSCSI、HostPath、NFS、FC和基于主机的存储。让我们看一个例子：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;apiVersion&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;v1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PersistentVolume
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;logs-persistent-vol
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;labels&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;local
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;storageClassName&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;manual
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;capacity&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;storage&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;256Mi
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;volumeMode&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Filesystem
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;accessModes&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ReadWriteMany
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;persistentVolumeReclaimPolicy&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Retain
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;hostPath&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;test&#x2F;output&#x2F;result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 如果主机上不存在此路径，将自动生成
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;注意：持久卷不属于集群中的任何命名空间。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;2-chi-jiu-juan-sheng-ming-pvc&quot;&gt;2. 持久卷声明（PVC）&lt;&#x2F;h4&gt;
&lt;p&gt;这个持久卷声明（PVC）从主机请求存储资源，并将分配的卷映射到Pod，实现数据持久化和容器内访问。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;apiVersion&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;v1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PersistentVolumeClaim
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;namespace&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui-test-job
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;logs-persistent-vol-claim
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;storageClassName&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;manual
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;resources&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;requests&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;storage&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;256Mi
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;volumeMode&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Filesystem
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;accessModes&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ReadWriteMany
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;注意：请检查集群的存储类名称。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;3-job&quot;&gt;3. Job&lt;&#x2F;h4&gt;
&lt;p&gt;Kubernetes中的Job类似于Pod，但有特定用途。与可能失败并重启的Pod不同，Job确保任务（如执行测试）只完成一次，无论Pod的状态如何。让我们看看Job清单文件：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;apiVersion&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;batch&#x2F;v1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Job
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;namespace&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui-test-job
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui-test
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;labels&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;app&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui-jobs
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 任务完成或结束后自动删除的等待时间
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;ttlSecondsAfterFinished&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;100
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 将backoffLimit设为0，否则此任务将持续执行直到状态为完成或结束
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;backoffLimit&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;template&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;metadata&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui-test-job
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;restartPolicy&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Never
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;containers&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;sample-ui-test
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;thanandock&#x2F;playwright-ui-typescript:latest
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;-c&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;volumeMounts&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;            - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;storage
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;mountPath&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;app&#x2F;playwright-report
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 容器内的报告生成路径
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;volumes&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;storage
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;persistentVolumeClaim&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;claimName&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;logs-persistent-vol-claim
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;bu-shu-dao-kubernetes&quot;&gt;部署到Kubernetes&lt;&#x2F;h3&gt;
&lt;p&gt;现在是时候将这些Kubernetes清单部署到集群了。在此之前，将所有文件保存在一个文件夹中，然后运行以下命令：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 创建命名空间
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;kubectl&lt;&#x2F;span&gt;&lt;span&gt; create namespace ui-test-job
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 将当前上下文设置为新创建的命名空间
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;kubectl&lt;&#x2F;span&gt;&lt;span&gt; config set-context&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --current --namespace&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;ui-test-job
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 部署所有清单文件
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;kubectl&lt;&#x2F;span&gt;&lt;span&gt; apply&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -f&lt;&#x2F;span&gt;&lt;span&gt; .
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;containerized-browser-testing-with-playwright-on-kubernetes&#x2F;_attachments&#x2F;1723010958.5846061*2dWQ2OTbQQmJDUhKMU5OcQ.png&quot; alt=&quot;Image 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;成功创建清单文件&lt;&#x2F;p&gt;
&lt;h3 id=&quot;jian-kong-he-huo-qu-jie-guo&quot;&gt;监控和获取结果&lt;&#x2F;h3&gt;
&lt;p&gt;通过执行&lt;code&gt;kubectl get all&lt;&#x2F;code&gt;命令检查任务状态&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;containerized-browser-testing-with-playwright-on-kubernetes&#x2F;_attachments&#x2F;1723010960.69324111*07Ske_2AhJ_zpzrV0DgQDA.png&quot; alt=&quot;Image 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;任务的整体状态&lt;&#x2F;p&gt;
&lt;p&gt;使用&lt;code&gt;kubectl logs --follow pods&#x2F;&amp;lt;name&amp;gt;&lt;&#x2F;code&gt;查看容器日志&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;containerized-browser-testing-with-playwright-on-kubernetes&#x2F;_attachments&#x2F;1723010962.8042251*l1-nN6DRD2lK6L8t9mK69g.png&quot; alt=&quot;Image 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;显示测试执行的容器日志&lt;&#x2F;p&gt;
&lt;p&gt;当Pod崩溃、初始化错误或配置错误时，&lt;code&gt;kubectl describe pod &amp;lt;name&amp;gt;&lt;&#x2F;code&gt;命令非常有用：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;containerized-browser-testing-with-playwright-on-kubernetes&#x2F;_attachments&#x2F;1723010964.89831191*rhkF9VhkqvwQT-aoGEX4ig.png&quot; alt=&quot;Image 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;描述Pod状态&lt;&#x2F;p&gt;
&lt;p&gt;容器的卷通过持久卷声明与主机持久卷映射&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;containerized-browser-testing-with-playwright-on-kubernetes&#x2F;_attachments&#x2F;1723010967.0428981*q_T2zsCXAlGgykIM6fi_ww.png&quot; alt=&quot;Image 8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;容器卷通过PVC挂载到主机卷&lt;&#x2F;p&gt;
&lt;p&gt;容器内发生的事件列表&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;containerized-browser-testing-with-playwright-on-kubernetes&#x2F;_attachments&#x2F;1723010969.2818741*8y5bxj2Zc_-Ykn7eqtPpww.png&quot; alt=&quot;Image 9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Pod内发生的事件列表&lt;&#x2F;p&gt;
&lt;p&gt;在下面的截图中，我们可以看到节点中不同部署的不同时间戳文件夹，即使Pod在一段时间后被删除，这些结果也将持久保存。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;containerized-browser-testing-with-playwright-on-kubernetes&#x2F;_attachments&#x2F;1723010971.436711*dDmsP2ysfPainjR2defSTA.png&quot; alt=&quot;Image 10&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;即使Pod被删除，HTML结果仍在主机上可用&lt;&#x2F;p&gt;
&lt;h2 id=&quot;can-kao-zi-liao&quot;&gt;参考资料：&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;示例代码可在&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;thananauto&#x2F;playwright-ui-typescript.git&quot;&gt;这里&lt;&#x2F;a&gt;找到。&lt;&#x2F;li&gt;
&lt;li&gt;有关Docker化Playwright镜像，请参考&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;docker&quot;&gt;文档&lt;&#x2F;a&gt;。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;jie-lun&quot;&gt;结论&lt;&#x2F;h2&gt;
&lt;p&gt;通过将Playwright容器化并部署在Kubernetes上，团队可以实现可扩展、隔离且高效的测试环境。这种方法不仅简化了测试过程，还与现代CI&#x2F;CD管道无缝集成，确保以更快的速度实现高质量的发布。&lt;&#x2F;p&gt;
&lt;p&gt;祝学习愉快！🚀&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@thananjayan1988&#x2F;containerized-browser-testing-with-playwright-on-kubernetes-09743e5d2362&quot;&gt;原文&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何为playwright项目添加强类型检查和Linting</title>
        <published>2024-07-25T00:00:00+00:00</published>
        <updated>2024-07-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-type-checking-and-linting/"/>
        <id>https://playwright.itest.info/blog/playwright-type-checking-and-linting/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-type-checking-and-linting/">&lt;p&gt;如果你在使用端到端测试或者&lt;a href=&quot;https:&#x2F;&#x2F;www.checklyhq.com&#x2F;product&#x2F;synthetic-monitoring&#x2F;&quot;&gt;综合监控&lt;&#x2F;a&gt;，很可能你正在使用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;&quot;&gt;Microsoft的Playwright&lt;&#x2F;a&gt;。如果你的工具链中包含Playwright，你可能也采用了TypeScript。这是个简单的选择，因为它提供了稳定的自动补全和类型安全。&lt;&#x2F;p&gt;
&lt;p&gt;有了这套配置，你就可以享受出色的开发体验(DX)，并且在重构不断增长的代码库时，不用担心因为TypeScript的类型检查而出现运行时异常，对吧？&lt;strong&gt;大错特错！&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这里有&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-typescript#introduction&quot;&gt;Playwright文档中的一段话&lt;&#x2F;a&gt;：&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;注意，Playwright不会检查类型，即使存在非关键的TypeScript编译错误，它也会运行测试。&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;没错！当你运行&lt;code&gt;npx playwright test&lt;&#x2F;code&gt;时，Playwright会把你的&lt;code&gt;*.spec.ts&lt;&#x2F;code&gt;文件转换成JavaScript并运行它们。这个过程中没有类型检查。&lt;&#x2F;p&gt;
&lt;p&gt;你可以自己试试。这里有一个简单的spec.ts文件示例。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ⚠️ 这个示例代码包含错误！
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 请不要直接复制粘贴。
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test with a type error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;playwright.dev&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;heading&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Installation&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;  ).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisibles&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个测试包含一个明显的错误，如果你运行&lt;code&gt;npx playwright test&lt;&#x2F;code&gt;，你会看到以下结果。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-type-checking-and-linting&#x2F;_attachments&#x2F;1723012175.111678ZqDFkR5LeNNTxcw0_playwright-missed-type-error.png&quot; alt=&quot;图1：运行npx playwright test的结果，显示一个断言中存在拼写错误。&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;糟糕！&lt;code&gt;toBeVisible()&lt;&#x2F;code&gt;断言拼写错误，导致测试失败。TypeScript不是应该防止这种情况吗？是的，这正是类型检查的用途，但让我再强调一遍：&lt;strong&gt;Playwright不会检查你的类型，也没有内置的类型安全机制。&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;但是当你编写端到端测试时，你确实看到了类型错误；这是怎么回事呢？&lt;&#x2F;p&gt;
&lt;p&gt;如果你使用现代编辑器，TypeScript通常已经集成其中，会在后台为任何打开的.ts文件运行。你编写测试时，会得到一些提示，享受自动补全功能，当出错时会看到常见的红色波浪线。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-type-checking-and-linting&#x2F;_attachments&#x2F;1723012178.803118ZqDF5x5LeNNTxcw9_typescript-error-in-editor.png&quot; alt=&quot;图2：VS Code显示Playwright测试中的类型错误。&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;你的编辑器显示TypeScript错误是因为它善意地为你做了繁重的工作。而Playwright则没有为你提供保护。它编译你的代码时不关心是否存在类型不匹配或&quot;undefined is not a function&quot;等错误。Playwright仍会运行你的测试。没有安全保障，也没有额外帮助。&lt;&#x2F;p&gt;
&lt;p&gt;这个&quot;缺失的功能&quot;对小项目来说可能没问题，但请记住，&lt;strong&gt;对于大型复杂项目，类型安全是无价的&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;想象一个高级测试套件，包含数百个测试、数十个POM（页面对象模型）以及更多实用函数：当你做一个小改动时，你可能只会在本地运行部分测试，然后推送代码到CI&#x2F;CD运行所有测试。当你使用没有类型检查的TypeScript时，你只有在CI流水线启动浏览器运行测试后才会发现拼写错误和重构问题。你会浪费时间，而浪费多少取决于你项目的复杂程度。&lt;&#x2F;p&gt;
&lt;p&gt;让我们消除这种不确定性，重新让Playwright项目变得类型安全，并添加&lt;a href=&quot;https:&#x2F;&#x2F;typescript-eslint.io&#x2F;&quot;&gt;typescript-eslint&lt;&#x2F;a&gt;来避免最常见的Playwright错误。&lt;&#x2F;p&gt;
&lt;p&gt;让我们开始吧！&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;checkly&#x2F;playwright-examples&#x2F;tree&#x2F;main&#x2F;pwt-playwright-type-check-and-lint&quot;&gt;最终代码在GitHub上&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;使用&lt;code&gt;npm init playwright@latest&lt;&#x2F;code&gt;命令启动新的Playwright项目后，你会发现引导项目只有很少的依赖项。值得一提的只有&lt;code&gt;@playwright&#x2F;test&lt;&#x2F;code&gt;。尽管我喜欢精简的项目，但我更看重开发安全性。&lt;&#x2F;p&gt;
&lt;p&gt;让我们继续安装TypeScript作为新的&lt;code&gt;devDependency&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 安装`typescript`作为devDependency
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; install&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --save-dev&lt;&#x2F;span&gt;&lt;span&gt; typescript
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;你的&lt;code&gt;package.json&lt;&#x2F;code&gt;现在应该如下所示。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pwt-playwright-type-check-and-lint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;1.0.0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;index.js&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {},
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;keywords&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [],
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;author&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;license&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ISC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;devDependencies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;^1.45.3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@types&#x2F;node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;^20.14.11&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;typescript&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;^5.5.4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;安装TypeScript后，&lt;code&gt;tsc&lt;&#x2F;code&gt;命令在你的项目中可用，现在你可以运行&lt;code&gt;npx tsc --init&lt;&#x2F;code&gt;来创建&lt;code&gt;tsconfig.json&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;compilerOptions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;target&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;es2016&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;commonjs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;esModuleInterop&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;forceConsistentCasingInFileNames&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;strict&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;skipLibCheck&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;创建的&lt;code&gt;tsconfig.json&lt;&#x2F;code&gt;包含大量配置选项和关于不同TypeScript功能的注释。我们不会在这篇文章中深入探讨TypeScript的细节，因为我们只想捕获一些简单的类型错误。保持原样即可；默认配置足够了。&lt;&#x2F;p&gt;
&lt;p&gt;有了新的TypeScript配置文件，你可以开始从命令行运行类型检查。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;# 运行类型检查，如果成功则运行Playwright
&lt;&#x2F;span&gt;&lt;span&gt;npx tsc --noEmit &amp;amp;&amp;amp; npx playwright test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;tsc&lt;&#x2F;code&gt;命令会自动使用你现有的配置文件。&lt;code&gt;--noEmit&lt;&#x2F;code&gt;标志指示编译器不要将&lt;code&gt;*.ts&lt;&#x2F;code&gt;文件编译成JavaScript（Playwright仍会为你做这件事），而只执行类型检查。&lt;&#x2F;p&gt;
&lt;p&gt;现在看看结果！&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-type-checking-and-linting&#x2F;_attachments&#x2F;1723012181.409843ZqDtGx5LeNNTxdK8_Screenshot2024-07-24at14.01.19.png&quot; alt=&quot;图3：类型检查错误&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;通过在&lt;code&gt;playwright test&lt;&#x2F;code&gt;之前运行&lt;code&gt;tsc --noEmit&lt;&#x2F;code&gt;命令，你刚刚为Playwright项目添加了类型安全。TypeScript做了它擅长的事（抱怨），并告诉我们我们搞砸了。它甚至提供了我们错在哪里的建议。太棒了！&lt;&#x2F;p&gt;
&lt;p&gt;当然，你不会想手动运行这些命令。让我们把它们包装并拆分成npm脚本，使它们更容易区分。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pretest&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;tsc --noEmit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;多亏了&lt;code&gt;pre*&lt;&#x2F;code&gt; npm生命周期脚本约定，你可以用一个命令运行多个脚本。当你运行&lt;code&gt;npm run test&lt;&#x2F;code&gt;时，&lt;code&gt;pretest&lt;&#x2F;code&gt;脚本会先运行，只有在它成功后&lt;code&gt;test&lt;&#x2F;code&gt;脚本才会执行。先类型检查，然后用真实浏览器进行端到端测试。酷！&lt;&#x2F;p&gt;
&lt;p&gt;既然为Playwright项目添加类型检查并不难，我们就此停止吗？&lt;&#x2F;p&gt;
&lt;p&gt;当然不！现在我们有了&lt;code&gt;pretest&lt;&#x2F;code&gt;步骤，让我们也引入TypeScript linting。它将帮助我们在运行测试代码之前捕获常见的Playwright错误。相信我，额外的linting步骤绝对值得！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wei-ni-de-playwrightxiang-mu-tian-jia-typescript-linting&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.checklyhq.com&#x2F;blog&#x2F;playwright-type-checking-and-linting#add-typescript-linting-to-your-playwright-project&quot;&gt;为你的Playwright项目添加TypeScript linting&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;类型检查有助于避免明显的运行时异常，但用户错误呢？如果你错误地使用Playwright，你的错误不会导致测试崩溃，而只是使它们失败怎么办？&lt;&#x2F;p&gt;
&lt;p&gt;TypeScript linting将把你的Playwright测试提升到新的水平，你再也不用眯着眼睛寻找这些难以发现的Playwright错误了。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zui-chang-jian-de-playwrightcuo-wu-bu-zheng-que-de-promiseshi-yong&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.checklyhq.com&#x2F;blog&#x2F;playwright-type-checking-and-linting#the-most-common-playwright-mistake-incorrect-promise-usage&quot;&gt;最常见的Playwright错误 — 不正确的Promise使用&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;你可能知道Playwright依赖JavaScript的Promise。测试运行器和核心库为你隐藏了所有异步魔法，多亏了&lt;code&gt;async&#x2F;await&lt;&#x2F;code&gt;，测试用例看起来像是在运行同步操作。&lt;&#x2F;p&gt;
&lt;p&gt;但在底层，有大量的异步自动等待发生，很容易遗漏&lt;code&gt;await&lt;&#x2F;code&gt;或者过度使用&lt;code&gt;await&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;让我们看另一个例子。这次包含了我在与客户交流时经常看到的两个错误。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ⚠️ 这个示例代码包含错误！
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 请不要直接复制粘贴。
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test with incorrect promise handling&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;playwright.dev&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这个`await`是不必要的 
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Get started&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这个`click()`需要await
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;最常见的两个Playwright错误是&lt;strong&gt;将同步方法当作异步方法处理&lt;&#x2F;strong&gt;或&lt;strong&gt;将异步方法当作同步方法处理&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Playwright定位器是同步的，
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 它们会在与操作和web优先断言一起使用时被评估
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; -&amp;gt; 它们不需要`await`
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 正确
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 错误
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; -----
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 操作（`click()`）、web优先断言（`expect().toBeVisible()`）
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 和`test.step()`等方法是异步的
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; -&amp;gt; 它们需要`await`
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这些错误的棘手之处在于它们经常导致不可预测的行为。有时它们会立即使你的测试失败，有时由于某些竞态条件，它们可能会正常工作。&lt;&#x2F;p&gt;
&lt;p&gt;不正确的Promise处理会导致不稳定性，很容易引发一整个下午的bug追踪之旅。&lt;&#x2F;p&gt;
&lt;p&gt;幸运的是，一些linting可以帮上忙。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wei-ni-de-playwrightxiang-mu-tian-jia-typescript-eslint&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.checklyhq.com&#x2F;blog&#x2F;playwright-type-checking-and-linting#add-typescript-eslint-to-your-playwright-project&quot;&gt;为你的Playwright项目添加typescript-eslint&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;typescript-eslint&lt;&#x2F;code&gt;是一个流行的TypeScript linter，也是&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;best-practices#lint-your-tests&quot;&gt;Playwright团队推荐的&lt;&#x2F;a&gt;。它能帮助解决这两个错误吗？当然可以！&lt;&#x2F;p&gt;
&lt;p&gt;让我们安装更多的开发依赖。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;# 安装`typescript-eslint`依赖
&lt;&#x2F;span&gt;&lt;span&gt;npm install --save-dev eslint @eslint&#x2F;js @types&#x2F;eslint__js typescript-eslint
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;typescript-eslint&lt;&#x2F;code&gt;依赖于&lt;a href=&quot;https:&#x2F;&#x2F;eslint.org&#x2F;&quot;&gt;ESLint&lt;&#x2F;a&gt;，所以我们必须安装它并添加一些额外的类型。&lt;&#x2F;p&gt;
&lt;p&gt;安装完这些后，我们可以按照&lt;a href=&quot;https:&#x2F;&#x2F;typescript-eslint.io&#x2F;getting-started#step-2-configuration&quot;&gt;入门指南&lt;&#x2F;a&gt;在项目根目录创建一个&lt;code&gt;eslint.config.mjs&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; @ts-check
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;eslint &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@eslint&#x2F;js&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tseslint &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;typescript-eslint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tseslint&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;eslint&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;configs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;recommended&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tseslint&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;configs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;recommended&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个配置文件设置了ESLint和&lt;code&gt;typescript-eslint&lt;&#x2F;code&gt;推荐的配置。你不用担心这些细节。现在你可以开始用&lt;code&gt;npx eslint tests&#x2F;**&lt;&#x2F;code&gt;来检查你的基于TypeScript的Playwright代码了。你会看到…&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-type-checking-and-linting&#x2F;_attachments&#x2F;1723012184.098311ZqDu9B5LeNNTxdL__Screenshot2024-07-24at14.09.15.png&quot; alt=&quot;图4：npx eslint tests&#x2F;**没有结果。&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;…什么都没有。我们的错误在运行linting时还没有显示出来。为什么？因为我们还没有开启&quot;高级TypeScript&quot;linting。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;typescript-eslint&lt;&#x2F;code&gt;支持所谓的&lt;a href=&quot;https:&#x2F;&#x2F;typescript-eslint.io&#x2F;getting-started&#x2F;typed-linting&#x2F;&quot;&gt;&quot;带类型信息的Linting&quot;&lt;&#x2F;a&gt;。这个功能使linter能够理解底层的类型信息。如果你启用它，ESLint不仅会检查格式和语法规则，还会理解并评估你的代码。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;要使带类型信息的linting生效，你必须在&lt;code&gt;eslint.config.mjs&lt;&#x2F;code&gt;文件中定义TypeScript设置。幸运的是，我们已经创建了一个&lt;code&gt;tsconfig.js&lt;&#x2F;code&gt;，所以你可以像下面这样指定&lt;code&gt;languageOptions&lt;&#x2F;code&gt;。&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;对Playwright项目最有价值的规则是&lt;a href=&quot;https:&#x2F;&#x2F;typescript-eslint.io&#x2F;rules&#x2F;no-floating-promises&#x2F;&quot;&gt;no-floating-promises&lt;&#x2F;a&gt;和&lt;a href=&quot;https:&#x2F;&#x2F;typescript-eslint.io&#x2F;rules&#x2F;await-thenable&#x2F;&quot;&gt;await-thenable&lt;&#x2F;a&gt;。让我们把它们加进去。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; @ts-check
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;eslint &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@eslint&#x2F;js&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tseslint &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;typescript-eslint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tseslint&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;eslint&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;configs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;recommended&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tseslint&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;configs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;recommended&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 定义TS项目配置以启用&amp;quot;带类型信息的linting&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    languageOptions: {
&lt;&#x2F;span&gt;&lt;span&gt;      parserOptions: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 重用现有的`tsconfig.json`
&lt;&#x2F;span&gt;&lt;span&gt;        project: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        tsconfigRootDir: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 启用对Playwright项目有益的linting规则
&lt;&#x2F;span&gt;&lt;span&gt;    rules: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@typescript-eslint&#x2F;no-floating-promises&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@typescript-eslint&#x2F;await-thenable&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;现在如果你运行&lt;code&gt;npx eslint tests&#x2F;**&lt;&#x2F;code&gt;，你的新linter将捕获两个最常见的Playwright错误。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-type-checking-and-linting&#x2F;_attachments&#x2F;1723012189.185555ZqDvhB5LeNNTxdMM_Screenshot2024-07-24at14.11.35.png&quot; alt=&quot;图5：linting后显示的typescript-eslint错误。&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;让我们再整理一下，将linting步骤添加到我们的&lt;code&gt;pretest&lt;&#x2F;code&gt;脚本中来完成设置。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pretest&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;tsc --noEmit &amp;amp;&amp;amp; eslint tests&#x2F;**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;现在当你用&lt;code&gt;npm run test&lt;&#x2F;code&gt;运行Playwright测试时，你的代码会在浏览器打开之前进行类型检查和linting。这种方法不仅会让你的测试更安全，还能帮助你更快地发现错误。这意味着你可以停止浪费那些不必要的CI&#x2F;CD分钟来发现拼写错误！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-lun&quot;&gt;结论&lt;&#x2F;h2&gt;
&lt;p&gt;但让我们现实一点：这些项目依赖和配置文件值得吗？&lt;&#x2F;p&gt;
&lt;p&gt;当运行一个小型端到端测试项目时，没有这些安全措施也可以。但当你开始扩展Playwright，编写页面对象模型，并运行大量测试时，你应该押注于所有可以获得的安全措施。&lt;&#x2F;p&gt;
&lt;p&gt;仅仅依赖编辑器中的一些红色波浪线无法帮助你驾驭复杂的测试设置。如果你还不相信，当你因为一次重大的代码重构而陷入无休止的CI&#x2F;CD测试循环时，你就会明白这一点了。祝你好运！&lt;&#x2F;p&gt;
&lt;p&gt;但如果你想采用类型检查和linting，&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;checkly&#x2F;playwright-examples&#x2F;tree&#x2F;main&#x2F;pwt-playwright-type-check-and-lint&quot;&gt;你可以在GitHub上找到示例代码&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;同时请记住，&lt;strong&gt;对预览部署进行端到端测试并不能保证生产环境正常运行&lt;&#x2F;strong&gt;。唯一能让你安心睡觉的方法是&lt;a href=&quot;https:&#x2F;&#x2F;www.checklyhq.com&#x2F;product&#x2F;synthetic-monitoring&#x2F;&quot;&gt;持续运行Playwright测试，并在出现问题时收到警报&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;不过，既然你已经看到了Checkly的博客，你应该已经知道这一点了。😉&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.checklyhq.com&#x2F;blog&#x2F;playwright-type-checking-and-linting&quot;&gt;原文地址&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>为什么page.goto()会拖慢你的playwright测试脚本</title>
        <published>2024-07-24T00:00:00+00:00</published>
        <updated>2024-07-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/why-page-goto-is-slowing-down-your-playwright-test/"/>
        <id>https://playwright.itest.info/blog/why-page-goto-is-slowing-down-your-playwright-test/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/why-page-goto-is-slowing-down-your-playwright-test/">&lt;p&gt;在使用Playwright进行端到端测试时，您面临着一个两难的局面。一方面，测试的稳定性至关重要。毕竟，没有什么比一个反复无常的测试套件更让人头疼的了。另一方面，测试的执行速度同样不容忽视。试想一下，当您急需部署一个紧急的生产环境修复时，却不得不干等几个小时才能看到测试结果，这种感觉恐怕会让人抓狂。&lt;&#x2F;p&gt;
&lt;p&gt;那么，当您听说page.goto()这个常用指令可能正是拖慢测试速度的元凶时，您会作何感想？这个问题值得我们深思。&lt;&#x2F;p&gt;
&lt;p&gt;准备好一起探讨如何在保证稳定性的同时提升测试效率了吗？让我们开始这段有趣的旅程吧！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mei-ge-playwrightjiao-ben-de-kai-shi-page-goto&quot;&gt;每个Playwright脚本的开始 — page.goto()&lt;&#x2F;h2&gt;
&lt;p&gt;这里有一个非常基础的Playwright脚本。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;js
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Login works&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginLink &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginLink&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginHeader &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;heading&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginHeader&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; More things to check after the login link was clicked
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;它导航到在playwright.config中定义的baseURL的根目录。定位一个链接，点击它，然后测试网站的UI是否相应更新（我隐藏了大部分动作和断言，因为它们对这篇文章不重要）。&lt;&#x2F;p&gt;
&lt;p&gt;这一切运行得很好，我相信您以前也写过类似的Playwright代码。如果您检查您的测试及其持续时间，您可能已经发现page.goto()的执行时间可能会有很大差异。&lt;&#x2F;p&gt;
&lt;p&gt;让我给您展示一个例子。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;images.prismic.io&#x2F;checklyhq&#x2F;Zo5o0B5LeNNTw_Lp_slow-page-goto.jpg?auto=format,compress&quot; alt=&quot;Playwright跟踪查看器突出显示缓慢的page.goto调用&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;让我们仔细看看这个Playwright的跟踪记录。令人惊讶的是，page.goto()竟然耗费了整整10秒钟！更有趣的是，从跟踪查看器顶部的预览界面可以看出，在这漫长的等待过程中，示例网站其实一直都是可用状态。&lt;&#x2F;p&gt;
&lt;p&gt;要知道，这还只是一个本地运行的、超级快速的电子商务网站的简单测试用例。想象一下，如果我们要测试一个中等规模商店的所有核心功能，很可能就需要运行上百个测试。这样一来，每个测试中的这点小延迟累积起来，就可能在您的CI&#x2F;CD流水线中增加超过15分钟的等待时间！&lt;&#x2F;p&gt;
&lt;p&gt;（当然，您可以通过并行运行测试来缩短总体等待时间，但这并不能改变每个测试都被不必要地拖慢的事实。）&lt;&#x2F;p&gt;
&lt;p&gt;那么问题来了，在这段时间里，我们到底在等待什么呢？这个问题值得我们深入探讨。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;page-goto-he-loadshi-jian&quot;&gt;page.goto()和load事件&lt;&#x2F;h2&gt;
&lt;p&gt;如果您查看Playwright文档，您会发现默认情况下，page.goto()等待load事件。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; is the same as
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {waitUntil: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;load&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;load事件是什么？MDN是这样说的。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;load事件在整个页面加载完成时触发，包括所有依赖资源，如css、javascript、iframe和图像。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;让我们仔细思考一下。每当您调用page.goto()时，您都在等待所有css文件、js脚本、iframe和图像加载完成。总之，您在等待所有内容(!)加载完成后才运行您的测试。&lt;&#x2F;p&gt;
&lt;p&gt;我在复盘上面无脑等待的用例时，我发现网站上某个SVG图像的加载延迟了所有的测试行为。为什么这个文件如此缓慢？我不知道，但资源加载时间较长的可能性总是存在。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;images.prismic.io&#x2F;checklyhq&#x2F;Zo5qnB5LeNNTw_M5_logo-svg-10-seconds.jpg?auto=format,compress&quot; alt=&quot;Playwright跟踪查看器突出显示一个加载时间为10秒的logo.svg文件。&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;现在，这只是一个小型演示应用。考虑一个成熟的电商站点；那里将有数百个图像，更不用说所有来自&quot;某处&quot;的js脚本了。对于这样的网站，page.goto()的默认配置将等待所有资源通过网络加载完成之后才点击第一个按钮。这是最好的方式吗？&lt;&#x2F;p&gt;
&lt;h2 id=&quot;nin-de-yong-hu-bu-hui-deng-dai-suo-you-de-httpqing-qiu-wei-shen-me-nin-de-ce-shi-yao-deng-ni&quot;&gt;您的用户不会等待所有的http请求 — 为什么您的测试要等呢？&lt;&#x2F;h2&gt;
&lt;p&gt;让我们换个角度思考这个问题...&lt;&#x2F;p&gt;
&lt;p&gt;为什么我们要使用真实浏览器进行端到端测试和网站监控呢？显然，我们的首要目标是确保网站和基础设施运行正常。但更重要的是，我们希望通过模拟真实用户的行为来实现这一目标。毕竟，这才是端到端测试的真正意义所在，不是吗？&lt;&#x2F;p&gt;
&lt;p&gt;想象一下，您的普通用户会等待浏览器地址栏上的加载图标完全消失后才开始点击页面元素吗？恐怕不会。我敢打赌，大多数访客看到&quot;加入购物车&quot;按钮的那一刻就会迫不及待地点击，根本不会关心网站是否已经完全加载完毕。人们的行为往往是看到什么就点什么，立即与网站进行互动。&lt;&#x2F;p&gt;
&lt;p&gt;这就是为什么在编写Playwright脚本时，我们应该尽可能地模仟这种自然的人类行为。毕竟，我们的测试越接近真实用户的操作，就越能发现实际环境中可能出现的问题。&lt;&#x2F;p&gt;
&lt;p&gt;那么，回到我们的问题：在测试用例中，我们应该如何更好地模拟用户访问页面的行为呢？这个问题值得我们深入思考，因为它可能是提高测试效率和真实性的关键。&lt;&#x2F;p&gt;
&lt;p&gt;page.goto()的等待行为可以通过waitUntil属性进行配置。您可以定义四种waitUntil设置。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Wait until the HTML starts loading.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {waitUntil: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;commit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Wait until the HTML is parsed
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; and deferred scripts (`&amp;lt;script deferred&amp;gt;` and `&amp;lt;script type=&amp;quot;module&amp;quot;&amp;gt;`) are loaded.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {waitUntil: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;domcontentloaded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Wait until all initially included resources are loaded.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {waitUntil: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;load&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Wait until every resource is loaded and the network is silent for 500ms.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {waitUntil: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;networkidle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;当您查看这些waitUntil选项时，除了commit之外的所有选项都严重依赖网络和资源的加载。这意味着除了commit之外的所有选项都依赖网页上的具体资源，当一个请求卡在网络层时可能会拖慢您的测试速度。&lt;&#x2F;p&gt;
&lt;p&gt;这些选项在速度上有哪些差异？以下是我的示例测试用例的结果。&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&quot;waitUntil&quot; 选项&lt;&#x2F;th&gt;&lt;th&gt;&quot;page.goto&quot; 执行时间&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;commit&lt;&#x2F;td&gt;&lt;td&gt;62ms&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;domcontentloaded&lt;&#x2F;td&gt;&lt;td&gt;159ms&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;load&lt;&#x2F;td&gt;&lt;td&gt;10.1s&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;networkidle&lt;&#x2F;td&gt;&lt;td&gt;12.6s&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;当然，这些数据是我从本地测试站点上搜集到的。但绝对值并不重要。重要的是看waitUntil选项之间的差异。&lt;&#x2F;p&gt;
&lt;p&gt;不出所料，commit等待选项是最快的goto()配置，因为它除了初始HTML的第一个字节外不等待任何与资源相关的内容。networkIdle是迄今为止最慢的，因为它等待每个资源加载完成，再等500ms。&lt;&#x2F;p&gt;
&lt;p&gt;但这里有一件有趣的事情：无论waitUntil选项如何，所有这些测试都成功了。这个简单的测试用例的总体测试持续时间从大约10秒到25秒不等，但测试结果都是绿色的。&lt;&#x2F;p&gt;
&lt;p&gt;这是怎么回事，它是如何工作的？&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bian-xie-bu-yi-lai-wang-luo-de-kuai-su-ce-shi&quot;&gt;编写不依赖网络的快速测试&lt;&#x2F;h2&gt;
&lt;p&gt;如果您要测试的网站本身就不稳定，那么想要创建稳定和快速的测试用例集合几乎是不可能的。就像俗话说的，巧妇难为无米之炊 — 不稳定的应用程序注定难以产生可靠的测试。&lt;&#x2F;p&gt;
&lt;p&gt;此外，如果您的网站用户体验糟糕，或者采用了不良的&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;navigations#hydration&quot;&gt;Hydration Patterns&lt;&#x2F;a&gt;，您的测试就不得不使用各种变通方法来勉强通过最基本的功能测试。当然，最理想的做法是直接修复这些应用程序问题，但我们都知道，现实中这往往不太容易做到。&lt;&#x2F;p&gt;
&lt;p&gt;另一方面，如果您在编写Playwright脚本时没有站在用户的角度思考，那么您同样可能会导致测试的不稳定。要想加快测试速度并提高其稳定性，最好的方法是利用自动等待机制和Web优先断言，让Playwright自己去处理其他细节问题。&lt;&#x2F;p&gt;
&lt;p&gt;文章开头的示例脚本中包含了一个看似&quot;简单&quot;的click()指令。实际上，这个click()指令是您的得力助手，因为它背后蕴含着一系列神奇的可操作性检查。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; this click() will wait for the login to be
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; - visible
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; - stable, as in not animating or completed animation
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; - able to receives events as in not obscured by other elements
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; - enabled
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginLink&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;每次您使用click()、fill()或其他操作指令时，Playwright都会智能等待，直到目标元素完全就绪，能够响应用户操作。这意味着元素已经完成渲染，变得可见且稳定，并且处于可用状态。&lt;&#x2F;p&gt;
&lt;p&gt;如果您的应用程序设计良好，能够快速呈现出可交互的界面元素，Playwright就能以最快的速度与这些元素进行交互。在这种情况下，您根本不需要额外等待任何网络请求完成。&lt;&#x2F;p&gt;
&lt;p&gt;类似地，当您使用Web优先的断言方式时，Playwright会自动等待，直到用户界面达到您预期的状态。这样一来，您就不必再专门去等待某个特定UI状态背后的HTML加载或API调用完成。&lt;&#x2F;p&gt;
&lt;p&gt;这种方法不仅简化了测试流程，还能更好地模拟真实用户的操作行为，从而提高测试的有效性和可靠性。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; wait until this element is visible
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginHeader&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果您能够充分利用这两个Playwright的核心原则，您就可以不再过分关注网络层的细节，而是将注意力集中在真正重要的方面 — 用户界面的交互操作及其导致的状态变化。这种方法几乎消除了手动等待网络事件和请求的需求，让您的测试更加高效和可靠。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;js
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Login works&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; don&amp;#39;t wait for all the resources to be loaded
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {waitUntil: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;commit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; let Playwright wait for this link to be visible
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginLink &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginLink&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginHeader &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;heading&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginHeader&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; More things to check after the login link was clicked
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;让我们再看看调整后的脚本。我们不再傻等网络事件，而是聪明地等待UI状态变化。Playwright会耐心等到&quot;Login&quot;链接出现，然后立即点击。这个小小的改动让我们的测试速度提升了10秒，同时还能完整覆盖核心登录功能。这不就是双赢吗？&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dan-shi-shi-shi-zong-you-li-wai&quot;&gt;但是...世事总有例外&lt;&#x2F;h2&gt;
&lt;p&gt;话说回来，在某些情况下，关注网络状况确实很有必要。&lt;&#x2F;p&gt;
&lt;p&gt;在进行端到端测试时，我们经常会在预发布环境上进行测试，以防止核心功能出现回归。理想情况下，预发布环境应该是生产环境的完美复制品。但现实往往不尽如人意。预发布环境通常部署在不同的基础设施上，前端也常常会加载不同的资源来进行跟踪、用户交互和监控。&lt;&#x2F;p&gt;
&lt;p&gt;这些差异有时是可以接受的，毕竟您的主要目的是测试新功能并避免回归。然而，当您将Playwright用于网站监控时，密切关注所有加载的资源就显得尤为重要，这可以帮您及时发现潜在的生产问题。我亲眼见过不止一次，第三方脚本导致生产环境崩溃的情况。&lt;&#x2F;p&gt;
&lt;p&gt;举个例子，我们在Checkly使用Playwright监控checklyhq.com。和大多数营销网站一样，我们也集成了Intercom和各种分析工具。有一天，我们所有的Playwright线上检查突然全部失败了。原因是Intercom在预期时间内没能加载完成。page.goto()的执行时间超过了一分钟，就因为一个JavaScript片段超时了。&lt;&#x2F;p&gt;
&lt;p&gt;那么，checklyhq.com是不是真的出问题了呢？其实没有，网站的核心功能一切正常。但是，及时发现Intercom出现问题难道不是件好事吗？当然是！&lt;&#x2F;p&gt;
&lt;p&gt;通过监控网络层，您可以获得关于网站整体健康状况和性能的宝贵信息。这些洞察可以帮助您更全面地了解您的网站运行情况，及时发现并解决潜在问题。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; fail when all resources aren&amp;#39;t loaded after 10 seconds
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { waitUntil: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;load&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10_000 &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h2&gt;
&lt;p&gt;那么，您是否应该立即将所有的page.goto()操作改为commit或domcontentloaded，以在CI&#x2F;CD中节省时间呢？答案并不是简单的是或否，而是要&quot;具体情况具体分析&quot;。&lt;&#x2F;p&gt;
&lt;p&gt;如果您特别看重测试的执行速度，需要运行大量测试，并且采用了以用户为中心、具有自动等待功能的测试方法，那么不等待所有资源加载确实能为您节省可观的时间 — 可能是几分钟，甚至几小时。不妨试一试，您会发现Playwright在判断何时点击以及点击什么方面相当智能。&lt;&#x2F;p&gt;
&lt;p&gt;不过，请记住，仅仅看到预览部署的绿灯并不意味着万事大吉。真正重要的是确保生产环境的稳定运行。要做到这一点，持续测试您的线上产品是必不可少的。这正是合成监控大显身手的地方。&lt;&#x2F;p&gt;
&lt;p&gt;在使用Playwright进行网站测试和监控时，密切关注那些加载缓慢的网络依赖可能会带来意想不到的收益。一旦您的基础设施、第三方资源或应用程序代码出现问题，您将能第一时间察觉。这才是让您晚上安心入睡的真正保障。&lt;&#x2F;p&gt;
&lt;p&gt;当然，最终如何取舍，还是需要您根据自身情况来决定。&lt;&#x2F;p&gt;
&lt;p&gt;如果您有任何疑问或想法，欢迎来Checkly社区和我们交流。我可以向您保证，我们是一群热情友好的伙伴，随时欢迎您的加入！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.checklyhq.com&#x2F;blog&#x2F;why-page-goto-is-slowing-down-your-playwright-test&#x2F;&quot;&gt;原文地址&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间：2024-07-24&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何安装playwright 2024版本</title>
        <published>2024-07-23T00:00:00+00:00</published>
        <updated>2024-07-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-to-install-playwright-in-cn/"/>
        <id>https://playwright.itest.info/blog/how-to-install-playwright-in-cn/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-to-install-playwright-in-cn/">&lt;h2 id=&quot;shi-pin-ban-ben&quot;&gt;视频版本&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;youtube&quot;&gt;Youtube&lt;&#x2F;h3&gt;
&lt;div class=&quot;w-full md:w-1&#x2F;3 mx-auto&quot;&gt;
&lt;iframe width=&quot;360&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;FimMK3jszAo?si=N9aN6KZaIH2Rgn2r&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;div&gt;
&lt;h3 id=&quot;b-zhan&quot;&gt;B 站&lt;&#x2F;h3&gt;
&lt;div class=&quot;w-full md:w-1&#x2F;3 mx-auto&quot;&gt;
&lt;iframe src=&quot;&#x2F;&#x2F;player.bilibili.com&#x2F;player.html?isOutside=true&amp;aid=1006395903&amp;bvid=BV1Zx4y1W7CZ&amp;cid=1624499655&amp;p=1&quot; scrolling=&quot;no&quot; border=&quot;0&quot; frameborder=&quot;no&quot; framespacing=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;这几天录视频，在 windows 电脑上试着去安装了一下 javascript 版本的 playwright，发现有点难度，主要是网络联通性有挑战，这里简单记录一下。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-zhuang-nodejs&quot;&gt;安装 nodejs&lt;&#x2F;h2&gt;
&lt;p&gt;首先我们需要安装 &lt;a href=&quot;https:&#x2F;&#x2F;nodejs.org&#x2F;zh-cn&quot;&gt;nodejs&lt;&#x2F;a&gt;，这里略过。&lt;&#x2F;p&gt;
&lt;p&gt;我的安装的版本是&lt;code&gt;Node.js v20.15.1&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zui-zhong-yao-de-bu-zou-she-zhi-npm-yuan&quot;&gt;最重要的步骤：设置 npm 源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;对于国内用户来说，这一步至关重要，是解决网络联通性的关键。&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;打开命令行，输入下面的命令。&lt;&#x2F;p&gt;
&lt;p&gt;如果你对命令行不熟悉，那么 win 11 系统里，随便打开 1 个文件夹，鼠标右键打开系统菜单，选择&lt;code&gt;在终端中打开&lt;&#x2F;code&gt;就可以了。&lt;&#x2F;p&gt;
&lt;p&gt;我们在终端里输入下面的命令，大部分情况下，不需要我们手动一个字符一个字符输入，只要在 playwright 中文站的安装教程里拷贝具体的命令，然后右键点击终端就可以粘贴了。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; config set registry https:&#x2F;&#x2F;registry.npmmirror.com
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;shi-yong-npm-an-zhuang-playwrgiht&quot;&gt;使用 npm 安装 playwrgiht&lt;&#x2F;h2&gt;
&lt;p&gt;如果你对命令行熟悉的话，可以新建 1 个文件夹 &lt;code&gt;demo&lt;&#x2F;code&gt;，然后从命令行里&lt;code&gt;cd&lt;&#x2F;code&gt;进去，再运行下面的命令。&lt;&#x2F;p&gt;
&lt;p&gt;如果你对命令行不熟悉，那么 win 11 系统里，我们可以新建 1 个文件夹 &lt;code&gt;demo&lt;&#x2F;code&gt;，进入 demo 文件夹，鼠标右键打开系统菜单，选择&lt;code&gt;在终端中打开&lt;&#x2F;code&gt;就可以了。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; init playwright@latest
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;an-zhuang-xuan-xiang&quot;&gt;安装选项&lt;&#x2F;h3&gt;
&lt;p&gt;命令运行时会出现 4 个问题，全部按 Enter 选择默认值&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;✔ Do you want to use TypeScript or JavaScript? · TypeScript&lt;&#x2F;li&gt;
&lt;li&gt;✔ Where to put your end-to-end tests? · tests&lt;&#x2F;li&gt;
&lt;li&gt;✔ Add a GitHub Actions workflow? (y&#x2F;N) · false&lt;&#x2F;li&gt;
&lt;li&gt;✔ Install Playwright browsers (can be done manually via &#x27;npx playwright install&#x27;)? (Y&#x2F;n) · true&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;等待一段时间后就可以看到 playwright 已经安装成功了。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-zhuang-vscode-yi-ji-playwright-cha-jian&quot;&gt;安装 vscode 以及 playwright 插件&lt;&#x2F;h2&gt;
&lt;p&gt;这一步不是必须的，但如果你不熟悉命令行的话，那么还是非常推荐的。&lt;&#x2F;p&gt;
&lt;p&gt;在 vscode 插件市场搜索 playwright 插件，安装微软官方出的那个插件，一般情况下就是排第 1 位的那个。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-vscode-da-kai-demo-wen-jian-jia&quot;&gt;使用 vscode 打开 demo 文件夹&lt;&#x2F;h2&gt;
&lt;p&gt;首先用 vscode 打开我们刚才新建的 demo 项目，然后在进行下面的配置。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;projects 选择 chrome&lt;&#x2F;li&gt;
&lt;li&gt;settings 里面选择&lt;code&gt;show trace viewer&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;打开&lt;code&gt;tests&lt;&#x2F;code&gt;文件夹下的&lt;code&gt;example.spec.ts&lt;&#x2F;code&gt;文件。&lt;&#x2F;p&gt;
&lt;p&gt;然后点击&lt;code&gt;test&lt;&#x2F;code&gt;方法旁的三角形按钮，就可以运行用例了。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yun-xing-yong-li&quot;&gt;运行用例&lt;&#x2F;h2&gt;
&lt;p&gt;在运行默认用例时可能因为网络原因出现失败的情况，这时候可以把第 1 个用例改一下。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;has title&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;playwright.itest.info&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Expect a title &amp;quot;to contain&amp;quot; a substring.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveTitle&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我们把用例里的&lt;code&gt;playwrgith.dev&lt;&#x2F;code&gt;改成&lt;code&gt;playwright.itest.info&lt;&#x2F;code&gt;，再次运行，不出意外的话用例会成功运行通过 ✅。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;si-kao-ti&quot;&gt;思考题&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveTitle&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;用中文去说明这个断言的具体检查了什么内容？&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>我的playwright项目实战初体验</title>
        <published>2024-07-19T00:00:00+00:00</published>
        <updated>2024-07-19T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/write-ui-test-case-for-brand-new-project/"/>
        <id>https://playwright.itest.info/blog/write-ui-test-case-for-brand-new-project/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/write-ui-test-case-for-brand-new-project/">&lt;p&gt;2023 年初接了一个新项目，配套有个 web 管理后台页面，尽管需求一直在迭代以及测试时间相对不宽裕，我还是决定写点自动化用例作为功能测试的补充和回归测试的输入，顺便玩一下 playwright，不在真是项目中使用一种技术其实是很难对这种技术产生深刻理解的。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xiang-mu-jie-shao&quot;&gt;项目介绍&lt;&#x2F;h2&gt;
&lt;p&gt;管理后台是前后端分离的，前端用的 react 加上蚂蚁的前端组件库，后端是基于 golang 构建的微服务。其实这种项目更适合做接口测试，ui 自动化作为补充就好了。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ji-shu-xuan-xing&quot;&gt;技术选型&lt;&#x2F;h2&gt;
&lt;p&gt;这点很清楚，之前 benchmark 过，playwright 比 cypress 性能要好，所以直接选 playwright，另外 playwright 的 python 版本完全安装之后自带了 pytest 和一系列断言，基本上开箱即用，非常方便。另外为什么不用 js 而用 python，主要是因为我用 python 写了一点接口用例，有些数据库操作的代码可以稍微复用一下，所以统一起见用 python+playwright&lt;&#x2F;p&gt;
&lt;h2 id=&quot;di-yi-ge-nan-dian-deng-lu&quot;&gt;第一个难点：登录&lt;&#x2F;h2&gt;
&lt;p&gt;管理后台接的是 google auth，由于我的账号开启了二次验证，需要收验证码，所以从 ui 上输入用户名和密码登录就走不通了。不过登录的原理基本相同，就是往 cookie 里写一些东西，后面所有的请求都自动带 cookie 到后端，后端通过之前写的那些东西就可以判断用户是谁，什么时候登录失效等。知道了原理后面就是随便试试了，我的代码大概是这样写的&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;()=&amp;gt; document.cookie=&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;COOKIE&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;其实就是先访问被测页面，然后自动跳到登录页面，这时候去用 js 设置 cookie，之后再访问一次被测页面就可以自动登录了。&lt;&#x2F;p&gt;
&lt;p&gt;cookie 的话可以从浏览器的开发者工具里直接拷贝出来，因为是测试环境，所以 cookie 的有效期很长，基本可以放心使用。&lt;&#x2F;p&gt;
&lt;p&gt;至于如何定期刷新 cookie 其实也不难搞，写个浏览器插件，每次打开被测页面的时候就把 cookie 发到一个自建后台服务，这个服务就是把 cookie 存到 redis 里，在测试用例里直接访问 redis 拿最新的 cookie 就好了。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ding-wei-you-dian-ma-fan&quot;&gt;定位有点麻烦&lt;&#x2F;h2&gt;
&lt;p&gt;作为熟练工，定位对我来说应该不会是大问题，然而现在的前端组件层级嵌套厉害，html 的表意性不强，而且 id，name 等比较有标志性的属性也不是很多，踌躇良久之后我决定请前端同学在一些关键的组件上面加上 id 或者 name，尽管他们不是很愿意，但是我倚老卖老，还是让他们从了。&lt;&#x2F;p&gt;
&lt;p&gt;playwright 的元素定位策略非常灵活，从这几天的使用情况来看，建议还是 css 加 xpath，如果你 css 不熟那就直接用 xpath，优点是从浏览器上就可以直接复制 xpath，缺点是复制的 xpath 稳定性很差，页面结构稍微发生一些变化就不可用了。&lt;&#x2F;p&gt;
&lt;p&gt;最后就是 playwright 有录制的功能，我一般是在调试暂停的时候顺便打开录制，看看 playwright 自己录制的定位器是怎么样的，感觉大多数时候 playwright 的录制结果很靠谱，不比自己写要差。playwright 生成的代码里 text 属性用的相对比较多点，对于多语言的系统来说需要慎用。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ri-qi-xuan-ze-qi&quot;&gt;日期选择器&lt;&#x2F;h2&gt;
&lt;p&gt;大人，大清早就亡了。&lt;&#x2F;p&gt;
&lt;p&gt;很多年前我们在处理日期选择器的时候基本上是用 js 来 set 相应的 input 的 value 值，可以做到不管 ui 怎么样，我想设置成什么值都可以的效果。&lt;&#x2F;p&gt;
&lt;p&gt;然而，这次时代变了。ant 框架的日期选择器直接设置 value 值并没有什么用。我尝试过打开日期选择器选择今天的日期，因为今天的日期会高亮，所以选起来比较容易，但这样就做不到选择任意一天的效果，对于写用例来说相当不友好。&lt;&#x2F;p&gt;
&lt;p&gt;最后经过一番尝试，还是使用黑科技，模拟键盘在日期框上输入相应的日期，模拟键盘按下回车键，代码如下。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;select_time_range&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;start&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;end&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    start_str &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;start.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;strftime&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;%Y&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;%m&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt; 00:00:00&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    end_str &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;end.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;strftime&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;%Y&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;%m&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt; 23:59:59&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#validTime&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;(start_str)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.ant-picker-input input &amp;gt;&amp;gt; nth=1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;(end_str)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#validTime&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;press&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;shu-ju-qing-li&quot;&gt;数据清理&lt;&#x2F;h2&gt;
&lt;p&gt;我的用例基本建立在数据的基础上，比如我会先创建一系列的数据，然后通过确定的条件去搜索数据，再度创建数据，编辑数据等，等于是写了剧本的，每个用例按照安排运行，这就要求在所有用例执行之后做数据清理的工作，这就要求我去数据库里更新每条记录的软删除字段。另外搜索页面的数据放在 es 里，这就要求清理的过程中除了数据库之外还要顺便把 es 清掉，稍微有一点点麻烦，不过还好，几行代码的事情。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;pymysql.cursors
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;requests, logging, sys
&lt;&#x2F;span&gt;&lt;span&gt;logging.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;basicConfig&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;format&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;%(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;asctime&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;)s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;%(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;levelname&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;)s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;%(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;)s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;level&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;logging.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;DEBUG&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;mysql_conf &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;host&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;xxxxx&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;port&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3306&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;db&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;xxx_db&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;secert&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;color:#8cdaff;&quot;&gt;DBCleaner&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;__init__&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;None&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.c &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;pymysql.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;host&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;mysql_conf[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;host&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;mysql_conf[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;port&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;mysql_conf[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;port&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;mysql_conf[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;database&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;mysql_conf[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;db&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;cursorclass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;pymysql.cursors.DictCursor)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;delete_by_mid&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;mid&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.c.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cursor&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;cursor:
&lt;&#x2F;span&gt;&lt;span&gt;            select_sql &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Select id from biz_tab where m_id = %s;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;            cursor.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;execute&lt;&#x2F;span&gt;&lt;span&gt;(select_sql, (mid))
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;cursor:
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;delete_es_index_by_biz_id&lt;&#x2F;span&gt;&lt;span&gt;(row[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;            logging.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Delete by mid = &lt;&#x2F;span&gt;&lt;span&gt;{mid}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;            sql &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Delete from biz_tab where m_id = %s;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;            cursor.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;execute&lt;&#x2F;span&gt;&lt;span&gt;(sql, (mid))
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.c.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;commit&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;delete_es_index_by_biz_id&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;biz_id&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test_index&#x2F;_doc&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;{biz_id}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;requests.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;DELETE&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, url).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        logging.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Deleting es index for &lt;&#x2F;span&gt;&lt;span&gt;{biz_id}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;, result is &lt;&#x2F;span&gt;&lt;span&gt;{response[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;__name__ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span&gt;__main__&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92649;color:#f8f8f0;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(sys.argv) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;USAGE: db_cleaner m_id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        m_id &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;sys.argv[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;        cleaner &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;DBCleaner&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        cleaner.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;delete_by_mid&lt;&#x2F;span&gt;&lt;span&gt;(m_id)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;上面的脚本有 2 个作用&lt;&#x2F;p&gt;
&lt;p&gt;当 module 引入时可以调用删除的方法，传入 mid 就可以删除对应的记录
直接命令行执行时给出 mid 也能删除
确保清除成功
pytest 在断言失败之后，后面的代码是不会被执行的，为了确保每次都会调用清理的代码，我们需要使用 pytest 的 fixture 机制，代码如下&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;@pytest.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fixture
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;data_set&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;    m_id &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;12&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;yield &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;m_id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: m_id}
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;DBCleaner&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;delete_by_mid&lt;&#x2F;span&gt;&lt;span&gt;(m_id)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h2&gt;
&lt;p&gt;经过几天的把玩和用例编写，发现&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;迭代过程中的项目确实不太适合写 ui 自动化测试，不过千金难买我喜欢，我就要写&lt;&#x2F;li&gt;
&lt;li&gt;playwright 很好用&lt;&#x2F;li&gt;
&lt;li&gt;page object 还是得用&lt;&#x2F;li&gt;
&lt;li&gt;pytest 很好用&lt;&#x2F;li&gt;
&lt;li&gt;headless 很好用&lt;&#x2F;li&gt;
&lt;li&gt;直接写 js 很好用&lt;&#x2F;li&gt;
&lt;li&gt;简单来说，熟练工是第一生产力。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;hui-gu&quot;&gt;回顾&lt;&#x2F;h2&gt;
&lt;p&gt;最后在 2024 年的今天来回顾一下这个项目。&lt;&#x2F;p&gt;
&lt;p&gt;项目最后没有维护了，主要是因为:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;playwright 的 python 版本一直更新失败，官方也没给出明确的说法&lt;&#x2F;li&gt;
&lt;li&gt;playwright 的 api 在当时变化十分频繁，过几个月之后再来看当时的代码，发现很多 api 都过时了&lt;&#x2F;li&gt;
&lt;li&gt;因为 web 端的用户很少，所以主要精力也没放在那里，渐渐的就不维护了&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;现在看来直接连数据库做数据清理不是很好。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;要了解数据库的分库分表策略，了解是小事，但是策略代码要自己写，这个没必要&lt;&#x2F;li&gt;
&lt;li&gt;直接在数据库删数据的时候没办法触发业务逻辑的数据处理脚本&lt;&#x2F;li&gt;
&lt;li&gt;数据库的变化对代码的影响很大&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;后面我在做 api 的自动化时用的是调接口清理的方法，代码可维护了很多。&lt;&#x2F;p&gt;
&lt;p&gt;总之在当时使用 playwright 做 ui 自动化应该是没问题的，问题是不应该用 python 版本，如果是 ts 版本的话估计稳定性会强很多。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>所有教程存档</title>
        <published>2024-07-14T00:00:00+00:00</published>
        <updated>2024-07-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/archive/"/>
        <id>https://playwright.itest.info/blog/archive/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/archive/"></content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何让 playwright 运行的更快</title>
        <published>2024-07-11T00:00:00+00:00</published>
        <updated>2024-07-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/make-playwright-10x-faster/"/>
        <id>https://playwright.itest.info/blog/make-playwright-10x-faster/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/make-playwright-10x-faster/">&lt;p&gt;在这篇文章中，我将解释如何使用 Playwright 将测试速度提高&lt;strong&gt;至少 10 倍&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;首先，让我们来了解一下什么是 Playwright。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 是一个 Node.js 库，它允许你使用相同的 API 脚本和自动化浏览器，如 Chrome、Firefox 和 Safari。&lt;&#x2F;p&gt;
&lt;p&gt;如果你想知道为什么要使用 Playwright，可以看看这个&lt;a href=&quot;https:&#x2F;&#x2F;www.devstringx.com&#x2F;playwright-tool&quot;&gt;链接&lt;&#x2F;a&gt;。现在让我们通过一个例子来看看如何使用 Playwright 加速测试。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bing-xing-hua&quot;&gt;并行化&lt;&#x2F;h2&gt;
&lt;p&gt;加速测试的一种方法当然是并行运行。首先，我们打开一个名为 example.spec.ts 的文件，放入 20 个测试用例，并在每个测试用例中写入以下代码，让其静态等待 30 秒。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await new &lt;&#x2F;span&gt;&lt;span&gt;Promise(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;30000&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个数字将帮助我们理解通过并行运行获得的收益。我们的文件最终版本如下。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;20个示例测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试 - 1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await new &lt;&#x2F;span&gt;&lt;span&gt;Promise(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;resolve &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;30000&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试 - 2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await new &lt;&#x2F;span&gt;&lt;span&gt;Promise(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;resolve &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;30000&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试 - 3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await new &lt;&#x2F;span&gt;&lt;span&gt;Promise(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;resolve &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;30000&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  .
&lt;&#x2F;span&gt;&lt;span&gt;  .
&lt;&#x2F;span&gt;&lt;span&gt;  .
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试 - 19&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await new &lt;&#x2F;span&gt;&lt;span&gt;Promise(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;resolve &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;30000&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试 - 20&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await new &lt;&#x2F;span&gt;&lt;span&gt;Promise(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;resolve &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;30000&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果我们一个一个地运行这些测试（是的，你猜对了！）需要 10 分钟。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*VIOOJ8QIkBLAKmhlUHsJNQ.png&quot; alt=&quot;图片3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;20 个测试一个一个运行需要 10 分钟&lt;&#x2F;p&gt;
&lt;p&gt;10 分钟对于每个运行 30 秒的 20 个测试用例来说太多了。让我们把这个时间缩短一些。Playwright 使用同时运行的工作进程来并行运行。让我们用 5 个工作进程并行运行这些测试，看看区别。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*SDe6gpDyOrqSsTPP8G5-ow.png&quot; alt=&quot;图片4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;20 个测试用 5 个工作进程运行需要 2 分钟&lt;&#x2F;p&gt;
&lt;p&gt;是的，我们成功地用 5 个工作进程在 2 分钟内运行了 20 个测试。这里我们用了 5 个工作进程作为例子，但你可以根据你的系统增加或减少这个数量，直到找到最适合你的测试数量。&lt;&#x2F;p&gt;
&lt;p&gt;那么我们能跑得更快吗？5 倍的加速对我们来说够了吗？当然不够，我们需要更快。那么我们该怎么做呢？让我们看看 Playwright 的分片定义。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fen-pian&quot;&gt;分片&lt;&#x2F;h2&gt;
&lt;p&gt;如果我们使用多台机器，每台机器上分配相同数量的测试用例并并行运行，会怎么样？听起来不错。为此，Playwright 为我们提供了分片功能。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:609&#x2F;1*Kb2TJi5VqpR20C3A5d2CQg.png&quot; alt=&quot;图片5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;用分片运行 20 个测试&lt;&#x2F;p&gt;
&lt;p&gt;在同一台机器上，我们将测试用例分成 5 个相等的部分，并进行 4 次并行运行。如果我们在 5 台不同的机器上运行这些命令，所有测试用例将在 30 秒后运行完毕。这意味着本来需要 10 分钟逐一运行的所有测试用例现在可以在 30 秒内运行完成。不可思议吧？&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test - shard=index&#x2F;total-shard
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这里你可以根据你拥有的机器数量划分资源。我这里举了 5 个的例子。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gitlab-ji-cheng&quot;&gt;Gitlab 集成&lt;&#x2F;h2&gt;
&lt;p&gt;让我们在 Gitlab 中创建一个流水线并在 5 台机器上试一试。创建一个.gitlab-ci.yaml 文件，让我们开始吧。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;stages&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;tests&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;stage&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;mcr.microsoft.com&#x2F;playwright:v1.38.0-focal
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;parallel&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;matrix&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;SHARD_INDEX&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;SHARD_TOTAL&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm ci
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test --shard=$SHARD_INDEX&#x2F;$SHARD_TOTAL
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我们使用“npm ci”命令安装我们的依赖项。&lt;&#x2F;p&gt;
&lt;p&gt;然后我们通过运行“npx playwright test — shard=$SHARD_INDEX&#x2F;$SHARD_TOTAL”命令来运行测试。让我们看看等待我们的是什么。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*mazgQFSwmx7mD2vt8-6_ag.png&quot; alt=&quot;图片6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;流水线&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*dQKvVzT6FubEToBz66cwJw.png&quot; alt=&quot;图片7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;总共 5 个作业&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*J4MLmw-mSN-lk0RSSWvlTA.png&quot; alt=&quot;图片8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;第一个作业的详细信息&lt;&#x2F;p&gt;
&lt;p&gt;正如你在这里看到的，流水线在大约 1 分钟内完成了 5 个并行运行的作业。实际上，测试运行了 30 秒，剩下的时间是环境准备时间，如你在截图中看到的。实际上，它从 10 分钟变成了 30 秒。&lt;&#x2F;p&gt;
&lt;p&gt;通过使用 Playwright 的工作进程和分片功能，你可以在短时间内运行大量测试用例。下次文章再见！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yuan-wen-di-zhi&quot;&gt;原文地址&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;hasangurhan.medium.com&#x2F;how-did-i-run-tests-at-least-10x-faster-with-playwright-c25687982caf&quot;&gt;这里&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>使用chatgpt4结合playwright进行智能化ui自动化测试</title>
        <published>2024-07-11T00:00:00+00:00</published>
        <updated>2024-07-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-and-chatgpt4/"/>
        <id>https://playwright.itest.info/blog/playwright-and-chatgpt4/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-and-chatgpt4/">&lt;p&gt;之前介绍过一个使用 chatgpt4 分析 dom，然后生成 puppeteer 代码进行自动化的测试工具 Taxy AI。今天发现有人推荐了一个使用 chatgpt4 生成 playwright 代码的测试工具&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mayt&#x2F;BrowserGPT&quot;&gt;BrowserGPT&lt;&#x2F;a&gt;，稍微看了一下，原理比较简单，比较适合我们去研究一下，顺便打开思路。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;yan-shi-dong-hua&quot;&gt;演示动画&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;github.com&#x2F;mayt&#x2F;BrowserGPT&#x2F;raw&#x2F;master&#x2F;public&#x2F;browsergpt.gif&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ju-ti-shi-yong&quot;&gt;具体使用&lt;&#x2F;h3&gt;
&lt;p&gt;因为我没有 chatgpt4 的 key，所以没办法直接上手使用，只能通过文档去猜测一下具体用法。&lt;&#x2F;p&gt;
&lt;p&gt;BrowserGPT 设置了 openai 的 key 以及 start url 之后就可以在命令行里运行了，大致的使用方式是输入一些自然语言，然后 BrowserGPT 执行 AI 生成的 playwright 代码，实现自然语言自动化的功能。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;go to hn
&lt;&#x2F;span&gt;&lt;span&gt;click on the abc article
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;比如上面的一些描述就实现了去 hacknews 网站点击 abc 这篇文章的功能。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;yuan-li-fen-xi&quot;&gt;原理分析&lt;&#x2F;h3&gt;
&lt;p&gt;大致看了一下，执行的流程是这样的，代码在&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mayt&#x2F;BrowserGPT&#x2F;blob&#x2F;master&#x2F;index.js&quot;&gt;这里&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;获取初始化的 url，打开 chrome 浏览器，跳转到这个 url&lt;&#x2F;li&gt;
&lt;li&gt;在命令行里启动 prompt，也就是给用户一个输入的 ui&lt;&#x2F;li&gt;
&lt;li&gt;初始化 openai 的 api&lt;&#x2F;li&gt;
&lt;li&gt;写个死循环，每次用户输入之后调用&lt;code&gt;doAction&lt;&#x2F;code&gt;函数&lt;&#x2F;li&gt;
&lt;li&gt;在&lt;code&gt;doAction&lt;&#x2F;code&gt;函数里简化当前页面的 dom 元素&lt;&#x2F;li&gt;
&lt;li&gt;将简化过的 dom 元素传给 chatgpt，让 gpt 根据 playwright 的示例生成代码&lt;&#x2F;li&gt;
&lt;li&gt;执行 chatgpt 生成的代码&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这里最有意思的部分是&lt;code&gt;doAction&lt;&#x2F;code&gt;函数&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;doAction&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;chatApi&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;task&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;options &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;systemPrompt &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;You are a programmer and your job is to write code. You are working on a playwright file. You will write the commands necessary to execute the given input. 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Context:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Your computer is a mac. Cmd is the meta key, META.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;You are on the website ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;location.href&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;)}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Here is the overview of the site
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;parseSite&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;)}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Your output should just be the code that is valid for PlayWright page api. When given the option to use a timeout option, use 1s. Except when using page.goto() use 10s. For actions like click, use the force option to click on hidden elements.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;User: click on show hn link
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Assistant:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\`\`\`
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;const articleByText = &amp;#39;Show HN&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;await page.getByText(articleByText, { exact: true }).click(articleByText, {force: true, hidden: true});
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\`\`\`
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;code &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;code &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;queryGPT&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chatApi&lt;&#x2F;span&gt;&lt;span&gt;, [
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;SystemChatMessage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;systemPrompt&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;HumanChatMessage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;task&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    ]);
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;catch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;e&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.data.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;AsyncFunction&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;code&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;catch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;e&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;e&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可以看到给 chatgpt 的提示词是两个部分合成的，一个是预先定义的，另一个是用户输入的指令。&lt;&#x2F;p&gt;
&lt;p&gt;简单分析一下系统的提示词，大概分这几个部分&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;角色和任务定义: &lt;code&gt;You are a programmer and your job is to write code. You are working on a playwright file. You will write the commands necessary to execute the given input.&lt;&#x2F;code&gt;，让 chatgpt 去扮演程序员，&lt;&#x2F;li&gt;
&lt;li&gt;环境描述: &lt;code&gt;Your computer is a mac. Cmd is the meta key, META.&lt;&#x2F;code&gt;，运行环境是 mac&lt;&#x2F;li&gt;
&lt;li&gt;提供主要信息: &lt;code&gt;Here is the overview of the site;${await parseSite(page, options)}&lt;&#x2F;code&gt;，这里把当前页面的 html 元素的 body 简化了一下，再喂给了 AI&lt;&#x2F;li&gt;
&lt;li&gt;提供示例: 最后提供一个示例，让 ai 按照例子生成代码，并且让 ai 不要废话，只需要生成代码就好了&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;一句话描述就是把当前的网页的 html 减肥一下，把页面的 dom 和用户的命令传给 ai，让 ai 生成 playwright 代码，最后执行 ai 生成的代码。&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fan-hua&quot;&gt;泛化&lt;&#x2F;h3&gt;
&lt;p&gt;泛化一下，使用 ai 进行一些测试工具的设计我们可以有下面的一些尝试&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;压缩信息。把页面&#x2F;api 的描述用尽量简化的方式描述出来，作为输入的一部分喂给 ai，尽量不要基于 ai 自身的先验（也就是学习过的）知识来进行任务的执行，这样可能会产生幻觉&lt;&#x2F;li&gt;
&lt;li&gt;定义好的提示词。一般来说好的提示词包含：角色定义，任务描述，示例以及规范的输出描述&lt;&#x2F;li&gt;
&lt;li&gt;利用 ai 生成代码或其他文本（比如手工测试用例）&lt;&#x2F;li&gt;
&lt;li&gt;利用执行 ai 的生成的代码或者保存 ai 生成的内容&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;其实按道理来说还应该使用 ai 去检查之前的生成的代码或内容，让 ai 提供改进方案或者建议，这样输出的代码可能准确性会更高，不过忽略掉这个步骤问题也不大。更泛化一点看，其实这个工具有点简单的 ai agent 的样子，把 agent 与测试结合起来应该是未来一个不错的尝试方向。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ke-neng-hui-yu-dao-de-wen-ti&quot;&gt;可能会遇到的问题&lt;&#x2F;h3&gt;
&lt;p&gt;这个工具目前看来实用性是有限的，这是因为当前时间节点，也就是 2023 年 10 月份，ai 可能会存在下面的问题&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;输出不稳定。同样的提示词可能每次生成的代码是不稳定的，不过之前看到 openai 的 dalle 3 提示词洋洋千言似乎每次得到的结果都很稳定，估计后面的模型在输出稳定性上会有极大的提升，另外好的提示词也能提升输出的稳定性；&lt;&#x2F;li&gt;
&lt;li&gt;无法生成最新 api 的代码。playwright 的 api 变化很快，我记得半年前写的一些代码现在就可能跑不起来了，不过因为 gpt4 的训练内容并不是最新的，所以生成的代码可能是基于旧的 api，有点鸡肋。不过如果是生成 selenium 的代码的话可能实用性会更强一点，毕竟 se 的主要接口这些年都没怎么变过；&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;最后大家可以头脑风暴一下，除了让 ai 把自然语言转成代码进行自动化测试之前，ai 还能在测试领域有什么样的应用呢？&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>playwright入门视频教程 by 虫师</title>
        <published>2024-07-11T00:00:00+00:00</published>
        <updated>2024-07-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-tutorials/"/>
        <id>https://playwright.itest.info/blog/playwright-tutorials/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-tutorials/">&lt;h2 id=&quot;chong-shi-lu-zhi-de-playwright-xi-lie-ru-men-shi-pin&quot;&gt;虫师录制的 playwright 系列入门视频&lt;&#x2F;h2&gt;
&lt;p&gt;这套包含 12 集的 Playwright 教程被称为初学者必备，因为&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;全面覆盖基础知识&lt;&#x2F;strong&gt;：教程从 Playwright 的安装开始，逐步讲解 API 使用、断言方法以及 Page Object 模式。这些都是进行自动化测试的基本要素，能帮助初学者系统地掌握 Playwright 的核心功能。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;循序渐进的学习方式&lt;&#x2F;strong&gt;：教程内容按顺序排列，从最基础的安装和配置到进阶的应用和优化，适合初学者逐步理解和掌握各个知识点，不会感到难以跟上。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;实际应用案例&lt;&#x2F;strong&gt;：通过具体的示例和实践操作，初学者可以更好地理解和应用所学的知识。这些示例不仅有助于理解理论知识，还能帮助初学者积累实战经验。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Page Object 模式&lt;&#x2F;strong&gt;：Page Object 模式是一种设计模式，能够提高测试代码的可维护性和可读性。在教程中详细介绍这个模式，可以帮助初学者养成良好的编程习惯，为未来的大规模自动化测试打下基础。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;解决常见问题&lt;&#x2F;strong&gt;：教程会介绍并解决初学者在使用 Playwright 时可能遇到的常见问题和错误，提供实用的技巧和建议，帮助初学者快速上手并避免常见陷阱。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;丰富的资源和支持&lt;&#x2F;strong&gt;：通过这套教程，初学者可以获取到丰富的学习资源和技术支持，包括示例代码、文档链接以及社区讨论等，帮助他们在学习过程中有更多的参考和帮助。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;shi-pin-di-zhi&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.bilibili.com&#x2F;video&#x2F;BV1Hs4y1B76j&#x2F;&quot;&gt;视频地址&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;div class=&quot;w-full md:w-1&#x2F;2 mx-auto text-center&quot;&gt;
    &lt;iframe src=&quot;&#x2F;&#x2F;player.bilibili.com&#x2F;player.html?isOutside=true&amp;aid=996617561&amp;bvid=BV1Hs4y1B76j&amp;cid=1140874845&amp;p=1&quot; scrolling=&quot;no&quot; border=&quot;0&quot; frameborder=&quot;no&quot; framespacing=&quot;0&quot; allowfullscreen=&quot;true&quot; width=&quot;400px&quot; height=&quot;300px&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>playwright和selenium的区别</title>
        <published>2024-07-11T00:00:00+00:00</published>
        <updated>2024-07-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-vs-selenium/"/>
        <id>https://playwright.itest.info/blog/playwright-vs-selenium/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-vs-selenium/">&lt;p&gt;playwright 是微软推出的一款 e2e（端到端）测试工具，支持多种语言及浏览器，那么它会成为下一个 selenium 吗？&lt;&#x2F;p&gt;
&lt;p&gt;我们首先看看 playwright 和 selenium 之间的区别。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwright-he-selenium-de-qu-bie&quot;&gt;playwright 和 selenium 的区别&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright 和 Selenium 都是用于 web 自动化测试的工具,但它们有一些重要的区别。以下是它们的主要对比:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;浏览器支持:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Playwright: 原生支持 Chromium, Firefox, 和 WebKit。&lt;&#x2F;li&gt;
&lt;li&gt;Selenium: 支持更多浏览器,包括 Chrome, Firefox, Safari, IE 等。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;架构:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Playwright: 使用浏览器特定的协议直接与浏览器通信。&lt;&#x2F;li&gt;
&lt;li&gt;Selenium: 使用 WebDriver 协议,需要额外的驱动程序。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;性能:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Playwright: 通常更快,因为它直接与浏览器通信。&lt;&#x2F;li&gt;
&lt;li&gt;Selenium: 可能较慢,因为它通过 WebDriver 进行通信。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;自动等待:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Playwright: 内置智能等待机制。&lt;&#x2F;li&gt;
&lt;li&gt;Selenium: 需要手动实现显式或隐式等待。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;多标签页和 iframe 处理:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Playwright: 提供更简单的 API 来处理多标签页和 iframe。&lt;&#x2F;li&gt;
&lt;li&gt;Selenium: 处理这些场景相对复杂。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;移动测试:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Playwright: 主要 focused on 桌面浏览器,但可以模拟移动设备。&lt;&#x2F;li&gt;
&lt;li&gt;Selenium: 通过 Appium 支持真实移动设备测试。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;语言支持:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Playwright: 支持 JavaScript, TypeScript, Python, .NET, 和 Java。&lt;&#x2F;li&gt;
&lt;li&gt;Selenium: 支持更多语言,包括上述语言以及 Ruby, PHP 等。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;社区和生态系统:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Playwright: 较新,但发展迅速。&lt;&#x2F;li&gt;
&lt;li&gt;Selenium: 历史悠久,有大量的社区支持和插件。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;网络拦截和修改:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Playwright: 提供强大的内置功能。&lt;&#x2F;li&gt;
&lt;li&gt;Selenium: 需要借助第三方工具。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;学习曲线:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Playwright: 对于新项目来说可能更容易上手。&lt;&#x2F;li&gt;
&lt;li&gt;Selenium: 有更多的学习资源,但某些概念可能较复杂。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;selenium-de-cheng-gong-de-yuan-yin&quot;&gt;Selenium 的成功的原因&lt;&#x2F;h2&gt;
&lt;p&gt;selenium 作为浏览器自动化项目来说是非常成功的存在。Selenium 现在已经被下载了几百万次，并继续在全球范围内被广泛接受和使用。&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Selenium 是开源的，支持多种（如 Java、C#、Js、Python、Ruby、Perl 等），支持所有的浏览器（chrome、firefox、edge、ie、safari、opera 等），可以在多种操作系统（Windows、MAC、Linux）上运行。&lt;&#x2F;li&gt;
&lt;li&gt;Selenium 功能强大--它可以做 web 测试，也能做跨浏览器兼容性测试。另外 selenium 设计的初衷是浏览器的自动化，所以除了用作测试之外，selenium 还在 web 自动化操作领域有所建树。&lt;&#x2F;li&gt;
&lt;li&gt;Selenium 有一个庞大的用户社区，可以帮助你快速入门。&lt;&#x2F;li&gt;
&lt;li&gt;与其他开源工具相比，Selenium 非常稳定，它的实现甚至成了标准的 w3c 协议。&lt;&#x2F;li&gt;
&lt;li&gt;最后，Selenium 社区是充满活力的，定期举行许多活动和研讨会，你可以与志同道合的人讨论最新的工具和技术。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;playwright-hui-cheng-wei-xia-yi-ge-selenium-ma&quot;&gt;playwright 会成为下一个 selenium 吗？&lt;&#x2F;h2&gt;
&lt;p&gt;考虑到现代 Web 应用自动化，Selenium WebDriver 似乎是最受欢迎的工具之一，然而，像 Playwright、Puppeteer、Cypress 这样的替代工具正在出现，并争取在一段长时间之后能对其进行超越。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 是一个 JavaScript 框架，支持在前端实现 Web 应用程序的自动化。它在后端使用 Node.js，就像 Puppeteer 那样。它扩展了该框架，为用户提供了编写端到端测试或隔离测试应用程序特定部分所需的所有工具。&lt;&#x2F;p&gt;
&lt;p&gt;支持使用包括 Java、Js、C#、Python 在内的语言编写测试用例，并像 Selenium WebDriver 一样在任何浏览器和任何操作系统上运行。它是开源的，很容易使用，支持单兵作战和团队协同。&lt;&#x2F;p&gt;
&lt;p&gt;在 UI 自动化领域，Playwright 能够成为下一个 Selenium 的主要原因有以下七个方面。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Playwright 得到了微软的支持，其作者来自 Puppeteer（谷歌）团队，因此 playwright 可以吸收 Puppeteer 积极的方面。另外，它已经了一些版本来支持多种编程语言，社区的反馈也非常积极。简而言之微软的钞能力和干爹属性使其相对其他开源项目来说可能会有更多的持续性。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Playwright 的架构更简化，它摆脱了 selenium 复杂的设置和维护本地 driver 的繁琐过程，基本上开箱即用，工程化方面的实践也更加深入。初学 selenium 的同学应该记得 selenium 安装之后没有下载 driver 的话就是不能用的，特定版本的浏览器需要特定版本的 driver 配合，对于一些长期项目的维护来说确实有时候会带来额外的工作量。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Playwright 的测试执行速度非常高（平均比 selenium 快 40%），因为它使用 JavaScript 引擎如 Node.js 来运行测试，而不是 Selenium 的 driver 程序。因此，与 Selenium WebDriver 相比，使用 Playright 可以大大降低测试执行时间。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;与 Selenium WebDriver 不同，Playwright 除了支持测试页面的全屏截图外，还支持边测试边录屏，感觉现代化了不少。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;与 Selenium WebDriver 相比，Playright 的维护成本更低，因为它使用内部等待，而不像 Selenium WebDriver 需要管理显式等待。这大大降低了总的代码编写和维护成本。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Playwright 除了支持 web 自动化测试外，还支持 RESTFul API 测试。这使测试人员可以灵活地使用 Playwright 测试他们的后端服务。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;最后，Playwright 可以跟浏览器的开发者工具进行集成，这使得用 Playwright 编写开发测试非常容易和简单。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;总之 playwright 很有潜力成为下一个 selenium，成为开源自动化测试的现象级项目。&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;na-wo-ying-gai-xue-xi-playwright-huan-shi-selenium&quot;&gt;那我应该学习 playwright 还是 selenium？&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;如果你时间有限，代码能力有限，那么 playwright 是快速入门的首选;&lt;&#x2F;li&gt;
&lt;li&gt;如果你对 ui 自动化测试有着更高的追求和热情，那么两种工具都很值得学习;&lt;&#x2F;li&gt;
&lt;li&gt;如果你是 selenium 的忠实用户，那么了解 playwright 也是一个不错的选择和 side project;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;总之，如果时间有限就直接 playwright，如果精力有剩余的话，两种工具都非常值得学习。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>playwright简介</title>
        <published>2024-07-11T00:00:00+00:00</published>
        <updated>2024-07-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/what-is-playwright/"/>
        <id>https://playwright.itest.info/blog/what-is-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/what-is-playwright/">&lt;p&gt;playwright 是微软推出的一款 ui 自动化测试工具(浏览器自动化测试工具)，据说开发小组的核心人员来自 google 的 puppeteer 团队，真是一出生就有了个好爹而且好爹的亲爹也是爹中的战斗机，这就让人非常期待了。&lt;&#x2F;p&gt;
&lt;p&gt;以下是 Playwright 的主要特性:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;多浏览器支持: 支持 Chromium、Firefox 和 WebKit 引擎的浏览器。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;跨平台: 可在 Windows、macOS 和 Linux 上运行。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;多语言 API: 提供 JavaScript&#x2F;TypeScript、Python、Java 和.NET 等多种编程语言的 API。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;自动等待: 智能等待元素准备就绪,减少测试中的超时和随机失败。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;强大的选择器: 支持文本、CSS 和 XPath 选择器,以及自定义选择器。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;网络拦截: 可以拦截和修改网络请求和响应。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;移动设备模拟: 可以模拟移动设备的屏幕尺寸、方向和触摸事件。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;并发测试: 支持并行执行测试以提高效率。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;调试工具: 提供内置的调试器和代码生成器。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;持续集成: 易于集成到 CI&#x2F;CD 流程中。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Playwright 的设计目标是提供一个现代化、可靠和高效的自动化测试解决方案,适用于现代 web 应用程序的测试需求。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xia-zai-yu-an-zhuang&quot;&gt;下载与安装&lt;&#x2F;h2&gt;
&lt;p&gt;playwright 应该是使用 JavaScript 进行开发的，不过考虑到大部分的测试同学可能对 python 更为熟悉，这里的安装就以 python 版本为例。&lt;&#x2F;p&gt;
&lt;p&gt;在命令行中输入&lt;code&gt;pip install pytest-playwright&lt;&#x2F;code&gt;，等 playwright 安装成功之后再输入命令&lt;code&gt;playwright install&lt;&#x2F;code&gt;，这条命令的作用是安装测试所需要的各种浏览器支持，相比较 selenium 需要先安装浏览器再安装相应版本的 driver，playwright 的初始化工作就显得轻松了很多。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;di-yi-ge-yong-li&quot;&gt;第一个用例&lt;&#x2F;h2&gt;
&lt;p&gt;接下来按照官方文档里的例子我们来跑第一个 playwright 用例。&lt;&#x2F;p&gt;
&lt;p&gt;新建一个名为 test_my_app.py 的文件，然后把文档中的例子一字不差的进行拷贝粘贴。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;re
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;playwright.sync_api &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Page, expect
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test_homepage_has_Playwright_in_title_and_get_started_link_linking_to_the_intro_page&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;: Page):
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;playwright.dev&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Expect a title &amp;quot;to contain&amp;quot; a substring.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(page).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;to_have_title&lt;&#x2F;span&gt;&lt;span&gt;(re.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;compile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# create a locator
&lt;&#x2F;span&gt;&lt;span&gt;    get_started &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=Get Started&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Expect an attribute &amp;quot;to be strictly equal&amp;quot; to the value.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(get_started).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;to_have_attribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;docs&#x2F;intro&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Click the get started link.
&lt;&#x2F;span&gt;&lt;span&gt;    get_started.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Expects the URL to contain intro.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(page).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;to_have_url&lt;&#x2F;span&gt;&lt;span&gt;(re.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;compile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.*intro&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然后在命令行里输入 pytest，视网速而定，等待个 10 秒钟左右，就可以看运行结果了。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;collected &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;item
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;test_my_application.py .                                                                                                                       [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;================================================================= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;passed &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;7.08&lt;&#x2F;span&gt;&lt;span&gt;s &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;==================================================================
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;上面的用例去到了 playwright 的官网，定位并点击了名为 Get Started 的链接，最后断言页面上的 url 里含有 intro 这个字符串。&lt;&#x2F;p&gt;
&lt;p&gt;1 passed 表示运行了 1 个用例并且用例成功通过。另外默认情况下 playwright 是以 headless 的模式运行的，这表示运行的过程中浏览器在后台打开和工作，所以看不到浏览器界面是常规操作，不需要紧张和疑惑。&lt;&#x2F;p&gt;
&lt;p&gt;如果遇到了网络超时的情况，可以试试用下面的用例去替换。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;re
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;playwright.sync_api &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Page, expect
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#ff5e5e;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test_homepage_has_Playwright_in_title_and_get_started_link_linking_to_the_intro_page&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;: Page
&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;www.itest.info&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Expect a title &amp;quot;to contain&amp;quot; a substring.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(page).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;to_have_title&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;重定向科技&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# create a locator
&lt;&#x2F;span&gt;&lt;span&gt;    plan &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;css=.navbar-nav li &amp;gt;&amp;gt; nth=1 &amp;gt;&amp;gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Expect an attribute &amp;quot;to be strictly equal&amp;quot; to the value.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(plan).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;to_have_attribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;plans&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Click the get started link.
&lt;&#x2F;span&gt;&lt;span&gt;    plan.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Expects the URL to contain intro.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(page).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;to_have_url&lt;&#x2F;span&gt;&lt;span&gt;(re.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;compile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.*plans&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h2&gt;
&lt;p&gt;playwright 在 2023 年下半年表现非常出色，已经成为了很多开发人员的首选工具，总之在 2024 年的话，playwright 是最值得学习的自动化测试工具。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>关于本站</title>
        <published>2024-07-07T00:00:00+00:00</published>
        <updated>2024-07-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/pages/about/"/>
        <id>https://playwright.itest.info/pages/about/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/pages/about/">&lt;h2 id=&quot;wei-shen-me-hui-chuang-jian-playwright-jiao-cheng-zhan&quot;&gt;为什么会创建 playwright 教程站&lt;&#x2F;h2&gt;
&lt;p&gt;playwright 是 &lt;a href=&quot;https:&#x2F;&#x2F;www.itest.info&#x2F;posts&#x2F;best-ui-automation-tool-you-can-learn-in-2024&quot;&gt;2024 年最值得学习的自动化测试工具&lt;&#x2F;a&gt;，然而中文的资料非常有限，所以我创建了这个站点，希望可以一直维护下去。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zuo-zhe-shi-shui&quot;&gt;作者是谁&lt;&#x2F;h2&gt;
&lt;p&gt;作者是乙醇和虫师(就是那个博客园的虫师)，我创建和录制过很多&lt;a href=&quot;https:&#x2F;&#x2F;www.bilibili.com&#x2F;video&#x2F;BV1ZK4y1x7XK&#x2F;&quot;&gt;教程&lt;&#x2F;a&gt;，你可以在下面的地方找到我。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.cnblogs.com&#x2F;nbkhic&quot;&gt;乙醇的博客园&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.cnblogs.com&#x2F;fnng&quot;&gt;虫师博客园&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zh.ethanhan.cc&#x2F;&quot;&gt;乙醇的博客&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zh.ethanhan.cc&#x2F;&quot;&gt;乙醇在 b 站录教程&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;space.bilibili.com&#x2F;524416851&quot;&gt;虫师在 b 站录教程&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;wen-zhang-lai-yuan&quot;&gt;文章来源&lt;&#x2F;h2&gt;
&lt;p&gt;文章主要来源两个地方&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;原创，这个没啥可说的，直接肝就完了&lt;&#x2F;li&gt;
&lt;li&gt;翻译，我写了个爬虫去收集有意义的内容。所有的翻译资料都会加上翻译标签，并尽量保留原始链接，每篇文章我都会校对一遍，尽量保证大家都可以读懂&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;wen-zhang-de-zhi-liang&quot;&gt;文章的质量&lt;&#x2F;h2&gt;
&lt;p&gt;因为 playwright 的 api 变化很快，之前已经过期的内容我这边会人肉过滤掉，尽量保证呈现出的内容是 up to date 的&lt;&#x2F;p&gt;
&lt;p&gt;一些文章里我会加入自己的看法，很明显的夹带私货了&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wo-men-de-wei-xin-gong-zhong-hao&quot;&gt;我们的微信公众号&lt;&#x2F;h2&gt;
&lt;p&gt;跟&lt;a href=&quot;https:&#x2F;&#x2F;www.cnblogs.com&#x2F;fnng&quot;&gt;虫师&lt;&#x2F;a&gt;一起维护的微信公众号。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;qrcode.jpg&quot; alt=&quot;测试圈TC二维码&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>playwright资源汇总</title>
        <published>2024-07-07T00:00:00+00:00</published>
        <updated>2024-07-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/pages/resource/"/>
        <id>https://playwright.itest.info/pages/resource/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/pages/resource/">&lt;p&gt;我的目标是创建一个针对 Typescript&#x2F;Javascript 的 Playwright 资源库，这些资源是我在网上发现的精心挑选的内容。这个资源页面的灵感来自&lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;rahulydv&#x2F;&quot;&gt;Rahul Yadav&lt;&#x2F;a&gt;和他在&lt;a href=&quot;https:&#x2F;&#x2F;testerops.com&#x2F;playwright-2&#x2F;&quot;&gt;TesterOps&lt;&#x2F;a&gt;的工作。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;guan-fang-zi-yuan&quot;&gt;官方资源&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;&quot;&gt;Playwright.Dev&lt;&#x2F;a&gt; - Playwright 文档，包含很棒的入门指南！&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;release-notes&quot;&gt;Release Notes&lt;&#x2F;a&gt; - 通常每月一次发布，包含许多新功能！&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;discord.gg&#x2F;W9Ut4Pv9&quot;&gt;Playwright Discord&lt;&#x2F;a&gt; - 一个学习、提问和成为 Playwright 社区一部分的地方&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;community&#x2F;release-videos&quot;&gt;Playwright Video Resources&lt;&#x2F;a&gt; - 发布视频、会议视频和直播！&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;try.playwright.tech&#x2F;&quot;&gt;Try Playwright&lt;&#x2F;a&gt; - 在线游乐场，可以在浏览器中试验 Playwright。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;qi-ta-zi-yuan&quot;&gt;其他资源&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;orgs&#x2F;playwrightsolutions&#x2F;repositories&quot;&gt;PlaywrightSolutions Github Repos&lt;&#x2F;a&gt; - 这是&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;&quot;&gt;playwrightsolutions 博客&lt;&#x2F;a&gt;的大部分代码示例存放的地方。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;ray.run&#x2F;&quot;&gt;Learn Playwright&lt;&#x2F;a&gt; &amp;amp; &lt;a href=&quot;https:&#x2F;&#x2F;ray.run&#x2F;ask&quot;&gt;Ask Playwright Questions&lt;&#x2F;a&gt; by &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;lucgagan&#x2F;recent-activity&#x2F;all&#x2F;&quot;&gt;Luc Gagan&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;tag&#x2F;playwright&#x2F;&quot;&gt;Martioli - Playwright Resources&lt;&#x2F;a&gt; by &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;adrian-maciuc&#x2F;&quot;&gt;Adrian Maciuc&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;AutomationPanda&#x2F;awesome-web-testing-playwright&quot;&gt;Awesome Web Testing Playwright Tutorial&lt;&#x2F;a&gt; by &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;andrew-leland-knight&#x2F;&quot;&gt;Andrew Knight&lt;&#x2F;a&gt; - 一个可以作为学习练习的自学教程。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;testerops.com&#x2F;playwright-2&#x2F;&quot;&gt;TesterOps&lt;&#x2F;a&gt; - 由&lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;rahulydv&#x2F;&quot;&gt;Rahul Yadav&lt;&#x2F;a&gt;创建的 Playwright 文章以及许多其他 QA 和 OPS 资源&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mxschmitt&#x2F;awesome-playwright&quot;&gt;Awesome-Playwright&lt;&#x2F;a&gt; - 一个精心整理的使用 Playwright 的优秀工具、实用程序和项目列表&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.workwithloop.com&#x2F;blog-categories&#x2F;playwright-test-automation&quot;&gt;LooP&lt;&#x2F;a&gt; - 由&lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;ben-f-44778426&#x2F;&quot;&gt;Ben Fellows&lt;&#x2F;a&gt;撰写的 Playwright 测试自动化文章&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;debbie.codes&#x2F;&quot;&gt;Debbie.codes&lt;&#x2F;a&gt; - &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;debbie-o-brien-1a199975&#x2F;&quot;&gt;Debbie O&#x27;Brien&lt;&#x2F;a&gt; 是 Playwright 的代言人，也是微软 Playwright 团队的产品经理。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;testersdock.com&#x2F;category&#x2F;playwright&#x2F;&quot;&gt;TestersDock&lt;&#x2F;a&gt; - 由&lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;alapandas&#x2F;&quot;&gt;Alapan Das&lt;&#x2F;a&gt;维护的关于 Playwright（和更多内容）的常见场景的精彩指南&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;timdeschryver.dev&#x2F;blog?q=Playwright&quot;&gt;TimDeschryver.dev&lt;&#x2F;a&gt; - Tim 的博客有一些很棒的 Playwright 内容和示例！&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;shi-pin&quot;&gt;视频&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;playlist?list=PLXgRgGX8-5UVm9yioRY329rfcfy3MusiY&quot;&gt;Commit Quality&lt;&#x2F;a&gt; YouTube 频道&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@uncleaarohtesting&quot;&gt;Uncle Aaroh Testing&lt;&#x2F;a&gt; YouTube 频道&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@AutomateTogether&#x2F;videos&quot;&gt;AutomateTogether&lt;&#x2F;a&gt; YouTube 频道&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@benfellowsloop&#x2F;videos&quot;&gt;Ben Fellows&lt;&#x2F;a&gt; YouTube 频道&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;playlist?list=PL699Xf-_ilW7EyC6lMuU4jelKemmS6KgD&quot;&gt;LetCode with Koushik&lt;&#x2F;a&gt; YouTube 频道&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;ke-cheng&quot;&gt;课程&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.ministryoftesting.com&#x2F;events&#x2F;hands-on-test-automation-with-playwright-a72b4035-fc54-4320-8057-5574f87492a4&quot;&gt;Hands On Test Automation with Playwright&lt;&#x2F;a&gt; 99 分钟的工作坊 - Butch Mayhew (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;mot-playwright-workshop&#x2F;tree&#x2F;main&quot;&gt;repo&lt;&#x2F;a&gt; 参见每个进展&#x2F;练习的分支)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;testautomationu.applitools.com&#x2F;playwright-intro&#x2F;&quot;&gt;Introduction to Playwright&lt;&#x2F;a&gt; - &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;raptatinha&quot;&gt;Renata Andrade&lt;&#x2F;a&gt; - TestAutomationU&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;testautomationu.applitools.com&#x2F;playwright-advanced&#x2F;&quot;&gt;Advanced Playwright&lt;&#x2F;a&gt; - &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;raptatinha&#x2F;&quot;&gt;Renata Andrade&lt;&#x2F;a&gt; - TestAutomationU&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;bo-ke-wen-zhang&quot;&gt;博客文章&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;automationpanda.com&#x2F;2023&#x2F;07&#x2F;28&#x2F;modern-web-testing-with-playwright&#x2F;&quot;&gt;Modern Web Testing With Playwright&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;testerops.com&#x2F;playwright-and-shadow-dom-a-love-story&#x2F;&quot;&gt;Playwright and Shadow Dom a Love Story&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;bosphorusiss&#x2F;verify-pdf-contents-using-playwright-and-node-js-dd3cf6749f70&quot;&gt;Verify PDF Contents Using Playwright and NodeJS&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@jfgreffier&#x2F;faster-end-to-end-tests-with-playwright-4cbf32c3f9bf&quot;&gt;Faster End To End Tests With Playwright&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;fei-js-bo-ke-wen-zhang&quot;&gt;非 JS 博客文章&lt;&#x2F;h2&gt;
&lt;p&gt;这些内容太好，不分享不行&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;dev.to&#x2F;irarainey&#x2F;using-playwright-to-test-azure-active-directory-secured-apis-38p5&quot;&gt;Using Playwright to Test Azure Active Directory Secured APIs&lt;&#x2F;a&gt; (.NET)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;js-ts-zi-yuan&quot;&gt;JS&#x2F;TS 资源&lt;&#x2F;h2&gt;
&lt;p&gt;帮助大家更多了解 Javascript 的资源列表。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;dev.to&#x2F;codeofrelevancy&#x2F;all-about-promises-in-javascript-39lj&quot;&gt;All About Promises in Javascript&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何使用Playwright监控JavaScript控制台日志和异常</title>
        <published>2024-06-21T00:00:00+00:00</published>
        <updated>2024-06-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-to-monitor-javascript-logs-and-exceptions-with-playwright/"/>
        <id>https://playwright.itest.info/blog/how-to-monitor-javascript-logs-and-exceptions-with-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-to-monitor-javascript-logs-and-exceptions-with-playwright/">&lt;h2 id=&quot;mu-lu&quot;&gt;目录&lt;&#x2F;h2&gt;
&lt;p&gt;监控 JavaScript 日志消息是了解浏览器 上 JavaScript 运行情况的基本方法。Playwright 提供了一种高效的方式来监听页面中的控制台日志和未捕获的异常。这一功能对于开发人员和测试人员来说非常宝贵,可以帮助他们在开发周期的早期发现和解决问题。本文将指导您如何设置 Playwright 来监控 JavaScript 日志和异常,从而增强您的测试策略。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;huan-jing-she-zhi&quot;&gt;环境设置&lt;&#x2F;h2&gt;
&lt;p&gt;测试需要一个基本的 Playwright 环境。设置过程很简单,您可以在&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;intro&quot;&gt;Playwright 官方文档&lt;&#x2F;a&gt;中找到详细说明。您也可以&lt;a href=&quot;https:&#x2F;&#x2F;www.checklyhq.com&#x2F;docs&#x2F;cli&#x2F;&quot;&gt;使用 Checkly 编写和运行首个 Playwright 测试&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;之后,最好能在一个具有完全可预测日志消息和错误的页面上折腾。在我的例子中,我搭建了一个简单服务,其页面内代码如下:&lt;&#x2F;p&gt;
&lt;p&gt;consoleLogsAndErrors.js&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你好,这是一个错误&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;这是一条日志消息&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;未捕获的异常会中断JS执行,不要在生产环境中这样做! :)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;throw new &lt;&#x2F;span&gt;&lt;span&gt;Error(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;不要在try&#x2F;catch块外这样做!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;一旦您的环境准备就绪,就可以开始折腾本文讨论的技术了。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jian-ting-kong-zhi-tai-ri-zhi-xiao-xi&quot;&gt;监听控制台日志消息&lt;&#x2F;h2&gt;
&lt;p&gt;JavaScript 应用程序可以生成各种类型的控制台消息,包括日志、警告和错误。这些消息通常表示应用程序的状态或需要注意的问题。Playwright 允许您通过为页面对象添加控制台事件监听器来捕获这些消息。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-xian-kong-zhi-tai-jian-ting-qi&quot;&gt;实现控制台监听器&lt;&#x2F;h3&gt;
&lt;p&gt;要监听控制台消息,您需要为页面对象添加一个事件监听器,如下所示:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;msg&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这可以观察并响应控制台的日志输出。我们很容易会想到简单地在这个内部函数中使用 console.log 来记录每条日志消息。然而,以下代码是无效的:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;msg&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个内部的&lt;code&gt;console.log()&lt;&#x2F;code&gt;会在页面加载过程中被执行,而 Playwright 在执行测试时并不会显示出来。相反,你应该在页面加载前初始化一个数组,然后将消息推送到数组中。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;本地主机错误日志记录&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 监听所有控制台日志
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;msg&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;({ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;msg&lt;&#x2F;span&gt;&lt;span&gt;, type: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;msg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;() });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 监听所有控制台事件并处理错误
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;localhost:3000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logs&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这个例子中,我们将每条控制台消息推送到一个&lt;code&gt;logs&lt;&#x2F;code&gt;数组中,包括消息类型和消息文本。然后我们输出&lt;code&gt;logs&lt;&#x2F;code&gt;数组(注意,这段代码实际上并不包含断言,永远不会失败)。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;li-jie-consolemessage-lei&quot;&gt;理解 ConsoleMessage 类&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright 的&lt;code&gt;ConsoleMessage&lt;&#x2F;code&gt;类代表页面上的控制台消息事件,如日志、警告、错误、调试消息等。每个&lt;code&gt;ConsoleMessage&lt;&#x2F;code&gt;实例提供有关消息的详细信息,包括其类型、文本、位置(URL 和行号),以及&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-consolemessage&quot;&gt;传递给控制台方法的任何参数&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;an-lei-xing-guo-lu-xiao-xi&quot;&gt;按类型过滤消息&lt;&#x2F;h3&gt;
&lt;p&gt;在许多情况下,您可能对监控特定类型的消息感兴趣,如错误或警告。您可以使用&lt;code&gt;ConsoleMessage&lt;&#x2F;code&gt;类的&lt;code&gt;type()&lt;&#x2F;code&gt;方法按类型过滤消息:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;错误消息: ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;()}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这种技术特别有助于监控可能影响应用程序功能或用户体验的关键问题。&lt;&#x2F;p&gt;
&lt;p&gt;虽然有&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-consolemessage&quot;&gt;日志有 18 种类型&lt;&#x2F;a&gt;,但我们最可能遇到的是&lt;code&gt;log&lt;&#x2F;code&gt;、&lt;code&gt;debug&lt;&#x2F;code&gt;、&lt;code&gt;info&lt;&#x2F;code&gt;、&lt;code&gt;error&lt;&#x2F;code&gt;和&lt;code&gt;warning&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fang-wen-xiao-xi-wei-zhi&quot;&gt;访问消息位置&lt;&#x2F;h3&gt;
&lt;p&gt;Playwright 允许您访问生成控制台消息的位置,包括 URL、行号和列号。这些信息对调试非常有价值,因为它们可以精确定位消息的确切来源:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;location &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;来自 ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}:${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lineNumber&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}:${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;columnNumber&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;} 的消息&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果您使用上面&quot;设置&quot;部分中的演示 JS,结果将是:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;[
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;来自 http:&#x2F;&#x2F;localhost:3000&#x2F;index.js:0:8 的消息&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;来自 http:&#x2F;&#x2F;localhost:3000&#x2F;index.js:1:8 的消息&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;zai-ce-shi-zhong-duan-yan-kong-zhi-tai-xiao-xi&quot;&gt;在测试中断言控制台消息&lt;&#x2F;h3&gt;
&lt;p&gt;高级监控还包括在测试中断言特定控制台消息的存在或不存在。通过将消息收集到数组中,您可以对测试执行期间应该或不应该出现的消息进行断言:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;本地主机错误日志记录&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;msg&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;msg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;()));
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;localhost:3000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logs&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; [ &amp;#39;你好,这是一个错误&amp;#39;, &amp;#39;这是一条日志消息 苹果&amp;#39; ]
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logs&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你好,这是一个错误&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;注意,&lt;code&gt;toContain&lt;&#x2F;code&gt;是一个简单的字符串匹配,不接受正则表达式模式。在上面的代码里,运行&lt;code&gt;expect(logs).toContain(&#x27;苹果&#x27;)&lt;&#x2F;code&gt;将会失败,因为它需要完全匹配数组中的字符串。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bu-huo-wei-chu-li-yi-chang&quot;&gt;捕获未处理异常&lt;&#x2F;h2&gt;
&lt;p&gt;未捕获的异常是可能让应用程序无法正常运行的关键错误。Playwright 提供了一种简单的方法来使用 pageerror 事件捕获这些异常。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-xian-yi-chang-jian-ting-qi&quot;&gt;实现异常监听器&lt;&#x2F;h3&gt;
&lt;p&gt;与捕获控制台消息类似,您可以通过为 pageerror 事件添加事件监听器来监听未捕获的异常:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pageerror&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;exception&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;exception&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 稍后在测试中
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;[ &amp;#39;不要在try&#x2F;catch块外这样做!&amp;#39; ]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这里,我们将异常消息收集到一个 errors 数组中。这种方法允许您收集和监控在 Playwright 测试用例执行期间发生的未捕获异常。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ji-yu-ri-zhi-he-yi-chang-jin-xing-duan-yan&quot;&gt;基于日志和异常进行断言&lt;&#x2F;h2&gt;
&lt;p&gt;有了捕获的控制台消息和异常,您现在可以断言某些条件,以确保您的应用程序按预期运行。例如,您可能想验证是否没有错误或警告消息:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:8080&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 额外的测试步骤...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logs&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveLength&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveLength&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这些断言确认在测试执行期间没有遇到控制台消息或未捕获的异常。如果捕获到任何消息或异常,测试将失败。请注意,对日志消息数量进行过多断言可能不是一个好主意,特别是当您期望的数量不是零时，想想自动化测试的目的是什么？是尽量模拟用户的操作，真实的用户对控制台的 js 日志是不感兴趣的。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-ruan-duan-yan-jin-xing-chi-xu-jian-kong&quot;&gt;使用软断言进行持续监控&lt;&#x2F;h2&gt;
&lt;p&gt;** ❌ 乙醇的注释 👀: 这里的实现方式有问题，正确的实现应该是用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions#soft-assertions&quot;&gt;excpet.soft&lt;&#x2F;a&gt;**&lt;&#x2F;p&gt;
&lt;p&gt;在某些情况下,您可能希望即使遇到日志消息或异常,也能继续运行测试。这可以通过实现软断言来实现,它允许测试继续进行,同时仍然报告遇到的问题，也就是测试用例不失败，但是错误却会被记录下来&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 软断言实现示例
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logs&lt;&#x2F;span&gt;&lt;span&gt;.length &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;warn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;检测到控制台消息:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logs&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;.length &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;检测到未捕获的异常:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;软断言特别适用于当作日志来用。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;geng-jin-yi-bu&quot;&gt;更进一步&lt;&#x2F;h2&gt;
&lt;p&gt;监控 JavaScript 的日志消息和异常是提高 Web 应用质量的强大技术。Playwright 在自动化测试中实现这种监控非常简单,为潜在问题提供了有价值的视角,并确保您的应用程序满足所需的可靠性和性能标准。通过将这些实践纳入您的测试策略,您可以主动解决问题,从而构建一个更加稳健和无错误的应用程序。&lt;&#x2F;p&gt;
&lt;p&gt;要查看 Playwright 中的日志消息实际效果,请观看 Checkly YouTube 页面上的这个视频:&lt;&#x2F;p&gt;
&lt;p&gt;如果您想加入 Playwright 用户社区,&lt;a href=&quot;https:&#x2F;&#x2F;www.checklyhq.com&#x2F;slack&#x2F;&quot;&gt;请加入 Checkly Slack&lt;&#x2F;a&gt;,其中有我们出色的#Playwright-Community 频道。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;来源 URL: https:&#x2F;&#x2F;www.checklyhq.com&#x2F;blog&#x2F;how-to-monitor-javascript-logs-and-exceptions-with-playwright&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2024-06-21&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>关于测试不稳定(flakey tests)的一些思考</title>
        <published>2024-06-10T00:00:00+00:00</published>
        <updated>2024-06-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/a-few-thoughts-on-flakey-tests-playwright-solutions/"/>
        <id>https://playwright.itest.info/blog/a-few-thoughts-on-flakey-tests-playwright-solutions/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/a-few-thoughts-on-flakey-tests-playwright-solutions/">&lt;p&gt;当一个新的测试自动化项目开始时，通常有 3 个工作重点。&lt;strong&gt;构建&lt;&#x2F;strong&gt;（创建新测试）、&lt;strong&gt;维护&lt;&#x2F;strong&gt;（随着应用程序变化更新现有测试）和&lt;strong&gt;监控&lt;&#x2F;strong&gt;（关注自动化运行情况，调查失败原因并向团队反馈发现）。&lt;&#x2F;p&gt;
&lt;p&gt;&quot;不稳定&quot;测试主要涉及自动化运行的&quot;&lt;strong&gt;监控&lt;&#x2F;strong&gt;&quot;部分。如果一个测试不稳定，深入了解原始测试失败的原因总是很重要的...这一点很关键，如果人们不采取这一步骤，可能会带来重大风险。&lt;&#x2F;p&gt;
&lt;p&gt;下面我将分享一些我在处理不稳定性方面的经验，这个列表绝不是全面的。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wo-ren-wei-de-bu-wen-ding-ce-shi&quot;&gt;我认为的不稳定测试&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;由于陷入不良状态而失败的测试。例如，你期望处于登录状态，但实际上处于登出状态，或者可能另一个测试&#x2F;用户修改了你正在断言的数据。&lt;&#x2F;li&gt;
&lt;li&gt;用于测试的数据质量不佳。例如，你发送了一个 11 位的电话号码，或者由于运行自动化的机器与你的本地时区不同，你发送了一个带有错误 UTC 偏移的日期时间戳。&lt;&#x2F;li&gt;
&lt;li&gt;自动化代码中是否存在竞争条件，在页面数据加载完成之前就尝试进行断言？&lt;&#x2F;li&gt;
&lt;li&gt;是否由于未正确实现 await 语法而导致测试超时？&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;调查每次失败是至关重要的。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;对于这些场景中的每一个，仅仅因为测试偶尔失败并不意味着它是一个不稳定的测试。&lt;strong&gt;调查每次失败是至关重要的&lt;&#x2F;strong&gt;，确保问题不是出在被测应用程序上，而是其他因素。有可能导致测试失败的潜在问题实际上是一个需要解决的真正 bug。我能想到的一个例子是我们系统中的一个 bug，测试在晚上 6:00 通过但在晚上 11:00 失败，这是由于应用程序中的时区 bug 造成的。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ru-he-chu-li-bu-wen-ding-ce-shi&quot;&gt;如何处理不稳定测试？&lt;&#x2F;h2&gt;
&lt;p&gt;以一种容易导致测试不稳定的方式构建测试是很容易做到的（即使在我意识到并试图避免的情况下，我也在几个项目中这样做过）。测试数据和状态是我遇到的最大问题，必须在测试自动化项目开始时和整个过程中考虑这些因素。&lt;&#x2F;p&gt;
&lt;p&gt;如果你的主要问题是状态，由于特定的 cookie，Playwright 团队发布了一些新功能来清除某些 cookie，可以在&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;a-few-thoughts-on-flakey-tests-playwright-solutions&#x2F;Building%20your%20tests%20in%20a%20way%20where%20this%20can%20happen%20is%20really%20easy%20to%20do%20&#x2F;(I&amp;#x27;ve%20done%20it%20on%20a%20few%20projects%20even%20when%20I%20was%20aware%20and%20trying%20not%20to&#x2F;).%20Test%20Data%20and%20State%20have%20to%20be%20considered%20at%20the%20beginning%20and%20throughout%20building%20automated%20tests.%20If&quot;&gt;1.43 版本发布说明&lt;&#x2F;a&gt;中找到。&lt;&#x2F;p&gt;
&lt;p&gt;当你遇到不稳定测试时，要判断这个测试是否仍然有价值。&lt;strong&gt;如果测试没有价值，就删除它！&lt;&#x2F;strong&gt; 如果它有价值，要么修复它，要么将其设置为不频繁运行且不阻塞构建的测试集的一部分。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果测试没有价值，就删除它！&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;我处理不稳定测试的一种方法是通过 Playwright 标签。我特别使用&lt;code&gt;@unsatisfactory&lt;&#x2F;code&gt;作为标签名，因为它让我失望。实际上，在我的日常工作中，我有大约 70 个 API 测试以这种方式分类，我们每周运行一次以获得反馈，但不是在每次构建时运行。使用 Playwright 的&lt;code&gt;npx playwright test --grep-invert @unsatisfactory&lt;&#x2F;code&gt;命令，你可以在主 CI 管道中运行所有 spec，除了那些标记为&lt;code&gt;@unsatisfactory&lt;&#x2F;code&gt;的。&lt;&#x2F;p&gt;
&lt;p&gt;如果你想实现标签，请查看&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;release-notes#version-142&quot;&gt;1.42 版本发布说明&lt;&#x2F;a&gt;，了解在测试中创建标签的最新方法。你不再需要在测试标题中添加标签（尽管如果你愿意，仍然可以这样做）。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;不稳定测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  {
&lt;&#x2F;span&gt;&lt;span&gt;    tag: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@unsatisfactory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;稳定测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;wo-ren-wei-de-bu-wen-ding-huan-jing-ji-chu-she-shi&quot;&gt;我认为的不稳定环境&#x2F;基础设施&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;第三方服务是否失败？&lt;&#x2F;li&gt;
&lt;li&gt;服务&#x2F;API 调用是否达到了速率限制？&lt;&#x2F;li&gt;
&lt;li&gt;测试是否每隔一次运行就通过，因为我们只将最新代码部署到一半的运行服务器上？&lt;&#x2F;li&gt;
&lt;li&gt;容器是否因为我们在最便宜的服务器上运行（因为它是测试环境）而崩溃？&lt;&#x2F;li&gt;
&lt;li&gt;自动化是否在混沌测试或负载测试期间运行？&lt;&#x2F;li&gt;
&lt;li&gt;开发人员是否推送了代码并导致环境重新部署？&lt;&#x2F;li&gt;
&lt;li&gt;数据团队是否大量操作数据库以重建他们的数据仓库？&lt;&#x2F;li&gt;
&lt;li&gt;你所在地区的 AWS 是否出现故障？&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;有许多不同的情况可能导致你的环境或基础设施使测试返回&quot;失败&quot;。在这些情况下，与上面的不稳定测试一样，调查每次失败并确定没有真正的问题是至关重要的。不要仅仅做出假设，深入研究失败消息、网络跟踪和基础设施日志&#x2F;仪表板，以确保测试失败的原因。&lt;&#x2F;p&gt;
&lt;p&gt;我遇到过几次测试开始间歇性失败的情况，第一天我确信这是一个不稳定的基础设施问题，但第二天我决定深入调查。我检查了我们的 pganalyze 工具，它提供了对我们数据库查询的概览，清楚地看到问题是由于一个新查询执行时间是原来的 20 倍。与开发人员合作后，我们为表添加了一个索引，在问题进入生产环境之前就修复了这个 bug！&lt;&#x2F;p&gt;
&lt;p&gt;如果你接受可能有一个 api 或测试会失败，但你仍然想测试并确保它至少能在 X 次中通过 1 次，你可以尝试在测试中使用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions#expectpoll&quot;&gt;expect.poll()&lt;&#x2F;a&gt;或&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions#expecttopass&quot;&gt;expect.toPass()&lt;&#x2F;a&gt;。我发现使用这些方法的最佳场合是在测试运行之前&quot;创建测试数据&quot;时，因为如果你在实际测试断言步骤中使用它们，可能会掩盖系统的潜在问题。&lt;&#x2F;p&gt;
&lt;p&gt;你还可以通过模拟某些请求和&#x2F;或响应来使测试更稳定。&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;mock#mock-api-requests&quot;&gt;Playwright 文档&lt;&#x2F;a&gt;提供了许多好的例子！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fang-zhi-jiang-bu-wen-ding-ce-shi-yin-ru-ni-de-xiang-mu&quot;&gt;防止将不稳定测试引入你的项目&lt;&#x2F;h2&gt;
&lt;p&gt;理想情况下，你不想将不稳定测试引入你的仓库。防止这种情况的最佳方法是在代码合并到你的测试仓库之前多次运行你的测试。下面的文章将指导你如何循环运行测试，以在合并到主代码分支之前检查不稳定性。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;in-playwright-test-is-there-an-easy-way-to-loop-the-test-multiple-times-to-check-for-flakiness&#x2F;&quot;&gt;在 Playwright Test 中是否有一种简单的方法来多次循环测试以检查不稳定性？我发现在合并新代码到测试套件的主分支之前，在拉取请求上多次运行新的自动化测试或对现有测试的编辑是一个很好的做法。&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我想我们都同意：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;酥脆的饼皮 = 好&lt;&#x2F;li&gt;
&lt;li&gt;不稳定的测试 = 坏&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2024&#x2F;06&#x2F;Flakey-Test---BAD.png&quot; alt=&quot;图片 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;a-few-thoughts-on-flakey-tests-playwright-solutions&#x2F;&quot;&gt;原文地址&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间：2024-06-10&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在 Playwright 中创建动态定位器(locator)?</title>
        <published>2024-06-09T00:00:00+00:00</published>
        <updated>2024-06-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-do-you-create-dynamic-locators-with-playwright/"/>
        <id>https://playwright.itest.info/blog/how-do-you-create-dynamic-locators-with-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-do-you-create-dynamic-locators-with-playwright/">&lt;p&gt;这是一个在 &lt;a href=&quot;https:&#x2F;&#x2F;aka.ms&#x2F;playwright&#x2F;discord&quot;&gt;Playwright Discord&lt;&#x2F;a&gt; 帮助频道中经常被问到的热门问题。我认为这个问题如此受欢迎的主要原因之一是,没有人想为同一操作反复编写相似的代码，因为每次都只是输入略有不同，写重复的代码会显得比较啰嗦。处理这个问题有很多不同的方法,我将分享我的方法。&lt;&#x2F;p&gt;
&lt;p&gt;我使用的示例网站并不像其他网站那样直观,因为我将从网络请求中获取一个唯一 ID,并将其传递给动态定位器。我测试的网站是 &lt;a href=&quot;https:&#x2F;&#x2F;practicesoftwaretesting.com&#x2F;&quot;&gt;https:&#x2F;&#x2F;practicesoftwaretesting.com&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-29.png&quot; alt=&quot;图片 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在这个网站上,页面从 API 接口获取数据,并填充了一个可以交互的产品网格。我想创建一个动态定位器,允许我传入一个参数,根据传入的内容动态选择不同的元素。下面是&quot;Combination Pliers&quot;项目的 HTML 代码。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;a
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#97d8ea;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;card&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#97d8ea;&quot;&gt;style&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text-decoration&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;none&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;color&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;black&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#97d8ea;&quot;&gt;data-test&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;product-01H8ABYWDYXDAYW9HM37N5FC5F&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#97d8ea;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#&#x2F;product&#x2F;01H8ABYWDYXDAYW9HM37N5FC5F&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#97d8ea;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;card-img-wrapper&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;img &lt;&#x2F;span&gt;&lt;span style=&quot;color:#97d8ea;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;card-img-top&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#97d8ea;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;assets&#x2F;img&#x2F;products&#x2F;pliers01.jpeg&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#97d8ea;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;card-body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;h5 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#97d8ea;&quot;&gt;data-test&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;product-name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#97d8ea;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;card-title&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Combination Pliers&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;h5&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;div &lt;&#x2F;span&gt;&lt;span style=&quot;color:#97d8ea;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;card-footer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;span &lt;&#x2F;span&gt;&lt;span style=&quot;color:#97d8ea;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;float-end text-muted&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;span &lt;&#x2F;span&gt;&lt;span style=&quot;color:#97d8ea;&quot;&gt;data-test&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;product-price&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;$14.15&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这个例子中,我们将使用链接上的 &lt;code&gt;data-test&lt;&#x2F;code&gt; 属性,其值为 &lt;code&gt;product-{unique_id}&lt;&#x2F;code&gt;。我们将构建一个动态定位器,允许我们传入 &lt;code&gt;unique_id&lt;&#x2F;code&gt; 来与我们想要交互的项目进行交互。下面的代码出现在我的 &lt;code&gt;homePage.ts&lt;&#x2F;code&gt; 文件中。我将 &lt;code&gt;productId&lt;&#x2F;code&gt; 设置为一个动态定位器。请看下面的使用示例。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;pages&#x2F;homePage.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;HomePage {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;productId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-test=&amp;quot;product-${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addToCart &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-test=&amp;quot;add-to-cart&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navCart &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-test=&amp;quot;nav-cart&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;#&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private readonly &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{}
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在 spec 文件中,我们需要实际拥有要传入的 &lt;code&gt;unique_id&lt;&#x2F;code&gt;。&lt;code&gt;await homePage.productId(productId).click();&lt;&#x2F;code&gt; 为了获取唯一的 productId,我使用 &lt;code&gt;page.route()&lt;&#x2F;code&gt; 来拦截网络流量并在我的测试中使用这些值。你可以在&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;get-a-response-value-of-an-underlying-network-request-when-running-a-playwright-test&#x2F;&quot;&gt;这里阅读一个简单的例子&lt;&#x2F;a&gt;以了解更多信息。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;checkout.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CheckoutPage&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;HomePage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@pages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getLoginToken &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@datafactory&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productIdRoute &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@fixtures&#x2F;productPageRoute&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Basic UI Checks&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CUSTOMER_01_USERNAME &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CUSTOMER_01_PASSWORD &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productId&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 通过API调用获取登录token
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getLoginToken&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在本地存储中设置登录令牌,使用户保持登录状态
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;addInitScript&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      window.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;localStorage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setItem&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth-token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;productIdRoute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Add to Cart and Checkout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;HomePage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;CheckoutPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;productId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productId&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addToCart&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navCart&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;proceed1&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这个例子中,我用异步方法实现了 &lt;code&gt;page.route()&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;fixtures&#x2F;productPageRoute.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;HomePage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@pages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * 设置一个路由来从API响应中检索产品ID。
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; - Playwright页面对象。
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; - 可选的字符串名称,用于获取特定ID。
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; 产品ID。
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * const page = await browser.newPage();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * const productId = await productIdRoute(page); &#x2F;&#x2F; 获取第二个产品ID
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * const productId = await productIdRoute(page, &amp;quot;Pliers&amp;quot;) &#x2F;&#x2F; 获取名为&amp;quot;Pliers&amp;quot;的产品ID
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;productIdRoute&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productId&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.practicesoftwaretesting.com&#x2F;products?between=price,1,100&amp;amp;page=1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;findIdByName&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 获取列表中的第二个产品
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.data[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;].id;
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;continue&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;HomePage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productId&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;findIdByName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;undefined &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;.data;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;.length; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;].name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;].id;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;undefined&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我构建的路由器会拦截返回 JSON 的网络流量,并返回 &lt;code&gt;id&lt;&#x2F;code&gt;。我添加了一个可选参数 &lt;code&gt;name&lt;&#x2F;code&gt;,如果传入该参数,它将动态遍历请求返回的 json 字符串,如果产品名称完全匹配(包括大小写),则返回该 &lt;code&gt;id&lt;&#x2F;code&gt; 以供后来使用。&lt;&#x2F;p&gt;
&lt;p&gt;通过这种方式,我可以按名称搜索,获取底层的 &lt;code&gt;data-id&lt;&#x2F;code&gt;,然后使用该 &lt;code&gt;data-id&lt;&#x2F;code&gt; 动态定位页面上的元素。这只需几行代码就能提供极大的灵活性。示例代码可以在下面找到。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-practicesoftwaretesting.com&#x2F;pull&#x2F;4&quot;&gt;添加动态定位器和路由以通过名称获取 productId by BMayhew&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读!如果您觉得这篇文章有帮助,请在 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; 上与我联系,或考虑&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;为我买杯咖啡&lt;&#x2F;a&gt;。如果您想接收更多内容直接发送到您的收件箱,请在下方订阅。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何通过CLI仅运行上次Playwright运行中的失败</title>
        <published>2024-06-03T00:00:00+00:00</published>
        <updated>2024-06-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-to-run-failures-only-from-the-last-playwright-run/"/>
        <id>https://playwright.itest.info/blog/how-to-run-failures-only-from-the-last-playwright-run/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-to-run-failures-only-from-the-last-playwright-run/">&lt;h1 id=&quot;ru-he-tong-guo-cli-jin-yun-xing-shang-ci-playwright-yun-xing-zhong-de-shi-bai&quot;&gt;如何通过 CLI 仅运行上次 Playwright 运行中的失败&lt;&#x2F;h1&gt;
&lt;p&gt;Hello Playwright &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;release-notes#version-144&quot;&gt;1.44&lt;&#x2F;a&gt;版本发布！在这个版本中，我们现在可以官方支持仅运行上次 Playwright 运行中的失败测试。&lt;&#x2F;p&gt;
&lt;p&gt;💡TLDR; &lt;code&gt;npx playwright test --last-failed&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;div class=&quot;w-full md:w-1&#x2F;3 mx-auto&quot;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2024&#x2F;06&#x2F;happy.gif&quot; &#x2F;&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;yi-qian-de-jie-jue-fang-an&quot;&gt;以前的解决方案&lt;&#x2F;h2&gt;
&lt;p&gt;去年，我创建了 json 摘要报告器和一个 bash 脚本，可以在下面的文章中找到。这是我为了能够轻松重新运行失败测试而创建的解决方案。我仍然认为我创建的工具具有很大的价值，特别是当我在 CI 中遇到失败时，我可以快速复制我的 json 摘要结果并将其粘贴到我的本地&lt;code&gt;summary.json&lt;&#x2F;code&gt;文件中，然后在本地运行失败的测试进行调试。请参阅下面的文章以了解更多信息。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;whats-the-easiest-way-to-only-re-run-failures-after-the-playwright-test-is-finished&#x2F;&quot;&gt;在 Playwright 测试完成后仅重新运行失败测试的最简单方法是什么？在所有关于重试失败的精彩功能中，我发现自己需要一种简单的方法来重新运行最近一次运行中的所有测试。这与测试重试和断言重试不同，因为那些是在完成重试的情况下进行的。&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dang-qian-de-jie-jue-fang-an&quot;&gt;当前的解决方案&lt;&#x2F;h2&gt;
&lt;p&gt;但真正令人兴奋的是，我现在可以使用这个新的 CLI 命令重新运行我上次运行中的失败测试。首先，您需要确保您的 Playwright 版本至少为 1.44。您可以通过查看&lt;code&gt;package.json&lt;&#x2F;code&gt;来检查。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2024&#x2F;06&#x2F;image-1.png&quot; alt=&quot;Image 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在这个例子中，我创建了一些每次运行时会失败一半的测试来演示这个功能。在运行&lt;code&gt;npx playwright test&lt;&#x2F;code&gt;之后，我看到了这些结果。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2024&#x2F;06&#x2F;image-4.png&quot; alt=&quot;Image 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我现在可以运行命令&lt;code&gt;npx playwright test --last-failed&lt;&#x2F;code&gt;，这将重新运行上次运行中失败的 1 个测试。在下面的情景中，测试失败了，但在“重试”时通过了。这表明&lt;code&gt;--last-failed&lt;&#x2F;code&gt;命令将会列队上次运行中失败状态的所有测试。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2024&#x2F;06&#x2F;image-5.png&quot; alt=&quot;Image 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yin-yong&quot;&gt;引用&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-to-run-failures-only-from-the-last-playwright-run&#x2F;&quot;&gt;原文&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;更新日期: 2024-06-03&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>挖掘playwright的隐藏宝藏: 第 2 部分- Andrey Enin </title>
        <published>2024-05-23T00:00:00+00:00</published>
        <updated>2024-05-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/hidden-gems-of-playwright-part-2-ca3e38a5954a/"/>
        <id>https://playwright.itest.info/blog/hidden-gems-of-playwright-part-2-ca3e38a5954a/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/hidden-gems-of-playwright-part-2-ca3e38a5954a/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;adequatica.medium.com&#x2F;hidden-gems-of-playwright-68fcf8896bcb&quot;&gt;在上一篇文章&lt;&#x2F;a&gt;中,我介绍了一些 Playwright 的实用方法,我把这些方法封装到了我的自动化测试框架里,大大简化了测试的过程。&lt;&#x2F;p&gt;
&lt;p&gt;本文将继续探讨这个强大工具的一些有趣特性:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;[为特定测试用例或者用例集重写全局配置]&lt;&#x2F;li&gt;
&lt;li&gt;[设备模拟]&lt;&#x2F;li&gt;
&lt;li&gt;[离线模式设置]&lt;&#x2F;li&gt;
&lt;li&gt;[断言重试机制]&lt;&#x2F;li&gt;
&lt;li&gt;[等待选择器(已弃用但仍可用) &#x2F; 等待元素]&lt;&#x2F;li&gt;
&lt;li&gt;[GitHub Actions 集成报告]&lt;&#x2F;li&gt;
&lt;li&gt;[仅运行上次失败的测试]&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这些特性的部分示例代码可以在这个&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;adequatica&#x2F;ui-testing&quot;&gt;GitHub 仓库&lt;&#x2F;a&gt;中找到。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wei-te-ding-ce-shi-ji-zhong-xie-quan-ju-pei-zhi&quot;&gt;为特定测试集重写全局配置&lt;&#x2F;h2&gt;
&lt;p&gt;相关文档: &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-use-options&quot;&gt;测试配置选项&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-testoptions&#x2F;&quot;&gt;TestOptions 接口&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 提供了一个&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-configuration&quot;&gt;全局配置文件&lt;&#x2F;a&gt;来管理测试运行的各种选项。然而,某些测试可能需要完全不同的设置,比如测试的 URL、浏览器配置或特定的用户环境(如窗口大小、地理位置等)。&lt;&#x2F;p&gt;
&lt;p&gt;要为单个测试重写全局配置,可以在测试开始处使用&lt;code&gt;[test.use()](https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-test#test-use)&lt;&#x2F;code&gt;方法来设置所需的参数:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt; baseURL: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:3000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;\[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Pixel 7&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;\],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;移动端首页测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;打开页面&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt; });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;she-bei-mo-ni&quot;&gt;设备模拟&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;emulation#devices&quot;&gt;相关文档&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 配置的另一个优势是其设备模拟功能。无需手动设置移动浏览器的 User Agent、视口大小等参数,只需在配置中直接指定目标设备即可(也可以通过上面提到的&lt;code&gt;test.use()&lt;&#x2F;code&gt;方法重写配置):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span&gt;use: {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;\[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;iPhone 14&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;\],
&lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;完整的可模拟设备列表可以
在&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright&#x2F;blob&#x2F;main&#x2F;packages&#x2F;playwright-core&#x2F;src&#x2F;server&#x2F;deviceDescriptorsSource.json&quot;&gt;Playwright 的 GitHub 仓库&lt;&#x2F;a&gt;中找到。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;chi-xian-mo-shi-she-zhi&quot;&gt;离线模式设置&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-browsercontext#browser-context-set-offline&quot;&gt;相关文档&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我在一个测试中使用了这个功能,主要是为了检查应用在网络连接丢失时的行为。通过&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-browsercontext&quot;&gt;BrowserContext&lt;&#x2F;a&gt;可以轻松启用离线模式:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试离线状态&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;打开页面并切换到离线&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;setOffline&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;需要注意的是,这并非完全的离线模式。&lt;&#x2F;strong&gt; 它会停止网络活动(模拟网络离线),但无法测试那些使用&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;EventTarget&#x2F;addEventListener&quot;&gt;addEventListener()方法&lt;&#x2F;a&gt;监听&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Window&#x2F;offline_event&quot;&gt;online&#x2F;offline 事件&lt;&#x2F;a&gt;的应用功能:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果你的应用代码中有这样的监听:
&lt;&#x2F;span&gt;&lt;span&gt;window.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;addEventListener&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;offline&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{});
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 那么使用 browserContext.setOffline(true)将无法触发这个事件
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;duan-yan-zhong-shi-ji-zhi&quot;&gt;断言重试机制&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions#expecttopass&quot;&gt;相关文档&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这是一个非常巧妙的方法,它允许在&lt;code&gt;expect&lt;&#x2F;code&gt;内部&quot;重试&quot;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions&quot;&gt;断言&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 按指定间隔重试,直到请求成功
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;sso-motd-api.web.cern.ch&#x2F;api&#x2F;motd&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toPass&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 重试间隔: 1 秒, 2 秒, 10 秒, 10 秒
&lt;&#x2F;span&gt;&lt;span&gt;    intervals: \[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10000&lt;&#x2F;span&gt;&lt;span&gt;\],
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; toPass 的超时设置不受自定义 expect 超时影响
&lt;&#x2F;span&gt;&lt;span&gt;    timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;60000&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个功能对于测试不稳定的后端 api 接口特别有用。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;还有一个类似但略有不同的&lt;code&gt;[expect.poll](https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions#expectpoll)&lt;&#x2F;code&gt;方法,它在断言中实现了&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;cache-me-out&#x2F;http-polling-and-long-polling-bd3f662a14f#0f5c&quot;&gt;HTTP 轮询&lt;&#x2F;a&gt;的概念。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;deng-dai-xuan-ze-qi-yi-qi-yong-dan-reng-ke-yong-deng-dai-yuan-su&quot;&gt;等待选择器(已弃用但仍可用) &#x2F; 等待元素&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-elementhandle#element-handle-wait-for-selector&quot;&gt;相关文档&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这是另一个用于检查选择器的优秀方法。&lt;&#x2F;p&gt;
&lt;p&gt;有一种观点认为&lt;strong&gt;不应该在&lt;&#x2F;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;pom&quot;&gt;&lt;strong&gt;页面对象模型&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;中包含断言,尽管 Playwright 自身的示例中也这么做了。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*9ItTFE2kQ2BonWtFf7jviA.png&quot; alt=&quot;图 1: 不建议在页面对象中这样做&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆&lt;em&gt;不建议在页面对象中包含断言&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;相反,你可以等待所需的选择器出现,而不使用显式的 assert 或 expect:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 页面工具栏对象
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;Toolbar {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toggleLocator&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Locator;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toggleLocator &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;=toggle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;clickOnToggle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toggleLocator&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 已弃用,推荐使用基于定位器的 locator.waitFor()
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForSelector&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;data-testid=&amp;quot;dropdown-menu&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;需要注意的是,**这个方法已被弃用,**现在推荐使用&lt;code&gt;[waitFor()](https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locator#locator-wait-for)&lt;&#x2F;code&gt;。因此,上面的页面对象代码应该改写为:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 页面工具栏对象
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;CernToolbar {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toggleLocator&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Locator;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dropdownMenu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Locator;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toggleLocator &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;=toggle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dropdownMenu &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dropdown-menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;clickOnToggle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toggleLocator&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dropdownMenu&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitFor&lt;&#x2F;span&gt;&lt;span&gt;({state: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;更多相关内容:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;adequatica.medium.com&#x2F;principles-of-writing-automated-tests-a2b72218264c&quot;&gt;自动化测试编写原则&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;github-actions-ji-cheng-bao-gao&quot;&gt;GitHub Actions 集成报告&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-reporters#github-actions-annotations&quot;&gt;相关文档&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果你正在使用&lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;actions&#x2F;automating-builds-and-tests&#x2F;about-continuous-integration&quot;&gt;GitHub Actions&lt;&#x2F;a&gt;进行 CI&#x2F;CD,那么&lt;code&gt;github&lt;&#x2F;code&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-reporters&quot;&gt;报告器&lt;&#x2F;a&gt;是一个必备的配置选项:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; GitHub Actions CI 中使用&amp;#39;github&amp;#39;,本地运行时使用&amp;#39;list&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;reporter: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;github&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;list&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;文档中提到这个 reporter 有注释功能,但并未详细说明。实际上,这些注释在 PR 代码中可以直接指出 diff 中失败的代码行。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*7YM1yDhsJfqVlalooK7ogQ.png&quot; alt=&quot;图 2: PR中的GitHub报告器注释&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆&lt;em&gt;PR 中的 GitHub 报告器注释示例&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在工作流作业中,&lt;code&gt;github&lt;&#x2F;code&gt;报告器的输出与常规的&lt;code&gt;list&lt;&#x2F;code&gt;报告类似。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*zzuw0WClgGOCxeehXiGAVQ.png&quot; alt=&quot;图 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆&lt;em&gt;作业中的 GitHub 报告器输出&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jin-yun-xing-shang-ci-shi-bai-de-ce-shi&quot;&gt;仅运行上次失败的测试&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-cli#reference&quot;&gt;CLI 文档&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;最新版本(&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;release-notes#version-144&quot;&gt;1.44&lt;&#x2F;a&gt;)引入了一个新的 CLI 选项,允许只运行上一次执行中失败的测试。&lt;&#x2F;p&gt;
&lt;p&gt;这是 Playwright 测试运行器的一个重大改进。在此之前,我们需要编写自定义脚本来重新运行失败的测试,而现在这个功能已经内置了。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*_ClgjN-zvyqGKOggTHMEsw.png&quot; alt=&quot;图 4: last-failed选项只运行失败的测试&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;使用 last-failed 选项只运行失败的测试&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;原文链接: https:&#x2F;&#x2F;adequatica.medium.com&#x2F;hidden-gems-of-playwright-part-2-ca3e38a5954a&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2024 年 5 月 23 日 05:33:13 UTC&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>你会喜欢的 Playwright 面试问题</title>
        <published>2024-05-17T00:00:00+00:00</published>
        <updated>2024-05-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-interview-questions-that-you-are-going-to-love/"/>
        <id>https://playwright.itest.info/blog/playwright-interview-questions-that-you-are-going-to-love/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-interview-questions-that-you-are-going-to-love/">&lt;p&gt;适用于中高级 QA 自动化工程师的 Playwright 面试问题&lt;&#x2F;p&gt;
&lt;p&gt;继我之前关于 Playwright 面试问题的&lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-interview-questions-that-you-are-going-to-hate&#x2F;&quot;&gt;文章&lt;&#x2F;a&gt;之后，这里有 9 个&lt;strong&gt;Playwright 棘手问题&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-xian-shi-deng-dai&quot;&gt;1. 显式等待&lt;&#x2F;h2&gt;
&lt;p&gt;从测试搜索功能的角度来看，你会如何改进下面的代码：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The explicit waits&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-tips-and-tricks-2&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright tips and tricks #2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;scrollIntoViewIfNeeded&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright tips and tricks #2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Search this site&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;  ).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Search this site&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;frameLocator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;iframe[title=&amp;quot;portal-popup&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByPlaceholder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Search posts, tags and authors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  ).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;frameLocator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;iframe[title=&amp;quot;portal-popup&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByPlaceholder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Search posts, tags and authors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Cypress&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;frameLocator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;iframe[title=&amp;quot;portal-popup&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;heading&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Cypress&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;  ).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContainText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Cypress&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;hui-da&quot;&gt;回答&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;移除所有 &lt;code&gt;toBeVisible()&lt;&#x2F;code&gt; 断言&lt;&#x2F;li&gt;
&lt;li&gt;移除 &lt;code&gt;scrollIntoViewIfNeeded()&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;将 iframe 存储在常量中以便重用和提高可读性&lt;&#x2F;li&gt;
&lt;li&gt;在定位器中使用正则表达式，以便使用部分文本，例如 &lt;code&gt;&#x2F;Search posts&#x2F;&lt;&#x2F;code&gt; 而不是 &lt;code&gt;Search posts, tags and authors&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;查看代码和解释请见页面末尾&lt;&#x2F;strong&gt; &lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-interview-questions-that-you-are-going-to-love&#x2F;#content-below-is-visible-to-members-only&quot;&gt;&lt;strong&gt;链接...&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;乙醇的解释 👀: 还是给个省流版本吧，我没看作者的解释，我按照自己的理解来编的 😳&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;移除所有 &lt;code&gt;toBeVisible()&lt;&#x2F;code&gt; 断言: 因为这些断言没必要，如果你要操作 1 个元素的话，没必要断言这个元素&lt;code&gt;toBeVisible&lt;&#x2F;code&gt;，因为 playwright 会自动帮你做这件事情&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;移除 &lt;code&gt;scrollIntoViewIfNeeded()&lt;&#x2F;code&gt;: 理由同上&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;将 iframe 存储在常量中以便重用和提高可读性: 个人喜好问题，但是有道理&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;在定位器中使用正则表达式，以便使用部分文本，例如 &lt;code&gt;&#x2F;Search posts&#x2F;&lt;&#x2F;code&gt; 而不是 &lt;code&gt;Search posts, tags and authors&lt;&#x2F;code&gt;: 完全同意，尽量避免精准匹配的断言和定位器，当然了，playwright 默认情况下&lt;code&gt;getByXXX&lt;&#x2F;code&gt;中如果出现文本，都是模糊匹配的。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-ke-jian-de-fang-fa&quot;&gt;2. 可见的方法&lt;&#x2F;h2&gt;
&lt;p&gt;这段代码将会做什么：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The visible methods&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;blog.martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;About&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isVisible&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可能的答案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;测试将失败，因为 isVisible() 不是一个有效的方法&lt;&#x2F;li&gt;
&lt;li&gt;测试将失败，并显示关于属性 &#x27;then&#x27; 的错误&lt;&#x2F;li&gt;
&lt;li&gt;测试将通过&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;hui-da-1&quot;&gt;回答&lt;&#x2F;h4&gt;
&lt;p&gt;测试将失败，并显示错误：Error: expect: Property &#x27;then&#x27; not found&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;查看解释请见页面末尾&lt;&#x2F;strong&gt; &lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-interview-questions-that-you-are-going-to-love&#x2F;#content-below-is-visible-to-members-only&quot;&gt;&lt;strong&gt;链接...&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;省流版本&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;错误原因是 &lt;code&gt;isVisible()&lt;&#x2F;code&gt; 方法返回的是一个 &lt;code&gt;Promise&lt;&#x2F;code&gt; 对象，但 &lt;code&gt;expect&lt;&#x2F;code&gt; 断言方法不能直接处理 &lt;code&gt;Promise&lt;&#x2F;code&gt; 对象。需要使用 &lt;code&gt;await&lt;&#x2F;code&gt; 关键字来等待 &lt;code&gt;Promise&lt;&#x2F;code&gt; 对象的解析。&lt;&#x2F;p&gt;
&lt;p&gt;正确的代码应该是：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The visible methods&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;blog.martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isVisible &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;About&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isVisible&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这样修改后，&lt;code&gt;isVisible()&lt;&#x2F;code&gt; 方法的返回结果会在被 &lt;code&gt;expect&lt;&#x2F;code&gt; 断言方法处理之前被正确解析。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;乙醇的注释 👀：最好用 playwright 的 web 断言 await expect(locator).toBeVisible()&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-ren-zhe-dian-ji&quot;&gt;3. 忍者点击&lt;&#x2F;h2&gt;
&lt;p&gt;以下代码将会发生什么：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The ninja&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.clickspeedtester.com&#x2F;mouse-test&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Second Clicker&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;({ trial: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;**&#x2F;clicks-per-second-test&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可能的答案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;测试将失败，错误为 page.waitForURL: Test ended，因为点击操作未执行&lt;&#x2F;li&gt;
&lt;li&gt;测试将失败，因为 &lt;code&gt;waitForURL()&lt;&#x2F;code&gt; 参数格式无效&lt;&#x2F;li&gt;
&lt;li&gt;测试将在点击步骤失败，没有 &lt;code&gt;trial:true&lt;&#x2F;code&gt; 这样的选项&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;hui-da-2&quot;&gt;回答&lt;&#x2F;h4&gt;
&lt;p&gt;测试将失败，错误为 page.waitForURL: Test ended。使用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page?ref=blog.martioli.com#page-click-option-trial&quot;&gt;trial:true&lt;&#x2F;a&gt; Playwright 只执行&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;actionability?ref=blog.martioli.com&quot;&gt;是否可以被点击的&lt;&#x2F;a&gt;检查，但跳过点击操作。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-ni-huan-hao-ma&quot;&gt;4. 你还好吗？&lt;&#x2F;h2&gt;
&lt;p&gt;以下代码将会发生什么：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The you OK&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;blog.martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeOK&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可能的答案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;测试将失败，因为没有 &lt;code&gt;toBeOK()&lt;&#x2F;code&gt; 这个方法&lt;&#x2F;li&gt;
&lt;li&gt;测试将失败，因为 &lt;code&gt;page&lt;&#x2F;code&gt; 没有 &lt;code&gt;request&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;测试将通过&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;hui-da-3&quot;&gt;回答&lt;&#x2F;h4&gt;
&lt;p&gt;测试将通过（前提是网站没挂的话）。&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-apiresponseassertions?ref=blog.martioli.com#api-response-assertions-to-be-ok&quot;&gt;toBeOK()&lt;&#x2F;a&gt; 是一个确保响应状态码在 &lt;code&gt;200..299&lt;&#x2F;code&gt; 范围内的方法。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-te-shu-ci&quot;&gt;5. 特殊词&lt;&#x2F;h2&gt;
&lt;p&gt;假设元素包含文本&quot;Be the first to discover new tips and tricks about automation in software development&quot;，以下代码将会发生什么：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The innerText?&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;blog.martioli.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;innertText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.gh-subscribe-description&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;innerText&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;innertText&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Be the first to discover new tips&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可能的答案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;测试将通过&lt;&#x2F;li&gt;
&lt;li&gt;测试将失败，错误为 Error: expect Received object: {}&lt;&#x2F;li&gt;
&lt;li&gt;测试将失败，因为我们不能在 &lt;code&gt;innerText()&lt;&#x2F;code&gt; 上使用 &lt;code&gt;toContain()&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;hui-da-4&quot;&gt;回答&lt;&#x2F;h4&gt;
&lt;p&gt;测试将失败，错误为 Error: expect Received object: {}。因为我们忘记在 &lt;code&gt;innerText()&lt;&#x2F;code&gt; 方法前加上 &lt;code&gt;await&lt;&#x2F;code&gt; 关键字，以解析 Promise 并提取文本。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;6-shen-qi-de-guo-lu-qi&quot;&gt;6. 神奇的过滤器&lt;&#x2F;h2&gt;
&lt;p&gt;过滤测试用例的最佳推荐方法是什么？&lt;&#x2F;p&gt;
&lt;h4 id=&quot;hui-da-5&quot;&gt;回答&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-annotations?ref=blog.martioli.com#tag-tests&quot;&gt;tags&lt;&#x2F;a&gt;是过滤测试最简单和最有效的方法。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;7-shi-bai-de-yi-ci&quot;&gt;7. 失败的一次&lt;&#x2F;h2&gt;
&lt;p&gt;以下代码将会发生什么：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The fail&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fail&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Astronaut&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可能的答案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;测试将通过，因为应用了 &lt;code&gt;test.fail()&lt;&#x2F;code&gt; 方法&lt;&#x2F;li&gt;
&lt;li&gt;测试将失败，因为应用了 &lt;code&gt;test.fail()&lt;&#x2F;code&gt; 方法&lt;&#x2F;li&gt;
&lt;li&gt;测试将执行所有步骤，但结果仍然失败&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;hui-da-6&quot;&gt;回答&lt;&#x2F;h4&gt;
&lt;p&gt;测试将通过，因为应用了 &lt;code&gt;test.fail()&lt;&#x2F;code&gt; 方法&lt;&#x2F;p&gt;
&lt;p&gt;为什么？因为它在我的&lt;a href=&quot;https:&#x2F;&#x2F;martioli.com&#x2F;?ref=blog.martioli.com&quot;&gt;网站&lt;&#x2F;a&gt;上找不到 &quot;Astronaut&quot; 这个词，因为找不到它，我们期望的测试整体失败，用例确实会失败，所以整体而言用例&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-test?ref=blog.martioli.com#test-fail&quot;&gt;将通过&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;8-jian-kang-jian-cha&quot;&gt;8. 健康检查&lt;&#x2F;h2&gt;
&lt;p&gt;以下代码将会发生什么？你如何改进代码：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locales &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;de&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;es&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;location &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;of &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locales&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;check health: ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.google.${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeOK&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可能的答案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;测试将通过&lt;&#x2F;li&gt;
&lt;li&gt;测试将失败，因为不能进行这样的 &lt;code&gt;for&lt;&#x2F;code&gt; 循环&lt;&#x2F;li&gt;
&lt;li&gt;测试将失败，因为 &lt;code&gt;expect&lt;&#x2F;code&gt; 没有 &lt;code&gt;await&lt;&#x2F;code&gt; 关键字&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;hui-da-7&quot;&gt;回答&lt;&#x2F;h4&gt;
&lt;p&gt;测试将通过。你可以在 Playwright 中进行这样的检查，只需注意在测试中加入一些延迟。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;如果你想知道为什么 &lt;code&gt;expect&lt;&#x2F;code&gt; 不需要 &lt;code&gt;await&lt;&#x2F;code&gt; 关键字，请见页面末尾的解释&lt;&#x2F;strong&gt; &lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-interview-questions-that-you-are-going-to-love&#x2F;#content-below-is-visible-to-members-only&quot;&gt;&lt;strong&gt;链接...&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;省流版本: 在 Playwright 中，expect 断言方法不需要 await 关键字的原因是 Playwright 的 expect 方法是同步的，不返回 Promise 对象。它会立即检查传入的值或对象的状态，并且如果检查失败，它会立即抛出一个错误&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;9-ye-mian-yi&quot;&gt;9. 页面一&lt;&#x2F;h2&gt;
&lt;p&gt;以下代码将会发生什么：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The page one&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;blog.martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Recommended Resources&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可能的答案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;测试将通过，因为我们有 &lt;code&gt;Recommended Resources&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;测试将失败，因为引用错误&lt;&#x2F;li&gt;
&lt;li&gt;测试将失败，因为我的博客中没有 &lt;code&gt;Recommended Resources&lt;&#x2F;code&gt; 文本&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;hui-da-8&quot;&gt;回答&lt;&#x2F;h4&gt;
&lt;p&gt;测试将失败，错误为 ReferenceError: getByRole is not defined。注意我们用了 &lt;code&gt;expect(getByText&lt;&#x2F;code&gt; 而不是 &lt;code&gt;expect(page.getByText.&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;如果你觉得这篇文章有用，请点击点赞按钮。或者&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;adrianmaciuc?ref=blog.martioli.com&quot;&gt;请我喝杯咖啡&lt;&#x2F;a&gt;，以&lt;&#x2F;p&gt;
&lt;p&gt;更好地激励我。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;来源网址：https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-interview-questions-that-you-are-going-to-love&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间：2024-05-17T07:08:35.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>playwright中如何参数化项目配置</title>
        <published>2024-05-09T00:00:00+00:00</published>
        <updated>2024-05-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-to-parameterize-projects-in-playwright/"/>
        <id>https://playwright.itest.info/blog/how-to-parameterize-projects-in-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-to-parameterize-projects-in-playwright/">&lt;p&gt;引言&lt;&#x2F;p&gt;
&lt;p&gt;今天,我在公司的 Playwright 项目中遇到了一个有趣的挑战,我想记录下我的发现。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;任务是同时运行多个项目,每个项目使用不同的数据集。&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;幸运的是,Playwright 提供了一个功能来解决这个需求 — 声明&lt;strong&gt;TestOptions&lt;&#x2F;strong&gt;的能力。&lt;&#x2F;p&gt;
&lt;p&gt;在本文中,我将指导您如何利用 Playwright 的 TestOptions 声明功能来高效地管理和同时执行多个项目。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;在我的 GitHub 仓库上探索演示项目的代码片段:&lt;&#x2F;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nora-weisser&#x2F;playwright_demo&quot;&gt;&lt;&#x2F;a&gt;&lt;em&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nora-weisser&#x2F;playwright_demo&quot;&gt;github.com&#x2F;nora-weisser&#x2F;playwright_demo&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;bu-zou-1-an-zhuang-xiang-mu&quot;&gt;&lt;strong&gt;步骤 1&lt;&#x2F;strong&gt;: 安装项目&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;npm init playwright@latest&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 将下载所有需要的浏览器并创建以下项目结构:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;playwright.config.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;package.json
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;package-lock.json
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tests&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;example.spec.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test-examples&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test.examples.ts
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;bu-zou-2-tian-jia-playwright-xiang-mu-pei-zhi&quot;&gt;&lt;strong&gt;步骤 2&lt;&#x2F;strong&gt;. 添加 Playwright 项目配置&lt;&#x2F;h2&gt;
&lt;p&gt;打开 &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; 文件。这个文件是 Playwright 配置的中心,允许您设置 Playwright 测试的首选浏览器等设置。在配置中,找到 &#x27;use&#x27; 部分并设置基础 URL。在这个演示中,我选择了 &#x27;&lt;a href=&quot;https:&#x2F;&#x2F;www.saucedemo.com&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.saucedemo.com&#x2F;&lt;&#x2F;a&gt;&#x27; 作为演示用的基础 URL。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span&gt;use: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* 在像 `await page.goto(&amp;#39;&#x2F;&amp;#39;)` 这样的操作中使用的基础 URL。 *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    baseURL: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.saucedemo.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* 在重试失败的测试时收集跟踪。查看 https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;trace-viewer *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    trace: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;on-first-retry&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt; },
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;bu-zou-3-liao-jie-playwright-xiang-mu&quot;&gt;&lt;strong&gt;步骤 3&lt;&#x2F;strong&gt;. 了解 Playwright 项目&lt;&#x2F;h2&gt;
&lt;p&gt;在文件的下方,您会遇到各种项目。我在下面引用了 Playwright 文档中的定义供您参考:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;项目是使用相同配置运行的测试的逻辑组。我们使用项目来在不同的浏览器和设备上运行测试。项目在 playwright.config.ts 文件中配置,配置完成后,您可以在所有项目上运行测试,或仅在特定项目上运行。您还可以使用项目在不同的配置下运行相同的测试。例如,您可以在登录和未登录状态下运行相同的测试。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;bu-zou-4-gai-shu-te-ding-ce-shi-chang-jing&quot;&gt;&lt;strong&gt;步骤 4&lt;&#x2F;strong&gt;. 概述特定测试场景&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;测试用例&lt;&#x2F;strong&gt;: 实现登录测试用例并使用各种数据集执行它&lt;&#x2F;p&gt;
&lt;p&gt;参数化功能在这里起着关键作用,它允许配置每个项目用不同的用户名&#x2F;密码进行测试。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bu-zou-5-kao-lu-ce-shi-shu-ju&quot;&gt;&lt;strong&gt;步骤 5&lt;&#x2F;strong&gt;. 考虑测试数据&lt;&#x2F;h2&gt;
&lt;p&gt;测试数据长这个样子 👇&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:1400&#x2F;1*2RzF1FFuHfqIEiAmNRNJ7Q.png&quot; alt=&quot;图片 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bu-zou-6-chuang-jian-ce-shi-shu-ju&quot;&gt;&lt;strong&gt;步骤 6&lt;&#x2F;strong&gt;. 创建测试数据&lt;&#x2F;h2&gt;
&lt;p&gt;建立一个 &#x27;test_data&#x27; 文件夹,并在其中创建 &#x27;login.data.ts&#x27;。首先声明一个包含必要属性(用户名和密码)及其数据类型的接口。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;interface &lt;&#x2F;span&gt;&lt;span&gt;USER_DATA {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;引入变量 USERS 并列出所有的测试数据。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;USERS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;{ [&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;USER_DATA } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  standard_user: {
&lt;&#x2F;span&gt;&lt;span&gt;    username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;standard_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;secret_sauce&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  locked_out_user: {
&lt;&#x2F;span&gt;&lt;span&gt;    username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;locked_out_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;secret_sauce&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  problem_user: {
&lt;&#x2F;span&gt;&lt;span&gt;    username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;problem_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;secret_sauce&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  performance_glitch_user: {
&lt;&#x2F;span&gt;&lt;span&gt;    username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;performance_glitch_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;secret_sauce&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  error_user: {
&lt;&#x2F;span&gt;&lt;span&gt;    username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;error_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;secret_sauce&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  visual_user: {
&lt;&#x2F;span&gt;&lt;span&gt;    username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visual_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;secret_sauce&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;bu-zou-7-kuo-zhan-testoptions&quot;&gt;&lt;strong&gt;步骤 7&lt;&#x2F;strong&gt;. 扩展 TestOptions&lt;&#x2F;h2&gt;
&lt;p&gt;为了将 &#x27;user&#x27; 参数整合到测试用例和项目中,必须声明 TestOptions &#x27;targetUser&#x27;。创建一个 &#x27;helper&#x27; 文件夹,并在其中建立 &#x27;test-option.ts&#x27;。&lt;&#x2F;p&gt;
&lt;p&gt;通过引入新的 TestOptions &#x27;targetUser&#x27; 来扩展现有的 &#x27;TestOptions&#x27;,以帮助用户参数的集成。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;USER_DATA &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;test_data&#x2F;login.data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;USERS &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;test_data&#x2F;login.data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;interface &lt;&#x2F;span&gt;&lt;span&gt;TestOptions {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;targetUser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;USER_DATA;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;extend &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;TestOptions &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  {
&lt;&#x2F;span&gt;&lt;span&gt;    targetUser: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;USERS&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;standard_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], { option: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;}],
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;bu-zou-8-zai-ce-shi-yong-li-zhong-shi-yong-testoptions&quot;&gt;&lt;strong&gt;步骤 8&lt;&#x2F;strong&gt;. 在测试用例中使用 TestOptions&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;helpers&#x2F;test-options&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用现有用户名和有效密码登录&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;targetUser &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;await page.goto(&amp;#39;https:&#x2F;&#x2F;www.saucedemo.com&#x2F;&amp;#39;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-test=&amp;quot;username&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-test=&amp;quot;username&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;targetUser&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-test=&amp;quot;password&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-test=&amp;quot;password&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;targetUser&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-test=&amp;quot;login-button&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;currentURL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;currentURL&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.saucedemo.com&#x2F;inventory.html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#header_container&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContainText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Swag Labs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;重要提示: 使用以下路径 &#x27;..&#x2F;helpers&#x2F;test-options&#x27; 从我们创建的辅助函数中导入 &#x27;test&#x27; 函数。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bu-zou-9-geng-xin-xiang-mu-pei-zhi&quot;&gt;&lt;strong&gt;步骤 9.&lt;&#x2F;strong&gt; 更新项目配置&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import type &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;TestOptions &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;helpers&#x2F;test-options&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;USERS &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;test_data&#x2F;login.data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;TestOptions&amp;gt;({
&lt;&#x2F;span&gt;&lt;span&gt;projects: [
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;standard_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      use: { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Desktop Chrome&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;], targetUser: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;USERS&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;standard_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] },
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;locked_out_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      use: { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Desktop Chrome&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;], targetUser: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;USERS&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;locked_out_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] },
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;problem_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      use: { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Desktop Chrome&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;], targetUser: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;USERS&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;problem_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] },
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;performance_glitch_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      use: { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Desktop Chrome&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;], targetUser: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;USERS&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;performance_glitch_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] },
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;error_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      use: { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Desktop Chrome&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;], targetUser: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;USERS&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;error_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] },
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visual_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      use: { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Desktop Chrome&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;], targetUser: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;USERS&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visual_user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] },
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;bu-zou-10-zhi-xing-suo-you-xiang-mu-bing-cha-kan-bao-gao&quot;&gt;&lt;strong&gt;步骤 10&lt;&#x2F;strong&gt;. 执行所有项目并查看报告&lt;&#x2F;h2&gt;
&lt;p&gt;运行命令: &lt;code&gt;npx playwright test&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;默认情况下,Playwright 生成包含测试执行信息、单个测试成功或失败以及测试运行期间遇到的任何错误的测试报告。reporter 的目的是提供测试结果的可见性,使开发人员或测试人员更容易理解自动化测试的结果。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:1400&#x2F;1*FKtwbhYN6xoz_QcxbcVHIw.png&quot; alt=&quot;图片 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 报告视图 👆&lt;&#x2F;p&gt;
&lt;p&gt;在这个特定的实例中,列出了执行的测试用例以及已处理的用户信息。我故意包含了一个失败的测试用例 &#x27;locked-out-user&#x27;,以展示登录失败的场景。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:1400&#x2F;1*eIUhJD0Ysc6hofVN8wL2FA.png&quot; alt=&quot;图片 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;失败测试用例的附加信息 👆&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bu-zou-11-zhi-xing-te-ding-xiang-mu&quot;&gt;&lt;strong&gt;步骤 11&lt;&#x2F;strong&gt;. 执行特定项目&lt;&#x2F;h2&gt;
&lt;p&gt;使用以下命令: &lt;code&gt;npx playwright test -project=&amp;lt;项目名称&amp;gt;&lt;&#x2F;code&gt;。此命令针对指定项目的预定义数据集执行登录测试用例。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-lun&quot;&gt;结论&lt;&#x2F;h2&gt;
&lt;p&gt;在本文中,我分享了关于 Playwright 项目中参数化的发现,涵盖了从创建测试数据到更新配置和实现 TestOptions 的各个方面。通过利用这个功能,我们可以提高用例的可维护性,扩大测试覆盖范围,并提高定位问题的效率。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;资源&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;测试参数化&lt;&#x2F;em&gt;: &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-parameterize&quot;&gt;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-parameterize&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;固定装置&lt;&#x2F;em&gt;: &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-fixtures&quot;&gt;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-fixtures&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;演示项目仓库&lt;&#x2F;em&gt;: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nora-weisser&#x2F;playwright_demo&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;nora-weisser&#x2F;playwright_demo&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>你会讨厌的 Playwright 面试题</title>
        <published>2024-05-05T00:00:00+00:00</published>
        <updated>2024-05-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-interview-questions-that-you-are-going-to-hate/"/>
        <id>https://playwright.itest.info/blog/playwright-interview-questions-that-you-are-going-to-hate/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-interview-questions-that-you-are-going-to-hate/">&lt;p&gt;这些问题是设计来考你的，所以如果你答不对，不要太苛责自己。此外，一些答案有解释，甚至还有代码示例。滚动到页面底部查看详细内容。记住，这些都是可以运行的代码，使用了真实的 URL，所以你可以尝试复制粘贴这些代码看看效果。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-ben-di-huan-jing&quot;&gt;1. 本地环境&lt;&#x2F;h2&gt;
&lt;p&gt;如果你要在本地机器上使用你设置好的本地环境运行测试，比如在 &lt;a href=&quot;http:&#x2F;&#x2F;localhost:3000&#x2F;?ref=blog.martioli.com&quot;&gt;http:&#x2F;&#x2F;localhost:3000&#x2F;&lt;&#x2F;a&gt; ，你会如何处理测试运行期间的本地环境设置？&lt;&#x2F;p&gt;
&lt;p&gt;可能的答案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;在运行测试之前启动你的本地环境&lt;&#x2F;li&gt;
&lt;li&gt;在 package.json 中编写脚本来启动本地环境并运行测试&lt;&#x2F;li&gt;
&lt;li&gt;使用 webServer&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;da-an&quot;&gt;答案&lt;&#x2F;h4&gt;
&lt;p&gt;所有可能的答案都是有效的，但推荐使用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-testconfig?ref=blog.martioli.com#test-config-web-server&quot;&gt;webServer&lt;&#x2F;a&gt; 配置&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-interview-questions-that-you-are-going-to-hate&#x2F;#content-below-is-visible-to-members-only&quot;&gt;&lt;strong&gt;查看解释&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-bao-han-bi-jiao&quot;&gt;2. 包含比较&lt;&#x2F;h2&gt;
&lt;p&gt;除了一个接受 &lt;code&gt;locator&lt;&#x2F;code&gt; 而另一个接受 &lt;code&gt;value&lt;&#x2F;code&gt; 之外，以下两者之间的主要区别是什么：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;await expect(locator).toContainText()&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;和&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;await expect(value).toContain()&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;da-an-1&quot;&gt;答案&lt;&#x2F;h4&gt;
&lt;p&gt;第一个是一个 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions?ref=blog.martioli.com#auto-retrying-assertions&quot;&gt;自动重试&lt;&#x2F;a&gt; 断言，意味着它会在 5 秒内重试，直到元素出现，而第二个只会尝试断言一次。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-yan-chi-jia-zai&quot;&gt;3. 延迟加载&lt;&#x2F;h2&gt;
&lt;p&gt;在以下 URL 中，文本“加载完成”将在 10 秒后出现。我们的测试在 &lt;code&gt;expect&lt;&#x2F;code&gt; 步骤中失败，错误为 &lt;code&gt;Expected: visible , Received: hidden&lt;&#x2F;code&gt;。你如何修复这个测试？&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The visible methods&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;webdriveruniversity.com&#x2F;Accordion&#x2F;index.html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;LOADING COMPLETE.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;da-an-2&quot;&gt;答案&lt;&#x2F;h4&gt;
&lt;p&gt;默认的 &lt;code&gt;expect&lt;&#x2F;code&gt; 超时时间为 5 秒，如果元素在此时间内不可见，测试将失败。为修复此测试，我们将仅在此步骤中延长超时时间，超过已知的 10 秒延迟，如下所示：&lt;code&gt;await expect(page.getByText(&quot;LOADING COMPLETE.&quot;)).toBeVisible({ timeout: 12000 })&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-zi-yu-self-healing&quot;&gt;4. 自愈(self-healing)&lt;&#x2F;h2&gt;
&lt;p&gt;知道 magento.softwaretestingboard.com 有一个名为“Sale”的菜单项，点击后会导航到新页面。完成以下测试，编写一个新的步骤来点击“Sale”菜单选项，但为该元素创建一个 &lt;strong&gt;自愈定位器&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The self-healing&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;magento.softwaretestingboard.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Consent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { exact: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;da-an-3&quot;&gt;答案&lt;&#x2F;h4&gt;
&lt;p&gt;自愈定位器是当你将多个定位器指向同一个元素时，你使用帮助方法或 OR 操作符将它们链接在一起。使用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locator?#locator-or&quot;&gt;or 操作符&lt;&#x2F;a&gt; 可以提供多个定位器指向同一个元素，如果由于应用程序更改其中一个失效，它可以尝试使用另一个&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-interview-questions-that-you-are-going-to-hate&#x2F;#content-below-is-visible-to-members-only&quot;&gt;&lt;strong&gt;查看代码&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;来自乙醇的解释 👀：作者的示例代码是要会员才能访问的，不过没关系，我从官网找了个一样的例子，可以得到同样的效果，出于对原创的尊重，我把作者的原始链接也放在这里&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators#matching-one-of-the-two-alternative-locators&quot; alt=&quot;Matching one of the two alternative locators&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newEmail &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;New&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dialog &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Confirm security settings&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newEmail&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;or&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dialog&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dialog&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isVisible&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Dismiss&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newEmail&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-yi-guan-bi-de-liu-lan-qi&quot;&gt;5. 已关闭的浏览器&lt;&#x2F;h2&gt;
&lt;p&gt;这段代码会做什么：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The closed browser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;blog.martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;About&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;about&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可能的答案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;测试会失败，因为 About 是一个按钮，而不是链接&lt;&#x2F;li&gt;
&lt;li&gt;测试会报错，关于目标页面&lt;&#x2F;li&gt;
&lt;li&gt;测试会失败，因为传递给 .toHaveURL() 的参数&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;da-an-4&quot;&gt;答案&lt;&#x2F;h4&gt;
&lt;p&gt;测试会报错，关于目标页面。错误：&lt;code&gt;expect.toHaveURL: 目标页面、上下文或浏览器已关闭&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-interview-questions-that-you-are-going-to-hate&#x2F;#content-below-is-visible-to-members-only&quot;&gt;&lt;strong&gt;查看解释&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;乙醇的解释 👀：上面的示例也是要开会员的，我来给个省流的版本&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这是因为 &lt;code&gt;toHaveURL&lt;&#x2F;code&gt; 是一个异步操作，需要等待（&lt;code&gt;await&lt;&#x2F;code&gt;）其完成。因此，正确的代码应如下：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The closed browser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;blog.martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;About&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;about&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这里添加了 await
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;6-yin-yong&quot;&gt;6. 引用&lt;&#x2F;h2&gt;
&lt;p&gt;知道在第二步中点击的元素是页面上的一个反向链接，这段代码会做什么：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The reference&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-tips-and-tricks-1&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;section&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;buy me a coffee&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;blog&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;martioli&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可能的答案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;测试会失败，因为“buy me a coffee”是一个链接，我们已经离开了 martioli.com&lt;&#x2F;li&gt;
&lt;li&gt;测试会通过&lt;&#x2F;li&gt;
&lt;li&gt;测试会失败，因为你不能混合使用 locator().locator().locator()&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;da-an-5&quot;&gt;答案&lt;&#x2F;h4&gt;
&lt;p&gt;测试会通过，因为点击一个 &lt;a href=&quot;https:&#x2F;&#x2F;backlinko.com&#x2F;hub&#x2F;seo&#x2F;backlinks?ref=blog.martioli.com&quot;&gt;反向链接&lt;&#x2F;a&gt; 会生成一个包含我们初始来源（blog.martioli.com）reference 的 URL&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-interview-questions-that-you-are-going-to-hate&#x2F;#content-below-is-visible-to-members-only&quot;&gt;&lt;strong&gt;查看解释&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;乙醇的解释 👀：我不懂，反正我没在真实的项目中遇到过这种情况&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;7-qi-ta-yu-yan-huan-jing&quot;&gt;7. 其他语言环境&lt;&#x2F;h2&gt;
&lt;p&gt;修改以下测试并使其在不同的语言环境下运行，例如德语。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The locale&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.google.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;da-an-6&quot;&gt;答案&lt;&#x2F;h4&gt;
&lt;p&gt;你可以在用例里直接修改。 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;emulation#locale--timezone&quot;&gt;文档&lt;&#x2F;a&gt;。只需在测试前添加 &lt;code&gt;test.use({locale: &#x27;de-DE&#x27;})&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;8-css-shu-xing&quot;&gt;8. CSS 属性&lt;&#x2F;h2&gt;
&lt;p&gt;给定以下代码，如何提高其可读性和简洁性。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The css properties&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;magento.softwaretestingboard.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Consent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { exact: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;element &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Shop New Yoga&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;backgroundColor &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;element&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;el&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;window.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getComputedStyle&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;el&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getPropertyValue&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;background-color&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;backgroundColor&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;rgb(25, 121, 195)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;da-an-7&quot;&gt;答案&lt;&#x2F;h4&gt;
&lt;p&gt;当你已经有内置方法 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locatorassertions#locator-assertions-to-have-css&quot;&gt;toHaveCSS()&lt;&#x2F;a&gt; 时，不需要调用 evaluate。记住 &lt;code&gt;toHaveCSS()&lt;&#x2F;code&gt; 具有自动重试功能，这在你等待某些元素属性延迟出现时至关重要。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-interview-questions-that-you-are-going-to-hate&#x2F;#content-below-is-visible-to-members-only&quot;&gt;&lt;strong&gt;查看代码&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;乙醇的解释 👀，参考&lt;a href=&quot;&#x2F;blog&#x2F;what-the-hex-or-how-i-check-colors-with-playwright&#x2F;&quot;&gt;这篇文章&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Shop New Yoga&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveCSS&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;background-color&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;rgb(25, 121, 195)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;9-chang-an-dian-ji&quot;&gt;9. 长按点击&lt;&#x2F;h2&gt;
&lt;p&gt;给定以下代码，如何修改点击步骤来模拟按住左键 3 秒然后松开鼠标键。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The long click&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.clickspeedtester.com&#x2F;mouse-test&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Second Clicker&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;da-an-8&quot;&gt;答案&lt;&#x2F;h4&gt;
&lt;p&gt;Click 可以选择性地接受一个带有 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-mouse?ref=blog.martioli.com#mouse-click&quot;&gt;delay&lt;&#x2F;a&gt; 属性的对象，值以毫秒为单位。像这样：&lt;code&gt;.click({delay: 3000})&lt;&#x2F;code&gt; 配合一个元素，它将在该元素上按住 3 秒钟，然后释放鼠标键&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;10-qiang-zhi-dian-ji&quot;&gt;10. 强制点击&lt;&#x2F;h2&gt;
&lt;p&gt;页面上有一个数据同意弹出框，你必须同意才能继续。搜索按钮隐藏在这个弹出框后面。给定以下代码。会发生什么？&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The force&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.google.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 搜索输入字段
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textarea&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;martioli&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 搜索按钮
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[type=submit][name=&amp;quot;btnK&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;last&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;({ force: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可能的答案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;测试会通过。我使用了 &lt;code&gt;{force:true}&lt;&#x2F;code&gt; ，它会点击按钮，结果会显示在弹出框后面&lt;&#x2F;li&gt;
&lt;li&gt;测试会通过，但没有结果显示在弹出框后面&lt;&#x2F;li&gt;
&lt;li&gt;测试会失败，超时，因为无法点击隐藏在弹出框后的按钮&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;da-an-9&quot;&gt;答案&lt;&#x2F;h4&gt;
&lt;p&gt;测试会通过，但没有结果显示在弹出框后面。&lt;code&gt;force:true&lt;&#x2F;code&gt; 在元素被另一个元素覆盖时不会点击，但也不会报错（playwright 中的一个 bug？）。这与 &lt;code&gt;fill()&lt;&#x2F;code&gt; 不同。如果你检查输入字段，它在输入时没有问题。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;11-base-url&quot;&gt;11. Base URL&lt;&#x2F;h2&gt;
&lt;p&gt;我在配置文件中设置了 baseUrl。这段代码会做什么：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The baseurl&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可能的答案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;测试会失败。我忘记了引号之间的 &lt;code&gt;&#x2F;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;测试会失败，因为 &lt;code&gt;.goto()&lt;&#x2F;code&gt; 需要双引号&lt;&#x2F;li&gt;
&lt;li&gt;测试会通过&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;da-an-10&quot;&gt;答案&lt;&#x2F;h4&gt;
&lt;p&gt;测试会通过，空字符串与 &quot;&#x2F;&quot; 效果相同。它会导航到在 playwright.config.js 中配置的 baseUrl&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;12-workders&quot;&gt;12. workders&lt;&#x2F;h2&gt;
&lt;p&gt;给定以下关于 workers 的配置。如果我运行一个包含 100 个测试的套件，它们会如何运行：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;exports &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  testDir: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;tests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  workers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;5%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  projects: [
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;chromium&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      use: { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Desktop Chrome&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] },
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可能的答案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;测试会运行得非常慢，因为只有 5% 的 workers 工作量&lt;&#x2F;li&gt;
&lt;li&gt;测试会在你拥有的 CPU 核心数的 5% 上运行&lt;&#x2F;li&gt;
&lt;li&gt;测试不会开始，因为你不能以百分比设置 workers 数量&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;da-an-11&quot;&gt;答案&lt;&#x2F;h4&gt;
&lt;p&gt;测试会在你拥有的 CPU 核心数的 5% 上运行。例如，如果你有 4 个核心，100% 就是 4 个，每个核心占 25%。在我们的例子中，5% 小于 25%，所以你只会得到一个 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-testconfig#test-config-workers&quot;&gt;worker&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;如果你觉得这有用，请点击赞同按钮。或者如果你想进一步激励我，可以 &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;adrianmaciuc?ref=blog.martioli.com&quot;&gt;请我喝杯咖啡&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;我的博客有一个 &lt;strong&gt;会员&lt;&#x2F;strong&gt; 部分。免费加入，没有垃圾邮件，你可以访问仅限会员的内容。 &lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-interview-questions-that-you-are-going-to-hate&#x2F;#&#x2F;portal&#x2F;signup&#x2F;free&quot;&gt;现在成为会员&lt;&#x2F;a&gt; ，你还将是第一个在我发布新文章时收到电子邮件的人。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-interview-questions-that-you-are-going-to-hate&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2024-05-05&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright 技巧与诀窍 #3</title>
        <published>2024-04-04T00:00:00+00:00</published>
        <updated>2024-04-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-tips-and-tricks-3/"/>
        <id>https://playwright.itest.info/blog/playwright-tips-and-tricks-3/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-tips-and-tricks-3/">&lt;p&gt;让我们深入了解 Playwright 的内部机制,掌握一些细节,从而提升我们的创造力。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-zai-ce-shi-yun-xing-qi-jian-huo-qu-geng-duo-ce-shi-xiang-qing&quot;&gt;1. 在测试运行期间获取更多测试详情&lt;&#x2F;h2&gt;
&lt;p&gt;你可以实时访问与测试相关的特定值。假设你有一个复杂项目,其配置基于环境、测试数据或其他细节而动态变化,你想在测试运行时查看具体设置的值。可以通过在测试中访问 &lt;code&gt;testInfo&lt;&#x2F;code&gt; 对象来实现。以下是一个代码示例:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试套件名称&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试名称&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;testInfo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试名称: ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;testInfo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.title}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;并行索引:${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;testInfo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;parallelIndex&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;分片索引: ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;testInfo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;shard&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;)}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;下面是通过 &lt;code&gt;testInfo&lt;&#x2F;code&gt; 可以访问的其他值的截图&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tips-and-tricks-3&#x2F;img&#x2F;1730512585.557697image.png&quot; alt=&quot;图片 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tips-and-tricks-3&#x2F;img&#x2F;1730512588.1221068image-1.png&quot; alt=&quot;图片 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我的&lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;full-parallelization-with-playwright&#x2F;&quot;&gt;关于并发执行的文章&lt;&#x2F;a&gt;展示了如何利用这个对象来显示特定值。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-ru-he-shi-yong-playwright-ce-shi-duo-ge-liu-lan-qi-chuang-kou&quot;&gt;2. 如何使用 Playwright 测试多个浏览器窗口?&lt;&#x2F;h2&gt;
&lt;p&gt;以下是测试多个窗口的方法。注意,这里指的是多个窗口,而不是同一窗口中的多个标签页。每个窗口都有自己的存储和 cookie。一个用例可能是测试网站的聊天功能,你想验证消息是否正确传递。你可以用两个浏览器分别登录两个用户,让他们互相聊天。如何在 Playwright 的单个测试中实现这一点呢?&lt;&#x2F;p&gt;
&lt;p&gt;我们可以使用 &lt;code&gt;browser&lt;&#x2F;code&gt; 和 &lt;code&gt;page&lt;&#x2F;code&gt; 对象。下面是一个代码示例:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;两用户聊天功能&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 打开两个浏览器,每个都有自己的存储和 cookie
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user1Context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user1Page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user1Context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user2Context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user2Page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user2Context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 打开聊天页面
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user1Page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.yourweb.com&#x2F;chat&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user2Page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.yourweb.com&#x2F;chat&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这里还需要添加登录凭证等详细信息
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 按顺序进行对话
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user1Page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getById&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你好,用户2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user1Page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getById&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;sendMsgBtn&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user2Page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你好,用户2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user2Page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getById&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;哦!你好,用户1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user2Page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getById&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;sendMsgBtn&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user1Page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;哦!你好,用户1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;当然,聊天只是一个例子,你可以根据需要应用到其他场景。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-playwright-ru-he-chu-li-tong-yi-liu-lan-qi-zhong-de-duo-ge-biao-qian-ye&quot;&gt;3. Playwright 如何处理同一浏览器中的多个标签页?&lt;&#x2F;h2&gt;
&lt;p&gt;对于某些元素具有 &lt;code&gt;target=&quot;_blank&quot;&lt;&#x2F;code&gt; 属性,点击后会打开新标签页的情况,可以参考 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;pages?ref=blog.martioli.com#handling-new-pages&quot;&gt;Playwright 文档中的这部分&lt;&#x2F;a&gt;。如果你觉得 &lt;code&gt;const pagePromise = context.waitForEvent(&#x27;page&#x27;)&lt;&#x2F;code&gt; 难以理解,可以将其视为一个事件监听器,它不会阻塞测试,只是在监听。在执行打开新标签页的点击操作后,立即添加 &lt;code&gt;const newPage = await pagePromise&lt;&#x2F;code&gt;,之后就可以像使用上面例子中的 &lt;code&gt;user2Page&lt;&#x2F;code&gt; 一样使用 &lt;code&gt;newPage&lt;&#x2F;code&gt; 了。现在你可以在 &lt;code&gt;newPage&lt;&#x2F;code&gt; 对象或初始页面对象之间切换,无需额外操作。熟悉 Selenium 的人可能记得使用 &lt;code&gt;driver.switchTo().window(actual)&lt;&#x2F;code&gt; 来回切换,在 Playwright 中不再需要这样做。这里每个页面都有自己的对象。&lt;&#x2F;p&gt;
&lt;p&gt;如果你想完全理解这种&quot;监听&quot;事件(如新页面打开)的技巧,建议仔细阅读第 6 点。这不是一个简单的概念,但我相信你读完解释后就能掌握它。&lt;&#x2F;p&gt;
&lt;p&gt;请记住:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;browser.newContext() = 新窗口 (还不是完整的浏览器,还需要一个标签页)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;context.newPage() = 新标签页&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;下面是更多示例用来帮助理解。请仔细阅读注释。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;默认方式的多窗口和标签页&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Playwright 的默认使用方式
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; page 包含了你在配置中设置的浏览器信息
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 可以直接使用,无需额外操作
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这会打开一个窗口(context)和一个标签页(page)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;duckduckgo.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;多窗口和标签页&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这创建了一个新窗口,但你还不能对 page2Context 执行操作
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 因为它还不完整,还需要一个标签页
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page2Context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 我们有了浏览器和窗口,只需要一个标签页。这样做:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page2Context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;尝试混合使用:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;混合使用多窗口和标签页&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这会正常打开一个完整的浏览器,包含窗口和标签页(默认方式)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;duckduckgo.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这会在同一个窗口(context)中创建一个新标签页
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page2&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这会设置一个新的浏览器窗口和标签页
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 独立于上面的操作
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page3Context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page3 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page3Context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page3&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;adrianmaciuc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-ru-he-zai-yi-ge-ce-shi-zhong-chu-li-duo-chong-lei-xing-de-liu-lan-qi&quot;&gt;4. 如何在一个测试中处理多种类型的浏览器?&lt;&#x2F;h2&gt;
&lt;p&gt;下面我要展示的不是测试多个浏览器的方法。有更高效的方式来做这件事。我不确定是否会写关于这个的文章,因为它相当简单,网上也有很多这样的教程。但为了让我们深入了解浏览器实例是如何创建的,看看下面如何直接在测试范围内操作各种浏览器。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;webkit&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firefox&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;多浏览器驱动&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;webkit&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firefox&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser2&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context2&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page2&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;注意这里没有使用 { browser, page }。我们把 webkit 和 firefox 对象直接引入了测试范围。这样做有点牵强,但为了理解原理并可能在未来开发一些创新想法,了解它的工作方式是有好处的。&lt;&#x2F;p&gt;
&lt;p&gt;记住,在正常设置中,当你只使用 &lt;code&gt;test&lt;&#x2F;code&gt; 并解构 &lt;code&gt;{ page }&lt;&#x2F;code&gt; 时,这个对象会带有你在 Playwright 配置文件中设置的浏览器信息,或者可以通过终端命令或流水线动态设置的值。&lt;&#x2F;p&gt;
&lt;p&gt;对 JAVA 爱好者来说,还记得下面这些代码吗?&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;org&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;openqa&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;selenium&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chrome&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ChromeDriver&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;WebDriver driver&lt;&#x2F;span&gt;&lt;span&gt; = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;new ChromeDriver&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;driver&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.martioli.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果你有 Java 和 Selenium 的背景,那么我上面所有关于如何实例化和处理驱动程序的解释对你来说都很有意义。你也会理解,我们不再需要编写任何其他代码来使 driver 对象准备就绪。我们不是必须这样做,但如果想要也没有问题。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-wo-ke-yi-zai-ce-shi-zhong-fu-gai-playwright-de-pei-zhi-xiang-ma&quot;&gt;5. 我可以在测试中覆盖 Playwright 的配置项吗?&lt;&#x2F;h2&gt;
&lt;p&gt;我们都知道 &lt;strong&gt;playwright.config&lt;&#x2F;strong&gt; 文件&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-configuration?ref=blog.martioli.com&quot;&gt;包含了配置&lt;&#x2F;a&gt;,帮助我们进行项目的设置,所有测试都会使用这些配置运行。但如果&lt;strong&gt;我想仅为一个测试或一组测试覆盖配置&lt;&#x2F;strong&gt;呢?&lt;&#x2F;p&gt;
&lt;p&gt;如果我想让一组测试使用一套配置,另一组使用另一套配置,该怎么办?&lt;&#x2F;p&gt;
&lt;p&gt;你可以通过&lt;strong&gt;两种方式&lt;&#x2F;strong&gt;实现:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;简单方法: 创建一个&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-projects?ref=blog.martioli.com&quot;&gt;项目&lt;&#x2F;a&gt;,并为该项目编写所有&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-use-options?ref=blog.martioli.com&quot;&gt;use 选项&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;第一种方法的示例如下:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  projects: [
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;项目名称&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;devices[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Desktop Chrome&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;colorScheme&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dark&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locale&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;fr-FR&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;httpCredentials&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你的用户名&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你的密码&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;testIdAttribute&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;data-testid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这里可以添加任何其他配置
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;默认情况下,运行测试时会对所有项目执行所有测试。所以请确保使用 &lt;code&gt;--project=项目名称&lt;&#x2F;code&gt; 指定要运行的项目。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;非正式方法: 在测试中覆盖配置值&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;对于第二种方法,假设你希望特定的测试集或 spec 文件中的测试用例使用特殊的地理位置或 window 设置,或者某个测试需要绕过登录过程。换句话说,&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-use-options?ref=blog.martioli.com&quot;&gt;这里列出的&lt;&#x2F;a&gt;任何值都可以在测试中进行操作。实现方法如下:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  geolocation: { longitude: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;36.095388&lt;&#x2F;span&gt;&lt;span&gt;, latitude: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;28.0855558 &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  userAgent: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;我的超级秘密代理值&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;覆盖配置&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这将保留其他设置,只覆盖你需要的部分。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;彩蛋 -&amp;gt; 告诉我这里地理位置对应的地点名称,你将赢得我的金牌读者称号&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果你想在同一个 spec 文件中有多个测试集,每个测试集都有自己的额外配置,可以这样做:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;webkit&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firefox&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;覆盖套件 1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    viewport: { width: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;, height: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;810 &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    geolocation: { longitude: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;36.095388&lt;&#x2F;span&gt;&lt;span&gt;, latitude: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;28.0855558 &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    userAgent: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;我的超级秘密代理值&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;覆盖测试 1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;覆盖套件 2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    viewport: { width: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;768&lt;&#x2F;span&gt;&lt;span&gt;, height: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1024 &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    geolocation: { longitude: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;36.095388&lt;&#x2F;span&gt;&lt;span&gt;, latitude: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;28.0855558 &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    userAgent: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;我的第二个超级秘密代理值 2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;覆盖测试 2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果你想在 spec 文件级别应用相同的覆盖配置,只需将 test.use() 移到文件顶部,它就会应用于文件中的所有套件。&lt;&#x2F;p&gt;
&lt;p&gt;配置也可以通过 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-configuration?ref=blog.martioli.com#advanced-configuration&quot;&gt;globalSetup&lt;&#x2F;a&gt; 完成。这是一种更优雅和高级的方法。我可能会在未来写一篇关于它的博文。&lt;&#x2F;p&gt;
&lt;p&gt;虽然不那么优雅,但还有另一种方法,你可以使用 context 传入配置覆盖。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;覆盖测试 1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;iPhone 13&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    isMobile: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;martioli.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;6-playwright-zhong-de-promise-all&quot;&gt;6. Playwright 中的 Promise.all&lt;&#x2F;h2&gt;
&lt;p&gt;我想讨论这个问题,因为我经常看到人们在使用 Playwright 进行项目开发时,并不完全理解何时应该使用 Promise.all()。我会引用一个最佳解释(不是我的原话):&lt;&#x2F;p&gt;
&lt;p&gt;我将用 &lt;code&gt;waitForResponse()&lt;&#x2F;code&gt; 方法来举例说明&lt;&#x2F;p&gt;
&lt;p&gt;假设我们有一个搜索输入框和一个触发搜索的按钮,最终会向 API 发送请求(&lt;a href=&quot;https:&#x2F;&#x2F;example.com&#x2F;api&#x2F;search?ref=blog.martioli.com&quot;&gt;https:&#x2F;&#x2F;example.com&#x2F;api&#x2F;search&lt;&#x2F;a&gt;,搜索词在请求体中)&lt;&#x2F;p&gt;
&lt;p&gt;你可能会这样写代码:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 搜索按钮
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForResponse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;example.com&#x2F;api&#x2F;search&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;上面的代码存在一个(很高的)可能性:在我们执行到 &lt;code&gt;await page.waitForResponse(&quot;https:&#x2F;&#x2F;example.com&#x2F;api&#x2F;search&quot;)&lt;&#x2F;code&gt; 这一行之前,我们已经收到了来自 &lt;a href=&quot;https:&#x2F;&#x2F;example.com&#x2F;api&#x2F;search?ref=blog.martioli.com&quot;&gt;&lt;code&gt;https:&#x2F;&#x2F;example.com&#x2F;api&#x2F;search&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; 的响应。&lt;code&gt;.click()&lt;&#x2F;code&gt; 方法不会立即 resolve,而是在 resolve await promise 并继续到下一行之前执行一系列(耗时的)步骤。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;敲黑板，乙醇的评论 👀。上面是原文的翻译，我尽力了，不过大家可能还是看不懂。其实作者的意思是上面的代码里&lt;code&gt;waitForResponse()&lt;&#x2F;code&gt;只有等到&lt;code&gt;click()&lt;&#x2F;code&gt;运行结束之后才会执行，而我们的目标是让这两行代码同时执行&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Await 按顺序异步执行代码,一个接一个。&lt;&#x2F;p&gt;
&lt;p&gt;我们真正想要的是 &lt;code&gt;await page.locator(&quot;button&quot;).click()&lt;&#x2F;code&gt; 和 &lt;code&gt;await page.waitForResponse(&quot;https:&#x2F;&#x2F;example.com&#x2F;api&#x2F;search&quot;)&lt;&#x2F;code&gt; 同时执行 - 这样两者都能正确完成各自的工作。&lt;&#x2F;p&gt;
&lt;p&gt;这就是 Promise.all() 发挥作用的地方。&lt;&#x2F;p&gt;
&lt;p&gt;Promise.all() 并发执行 promises,这意味着,&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Promise&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;([
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForResponse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;example.com&#x2F;api&#x2F;search&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;同时执行 &lt;code&gt;.click()&lt;&#x2F;code&gt; 和 &lt;code&gt;.waitForResponse()&lt;&#x2F;code&gt;。整个 await Promise.all() 只有在所有传入的 promise 参数都 resolve 后才会 resolve。我们在这里遇到的问题被称为竞态条件。&lt;&#x2F;p&gt;
&lt;p&gt;许多 Playwright 事件 &lt;code&gt;(.waitForRequest(), .waitForResponse(), .waitForEvent(), ...)&lt;&#x2F;code&gt; 必须使用 Promise.all 与它们的触发器并发执行。&lt;&#x2F;p&gt;
&lt;p&gt;这里是&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright&#x2F;issues&#x2F;5470?ref=blog.martioli.com#issuecomment-1285640689&quot;&gt;完整解释的链接&lt;&#x2F;a&gt;,感谢 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;advename?ref=blog.martioli.com&quot;&gt;advename&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果你觉得这篇文章有用,请点击鼓掌按钮。或者如果你想给我更多动力,&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;adrianmaciuc?ref=blog.martioli.com&quot;&gt;给我买杯咖啡&lt;&#x2F;a&gt;也行。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-tips-and-tricks-3&#x2F;&quot;&gt;URL Source&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2024-04-04&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何使用 Playwright 进行LLM的提示词注入测试</title>
        <published>2024-04-04T00:00:00+00:00</published>
        <updated>2024-04-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/testing-ai-how-to-create-automated-prompt-testing-with-playwright/"/>
        <id>https://playwright.itest.info/blog/testing-ai-how-to-create-automated-prompt-testing-with-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/testing-ai-how-to-create-automated-prompt-testing-with-playwright/">&lt;h2 id=&quot;wo-fan-yi-de-hao-tong-ku-joy-xie-zai-qian-mian&quot;&gt;我翻译的好痛苦 😂 写在前面&lt;&#x2F;h2&gt;
&lt;p&gt;这篇文章一开始看的我云山雾绕，后来我看了一下&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;stuart-thomas-zoopla&#x2F;LlmTestingWithPlaywright&#x2F;tree&#x2F;main&#x2F;tests&quot;&gt;作者的代码&lt;&#x2F;a&gt;，大概用一句话总结就是：作者用 playwright 调用 llm 的 api，然后实现了一些断言，用来检查 llm 是否存在注入的问题。&lt;&#x2F;p&gt;
&lt;p&gt;也就是说：&lt;strong&gt;不用 playwright，用其他的测试框架去测试也是没问题的。&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;欢迎来到我的 AI 测试系列的第二篇文章。本系列文章的灵感来源于 &lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.ministryoftesting.com&#x2F;events&#x2F;30-days-of-ai-in-testing&quot;&gt;Ministry of Testing 30 天 AI 测试活动&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;。如果您还没有阅读系列的第一篇文章，请查看 – &lt;a href=&quot;https:&#x2F;&#x2F;www.thequalityduck.co.uk&#x2F;testing-ai-how-to-run-the-llama-2-large-language-model-locally&#x2F;&quot;&gt;&lt;strong&gt;测试 AI – 如何在本地运行 Llama 2 LLM&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;。在本文中，我将讨论如何使用 Playwright 为大型语言模型 (LLM) 创建自动化提示测试框架。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-playwright-zi-dong-hua-llm-ce-shi&quot;&gt;使用 Playwright 自动化 LLM 测试&lt;&#x2F;h2&gt;
&lt;p&gt;在我写完 &lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.thequalityduck.co.uk&#x2F;testing-ai-how-to-run-the-llama-2-large-language-model-locally&#x2F;&quot;&gt;上篇文章&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; 后，我已经在本地运行了一个 LLM。接下来的问题是如何编写自动化测试？答案是使用 Playwright 进行提示测试。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shen-me-shi-ti-shi-ce-shi&quot;&gt;什么是提示测试？&lt;&#x2F;h3&gt;
&lt;p&gt;提示测试，有时称为提示工程，是向 LLM 提供提示并评估其响应的过程。在本文中，我不会详细讨论提示测试的细节，而是专注于如何创建可靠的自动化测试。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zi-dong-hua-ti-shi-ce-shi-gong-ju&quot;&gt;自动化提示测试工具&lt;&#x2F;h3&gt;
&lt;p&gt;在本文中，我将使用以下工具：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;llama.meta.com&#x2F;llama2&#x2F;&quot;&gt;LLama 2&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; – 由 Meta 创建的大型语言模型&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ollama&#x2F;ollama&#x2F;blob&#x2F;main&#x2F;docs&#x2F;api.md&quot;&gt;Ollama&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; – 用于本地运行 Llama 2 的工具&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;&quot;&gt;Playwright&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; – 支持多种语言的测试框架&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Ollama 提供了一个用于与 LLM 交互的 API，这使得创建自动化提示测试变得简单。&lt;&#x2F;p&gt;
&lt;p&gt;对于测试，我选择使用 TypeScript 编写。我在 &lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;stuart-thomas-zoopla&#x2F;LlmTestingWithPlaywright&quot;&gt;GitHub&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; 上提供了我的示例测试框架。&lt;&#x2F;p&gt;
&lt;p&gt;虽然我们在进行 API 调用，但提示测试与 API 测试有显著区别。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ti-shi-ce-shi-yu-api-ce-shi-de-qu-bie&quot;&gt;提示测试与 API 测试的区别&lt;&#x2F;h3&gt;
&lt;p&gt;使用 API 进行测试使得 LLM 测试与其他 API 测试非常相似，但有一个关键区别。我们永远不能百分之百确定从 LLM 接收到的响应。为了解决这个问题，我们必须编写基于期望响应中包含的关键字或短语的断言，而不是对完整响应进行断言。我有一些关于如何改进这方面的方法 – &lt;a href=&quot;https:&#x2F;&#x2F;www.thequalityduck.co.uk&#x2F;testing-ai-how-to-create-automated-prompt-testing-with-playwright&#x2F;#improving&quot; title=&quot;稍后会详细介绍&quot;&gt;&lt;strong&gt;稍后会详细介绍&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;jin-zhi-ci&quot;&gt;“禁止词”&lt;&#x2F;h3&gt;
&lt;p&gt;我还尝试了一种“禁止词”的概念。这些是任何回复中都不应该包含的词，并且在所有测试中都要断言它们不在响应中。&lt;&#x2F;p&gt;
&lt;p&gt;根据 LLM 部署的上下文，这种用例会有所不同。我假设 LLM 是一个面向公众的服务。在这种情况下，有很多事情是您希望聊天机器人不要说的。从使用攻击性语言到评论竞争对手等。我们可以在所有测试中进行断言，并创建专门测试 &lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;owasp.org&#x2F;www-project-top-10-for-large-language-model-applications&#x2F;Archive&#x2F;0_1_vulns&#x2F;Prompt_Injection.html&quot;&gt;提示注入&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; 的测试。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shen-me-shi-llm-de-ti-shi-zhu-ru&quot;&gt;什么是 LLM 的提示注入&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;描述:&lt;&#x2F;strong&gt; 提示注入涉及使用精心制作的提示绕过过滤器或操纵 LLM,使模型忽略先前的指令或执行非预期的操作。这些漏洞可能导致意外后果,包括数据泄露、未授权访问或其他安全漏洞。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;常见的提示注入漏洞:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;制作能够操纵 LLM 泄露敏感信息的提示。&lt;&#x2F;li&gt;
&lt;li&gt;通过使用特定的语言模式或标记绕过过滤器或限制。&lt;&#x2F;li&gt;
&lt;li&gt;利用 LLM 的分词或编码机制中的弱点。&lt;&#x2F;li&gt;
&lt;li&gt;通过提供误导性上下文,误导 LLM 执行非预期操作。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;如何预防:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;对用户提供的提示实施严格的输入验证和清理。&lt;&#x2F;li&gt;
&lt;li&gt;使用上下文感知的过滤和输出编码来防止提示操纵。&lt;&#x2F;li&gt;
&lt;li&gt;定期更新和微调 LLM,以提高其对恶意输入和边缘情况的理解。&lt;&#x2F;li&gt;
&lt;li&gt;监控和记录 LLM 交互,以检测和分析潜在的提示注入尝试。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;攻击场景示例:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;场景 #1:_ 攻击者制作一个提示,通过让模型认为请求是合法的,从而欺骗 LLM 泄露敏感信息,如用户凭证或内部系统细节。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;场景 #2:_ 恶意用户通过使用特定的语言模式、标记或编码机制绕过内容过滤器,而 LLM 未能识别这些内容为受限内容,从而允许用户执行原本应该被阻止的操作。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;duan-yan-shi-xian&quot;&gt;断言实现&lt;&#x2F;h3&gt;
&lt;p&gt;我将 &lt;code&gt;checkForForbiddenWords&lt;&#x2F;code&gt; 函数包含在 &lt;code&gt;checkResponseIncludes&lt;&#x2F;code&gt; 函数中，因为我不想在每个测试中专门调用它。但是，我也希望能够绕过它，因此我添加了一个变量来实现这一点。&lt;&#x2F;p&gt;
&lt;p&gt;Ollama API 提供了一种自动化单一提示和端到端对话的方法。对于端到端对话，我们提供用户和 LLM 的响应，直到最终响应。您可以在 &lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;stuart-thomas-zoopla&#x2F;LlmTestingWithPlaywright&quot;&gt;GitHub&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; 上看到这两种测试类型的示例。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gai-jin-duan-yan&quot;&gt;改进断言&lt;&#x2F;h2&gt;
&lt;p&gt;我决定以两种方式处理断言。第一种是对响应内容进行断言，这对任何做过 API 测试的人来说都很熟悉。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dui-xiang-ying-nei-rong-jin-xing-duan-yan&quot;&gt;对响应内容进行断言&lt;&#x2F;h3&gt;
&lt;p&gt;对于这部分测试，我只是断言响应包含某个关键字或短语。我为此创建了一个可重用的助手函数。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;word &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;of &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expectedWords&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseString&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;word&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeTruthy&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;该函数设计为可以提供单个字符串或多个字符串（作为数组）。这使我能够创建检查响应中是否包含单词或短语的断言，或者它们的组合。&lt;&#x2F;p&gt;
&lt;p&gt;需要以允许 LLM 响应变化的方式进行断言，这使我们可能会遇到不稳定的测试结果。我们真正需要的是评估答案是否传达了我们预期的信息。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-yong-llm-ce-shi-llm&quot;&gt;使用 LLM 测试 LLM&lt;&#x2F;h3&gt;
&lt;p&gt;我们可以使用 LLM 来测试预期响应和实际响应在语义上是否相似，我称这种方法为评估 LLM。这样我们就不需要知道将要收到的确切响应，只需了解 LLM 对提示的回答大致会是什么样。&lt;&#x2F;p&gt;
&lt;p&gt;为此，我们提供一个类似这样的提示。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;If the following 2 statements appear to give accurate and corroborating answers to the same question respond &amp;#39;Yes, the statements are similar&amp;#39;.
&lt;&#x2F;span&gt;&lt;span&gt;Statement 1 - {{originalResponse}} - end of statement 1. Statement 2 - {{expectedResponse}} - end of statement2
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;当然，这意味着我们在用另一个黑箱测试一个黑箱。在我的示例中，我们实际上使用相同的 LLM 进行比较。尽管这并不理想，但可以用来演示概念。这确实可能会导致 LLM 的不正确评估，但提供了另一种检查响应的方法。需要指出的是，我创建的这个提示可能远非完美，需要随着时间的推移不断改进，但它可以用于概念演示。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.thequalityduck.co.uk&#x2F;wp-content&#x2F;uploads&#x2F;2024&#x2F;04&#x2F;image-12.png&quot; alt=&quot;Image 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wei-shen-me-bu-liang-zhe-jian-yong&quot;&gt;为什么不两者兼用？&lt;&#x2F;h3&gt;
&lt;p&gt;我认为目前这种测试的最佳方法是结合传统断言和评估 LLM 的方法。这样，您可以使用传统断言检查响应中是否包含（或不包含）特定关键字，并使用评估 LLM 提供对预期答案语义相似度的信心。&lt;&#x2F;p&gt;
&lt;p&gt;在 &lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;stuart-thomas-zoopla&#x2F;LlmTestingWithPlaywright&quot;&gt;GitHub&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; 上，我提供了一个使用评估 LLM 的示例测试。在这个示例中，我再次使用 Llama 2 评估其提供的答案质量，并将其与已知答案进行对比。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhuan-yong-llm-ce-shi-gong-ju&quot;&gt;专用 LLM 测试工具&lt;&#x2F;h2&gt;
&lt;p&gt;市面上有专门的 LLM 测试工具，我在使用 Playwright 之前也看过几个。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;promptize&quot;&gt;promptize&lt;&#x2F;h3&gt;
&lt;p&gt;其中一个我想进一步调查的是 &lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;preset-io&#x2F;promptimize&quot;&gt;promptize&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;，因为它提供了一些使用 Playwright 这样的工具时不存在的额外功能。不幸的是，它专门支持 OpenAI，经过快速研究后，我决定重构它以支持其他工具和模型需要投入更多时间和精力，这超出了我目前的能力范围。也许将来会有机会继续研究。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;promptfoo&quot;&gt;promptfoo&lt;&#x2F;h3&gt;
&lt;p&gt;我还发现了 &lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;promptfoo&#x2F;promptfoo&quot;&gt;promptfoo&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;，看起来很有前途。它比 promptize 更开放，并且提供了一些不错的原生功能，包括创建测试矩阵。&lt;&#x2F;p&gt;
&lt;p&gt;我尚未投入足够的时间来研究 promptfoo。相对于我的 Playwright 解决方案，使用这样的工具确实更有吸引力，因此我肯定会在未来继续研究。我预计要获得一致的结果，我需要比目前更多地定制我的配置。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;trulens&quot;&gt;Trulens&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.trulens.org&#x2F;&quot;&gt;Trulens&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; 类似于 promptfoo。它提供了一种评估 LLM 质量的方法，而不是严格的测试工具。在撰写本文时，我仍在研究 Trulens，但它看起来很有前途，我会很快写一篇关于它的完整文章。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h2&gt;
&lt;p&gt;自动化提示测试感觉就像是 web 应用程序的自动化 UI 测试。它不需要了解底层的运行机制，并且类似于最终用户体验应用程序的方式。然而，由于不同的原因，它也具有一些相同的缺点，主要是运行成本高昂。在 LLM 的情况下，这是因为当它们托管在云上时，您会因每次提示而被收费。&lt;&#x2F;p&gt;
&lt;p&gt;自动化提示测试是我们开始自动化测试的一个很好的起点，但我们绝对需要深入了解底层机制，以最有效地进行测试。在未来的文章中，我希望进一步探索专用的 LLM 测试工具，以及研究专业的可观测性工具，敬请期待！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.thequalityduck.co.uk&#x2F;testing-ai-how-to-create-automated-prompt-testing-with-playwright&#x2F;&quot;&gt;来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2024-04-04&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何用标签来优化你的 Playwright 测试套件</title>
        <published>2024-03-18T00:00:00+00:00</published>
        <updated>2024-03-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/strategic-tagging-optimizing-your-playwright-test-suit/"/>
        <id>https://playwright.itest.info/blog/strategic-tagging-optimizing-your-playwright-test-suit/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/strategic-tagging-optimizing-your-playwright-test-suit/">&lt;p&gt;你是否发现自己每次都在运行完整的自动化测试套件?利用标签(tags)可以精准过滤一部分用例来加速这一过程。&lt;&#x2F;p&gt;
&lt;p&gt;随着软件项目的发展和自动化项目的扩展,测试数量通常会随着新功能的引入而增加。此时,只运行某些测试的子集变得非常有用。尽管 Playwright 允许测试并行运行,但将测试分割成更小的组别在某些时候会更有益。虽然维护一个强大的自动化测试套件对产品质量至关重要,但不谨慎的测试自动化可能会显著降低团队的进度。一个有效的策略是通过标签来分类你的自动化测试,并在软件开发过程的不同阶段只执行完整测试套件的一个子集。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;考虑以下示例:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;◾ 在非工作时间运行整个测试套件而不打扰团队,并在 pull request 上有选择地运行测试子集,以保持 &lt;strong&gt;CI 流水线&lt;&#x2F;strong&gt; 的速度和效率。&lt;br &#x2F;&gt;
◾ 允许特定团队(如 QA 或功能团队)只运行他们负责的测试。&lt;br &#x2F;&gt;
◾ 在生产发布期间运行只涉及读取操作(没有写入操作)的冒烟测试。&lt;&#x2F;p&gt;
&lt;p&gt;标签用于在 HTML 报告、UI 模式或 VSCode 扩展中过滤测试。&lt;&#x2F;p&gt;
&lt;p&gt;使用标签系统允许你将测试分类为逻辑集合。标签使用测试描述中的 &lt;strong&gt;@tag&lt;&#x2F;strong&gt; 语法定义。虽然技术上任何字符串都可以作为标签,但文档更倾向于使用 &lt;strong&gt;@tag&lt;&#x2F;strong&gt; 语法,所以建议遵循这个规则。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;如何安装 Playwright?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;npm init playwright@latest&lt;&#x2F;p&gt;
&lt;p&gt;更多详细信息请访问官方 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;intro&quot;&gt;Installation | Playwright&lt;&#x2F;a&gt; 文档。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;如何运行 Playwright 测试?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;旧的 Playwright 语法:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;过去,标签被整合到测试标题中,这仍然是一种受支持的方法。然而,这种方法会导致 HTML 报告不好看。Playwright 会自动从标题中提取标签并将其显示为标签,以提高可见性,从而消除了在标题内进行冗余标签的需求。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright Landing page - Has title @Smoke&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;playwright.dev&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveTitle&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;如何按标签运行测试?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;npx playwright test --grep @Smoke
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*sh8nQ1APLG4vq_0T7DshFw.png&quot; alt=&quot;图片 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆 旧的 Playwright 标签语法示例&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;新的 Playwright 语法:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;根据官方文档所述,引入新的标签语法的原因源于先前语法在 HTML 报告中显得有些冗余，而且也看不太出来。正如我们在旧的 Playwright 语法部分所展示的那样。这种标签方式可能导致混淆和显著的重复,尤其是在处理大量标签时。&lt;&#x2F;p&gt;
&lt;p&gt;要采用新语法,只需生成一个包含标签的数组或单个标签的标签对象:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright Landing page - Has title&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  { tag: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@Smoke&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@UI&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] },
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;playwright.dev&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveTitle&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*ldDx5Z4uAqPunnQ9ZzFJHw.png&quot; alt=&quot;图片 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆 新的 Playwright 标签语法示例&lt;&#x2F;p&gt;
&lt;p&gt;从新语法可以看出,标签不再显示在测试名称内,从而显著提高了可读性。&lt;&#x2F;p&gt;
&lt;p&gt;标签也适用于 describe 语法块:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Group Example&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { tag: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@Group&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright Landing page - Has title&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    { tag: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@Smoke&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@UI&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] },
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;playwright.dev&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveTitle&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright Github&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    { tag: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@Smoke&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@UI&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@Fast&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] },
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*Ry1DaYod_eLcC-HfEP9aKw.png&quot; alt=&quot;图片 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;⬆️ Describe 块标签语法示例&lt;&#x2F;p&gt;
&lt;p&gt;使用以下命令将 Playwright 更新到最新版本:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; install&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -D&lt;&#x2F;span&gt;&lt;span&gt; @playwright&#x2F;test@latest
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;同时下载新的浏览器二进制文件及其依赖项:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright install&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --with-deps
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;要验证你机器上安装的版本,请使用以下命令:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --version
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;zai-ce-shi-guan-li-zhong-shi-yong-tag-de-you-shi&quot;&gt;在测试管理中使用 @tag 的优势:&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;简化测试用例管理:&lt;&#x2F;strong&gt; &lt;strong&gt;@tag&lt;&#x2F;strong&gt; 通过对测试用例进行分类来简化测试管理。这种分类允许根据标签快速过滤和识别相关的测试用例。你可以轻松根据特定标签执行的具体的测试用例。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;标签统计热图:&lt;&#x2F;strong&gt; 分析仪表板上提供的标签统计热图提供了有价值的总览。它允许你跟踪与标签相关的指标,如标签总数和带有特定标签的测试用例数量。然而,跟踪按标签划分的测试自动化覆盖率也很重要。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;自定义测试场景:&lt;&#x2F;strong&gt; 你可以灵活地为任何自定义测试场景定义有用的标签。这允许来自不同功能、测试套件或功能文件(BDD)的场景一起执行。例如,你可以执行所有标签为 &lt;strong&gt;@Smoke&lt;&#x2F;strong&gt; 的测试,同时排除那些标签为 &lt;strong&gt;@Regression&lt;&#x2F;strong&gt; 的测试。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yi-xie-ju-ti-de-fen-lei-shi-li&quot;&gt;一些具体的分类示例&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;冒烟测试&lt;&#x2F;strong&gt; 是一种在软件构建完成后执行的软件测试技术,用于验证软件的关键功能是否正常工作。它在执行任何详细的功能或回归测试之前进行。冒烟测试的主要目的是拒绝存在错误的软件应用,以便 QA 工程团队不会浪费时间测试一个有问题的软件应用。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;健全性测试&lt;&#x2F;strong&gt; 是一种在收到软件的中间版本后执行的软件测试类型,通常涉及对代码或功能的微小更改。其目的是确保错误已被修复,并且这些更改没有引入新的问题。目标是验证预期的功能大致按预期工作。如果正确性测试给出错误结果,构建将被拒绝,以避免在更广泛的测试上花费时间和资源。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;回归测试&lt;&#x2F;strong&gt; 是在代码更新后进行的一种软件测试,以确保更新没有引入新的错误。这是因为新代码可能带来与现有代码冲突的新逻辑,导致缺陷。通常,QA 团队会为重要功能准备一系列回归测试用例,每次发生这些代码更改时都会重新执行这些用例,以节省时间并最大化测试效率。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:553&#x2F;1*hirQV3d8tQpEWEuZq-wiCQ.png&quot; alt=&quot;图片 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;因此,组织好你的测试标签策略非常重要。当你想只运行特定的测试集而不是整个套件时,你就处于更有利的位置。在 Smoke 标签名下,你可以只运行冒烟测试,或者单独运行回归测试。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@merisstupar11&#x2F;strategic-tagging-optimizing-your-playwright-test-suit-4ab109343fed&quot;&gt;URL 来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2024-03-18&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何让playwright的测试用例运行的更加稳定</title>
        <published>2024-03-13T00:00:00+00:00</published>
        <updated>2024-03-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/flaky-tests-playwright/"/>
        <id>https://playwright.itest.info/blog/flaky-tests-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/flaky-tests-playwright/">&lt;p&gt;&lt;strong&gt;写在前面 by 乙醇 💡: 英文里有个专用名词叫做 flaky test，也就是易碎的测试用例的意思，中文直接翻译过来不太好听，我一般喜欢翻译成不稳定的测试用例，大家以后看到这个词大概知道是什么意思就好了&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Flaky test 是指在不同运行时产生不同结果的测试。有时它会通过，有时又会失败，原因不明。可以想象，导致这种行为背后一定有一些原因。这个问题在 UI 和 E2E 工具如 Playwright 上尤为明显。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 中的 flaky tests 是 CI 流水线的最大敌人之一，因为它们会导致看似随机的流水线任务失败。因此，一定要想办法避免不稳定的测试用例出现！&lt;&#x2F;p&gt;
&lt;p&gt;在本指南中，你将了解什么是 flaky test 及其主要原因。然后，你将探索一些避免在 Playwright 中出现 flaky tests 的最佳实践。&lt;&#x2F;p&gt;
&lt;p&gt;让我们开始吧！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shen-me-shi-playwright-zhong-de-flaky-test-bu-wen-ding-de-ce-shi-yong-li&quot;&gt;什么是 Playwright 中的 Flaky Test(不稳定的测试用例)？&lt;&#x2F;h2&gt;
&lt;p&gt;Flaky test 是指在同一个 &lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;pull-requests&#x2F;committing-changes-to-your-project&#x2F;creating-and-editing-commits&#x2F;about-commits#about-commits&quot;&gt;commit SHA&lt;&#x2F;a&gt; 上，测试结果不一致的测试。识别这种测试的另一个规则是“如果它在分支上通过，但在合并后失败，它可能是 flaky test。”在 Playwright 中，当一个测试第一次失败但重试之后通过时，这个测试就耀被标记为“flaky”。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 中的 flaky tests 在 CI 流水线中的影响尤为显著，因为它们的不一致性导致了同一次代码提交在不同尝试中出现不可预测的失败。为了确保成功部署，流水线需要在失败时多次运行。这会让 ci 的效率变低，并打击开发人员的信心，因为每次部署似乎都受到看似随机的测试不通过的影响。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ce-shi-wei-he-hui-bian-de-flaky-de-yuan-yin&quot;&gt;测试为何会变得 Flaky 的原因&lt;&#x2F;h2&gt;
&lt;p&gt;以下是导致测试变得 flaky 的一些重要原因：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;竞争条件&lt;&#x2F;strong&gt;：并发操作导致动态页面产生变化，然后就造成了意外的行为。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;被测应用太慢&lt;&#x2F;strong&gt;：如果被测应用程序运行在性能不好的机器上，测试中使用的超时可能会导致测试间歇性失败。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;测试中的 bug&lt;&#x2F;strong&gt;：测试脚本中的特定选择器，如不可靠的节点定位器，可能是测试不稳定的原因。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这些因素可以单独或共同导致测试变得 flaky。现在，让我们看看如何通过一些最佳实践来防止这些问题！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zai-playwright-zhong-bi-mian-bian-xie-flaky-tests-de-ce-lue&quot;&gt;在 Playwright 中避免编写 Flaky Tests 的策略&lt;&#x2F;h2&gt;
&lt;p&gt;在开始之前，请记住，你可以在 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright.git&quot;&gt;Playwright 的 GitHub 仓库&lt;&#x2F;a&gt; 中找到一些 Playwright 示例测试来练习。Clone 仓库，安装 Playwright，并使用以下命令启动测试：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt; clone https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright.git
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;cd&lt;&#x2F;span&gt;&lt;span&gt; playwright&#x2F;examples&#x2F;todomvc&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; install &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright install
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;现在，我们去官方文档里看一下官方推荐的避免 flaky tests 的最佳策略。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zai-ti-jiao-zhi-qian-yun-xing-he-diao-shi-ce-shi&quot;&gt;在提交之前运行和调试测试&lt;&#x2F;h3&gt;
&lt;p&gt;避免 Playwright flaky tests 的最简单和直观的方法是尝试预防它们。这个想法是在本地彻底检查所有测试，然后再提交。这样，你可以确保它们在不同情况下都是健壮的并且按预期工作。&lt;&#x2F;p&gt;
&lt;p&gt;由于 flaky test 比较鬼魅，不是每次都会报错，你应该尝试多次运行每个测试，并在与生产服务器类似资源的机器上进行测试。在本地测试中更改环境会产生与 CI&#x2F;CD 流水线结果不一致的结果。&lt;&#x2F;p&gt;
&lt;p&gt;在正确设置环境和配置 Playwright 后，你可以使用以下命令在本地运行所有测试：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;一旦测试运行完毕，如果有些测试失败，&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-reporters#html-reporter&quot;&gt;Plawright HTML 报告&lt;&#x2F;a&gt; 将自动打开，你也可以使用以下命令手动打开报告：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright show-report
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;生成的 HTML 文件将显示所测试执行的完整报告。详细来说，它允许你通过 flaky tests 过滤结果：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;blob:https:&#x2F;&#x2F;semaphoreci.com&#x2F;f5cac92d0c758d8aba7147f0dca557df&quot; alt=&quot;Image 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;请记住，当一个测试第一次失败但在另一尝试中通过时，它会被标记为“faulty”。换句话说，如果你想在本地检测 flaky tests，你需要配置 Playwright 在失败时自动重试（retry）测试。&lt;&#x2F;p&gt;
&lt;p&gt;在识别出 flaky test 后，你可以使用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;debug&quot;&gt;Playwright 提供的许多调试工具&lt;&#x2F;a&gt; 中的任意工具进行调试。例如，假设检测到的 flaky test 定义在 &lt;code&gt;landing-page.spec.ts&lt;&#x2F;code&gt; 文件第 15 行的 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-test&quot;&gt;&lt;code&gt;test()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; 函数中。用下面的命令进行调试：&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;npx playwright test landing-page.spec.ts:15 --debug
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;另外强烈推荐使用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-ui-mode&quot;&gt;UI 模式&lt;&#x2F;a&gt; 进行调试，因为它提供了一种时间旅行体验，使你能够直观地检查和浏览测试执行的所有操作：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;blob:https:&#x2F;&#x2F;semaphoreci.com&#x2F;f76b32e76d6f5786a90be088d7c98470&quot; alt=&quot;Image 2: Debugging tools for Playwright&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-yong-locators-er-fei-selectors&quot;&gt;使用 Locators 而非 Selectors&lt;&#x2F;h3&gt;
&lt;p&gt;编写健壮的 E2E(ui 自动化) 测试用例的关键点之一是为 HTML 节点编写稳定的定位策略。虽然你可能熟悉 XPath 和 CSS 选择器，但在测试中它们并不是最佳解决方案。问题在于使用 JavaScript 的现代页面里，很多 DOM 是动态的，这种静态选择器可能导致测试不稳定。&lt;&#x2F;p&gt;
&lt;p&gt;这就是为什么 Playwright 推荐使用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators&quot;&gt;locators&lt;&#x2F;a&gt;，它们更接近用户对页面的感知。你应该 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators#locate-by-test-id&quot;&gt;定位自定义的 testID&lt;&#x2F;a&gt; 或使用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators#locate-by-role&quot;&gt;Role Locator&lt;&#x2F;a&gt;，而不是编写 CSS 选择器。Locators 代表一种在任何时刻进行查找页面元素的方法，它们是 Playwright 自动等待和重试能力的核心。&lt;&#x2F;p&gt;
&lt;p&gt;以下是你应该使用的推荐内置定位器函数：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators#locate-by-role&quot;&gt;&lt;code&gt;page.getByRole()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;：按显式和隐式可访问性属性定位元素。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators#locate-by-text&quot;&gt;&lt;code&gt;page.getByText()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;：按文本内容定位节点。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators#locate-by-label&quot;&gt;&lt;code&gt;page.getByLabel()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;：通过关联标签的文本定位表单控件。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators#locate-by-placeholder&quot;&gt;&lt;code&gt;page.getByPlaceholder()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;：按占位符定位输入。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators#locate-by-alt-text&quot;&gt;&lt;code&gt;page.getByAltText()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;：通过其 &lt;code&gt;alt&lt;&#x2F;code&gt; 文本替代项定位元素，通常是图像。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators#locate-by-title&quot;&gt;&lt;code&gt;page.getByTitle()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;：通过其 &lt;code&gt;title&lt;&#x2F;code&gt; 属性定位元素。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators#locate-by-test-id&quot;&gt;&lt;code&gt;page.getByTestId()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;：基于其 &lt;code&gt;data-testid&lt;&#x2F;code&gt; 属性定位元素。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;现在，考虑以下 CSS 选择器：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;btn&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;primary&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;submit
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这很容易理解，但它显然没有以下表达式健壮和具有表现力：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Submit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;虽然 HTML 元素在 DOM 中的 &lt;code&gt;class&lt;&#x2F;code&gt; 属性可以动态更改，但其在页面上的文本和 role 不太可能轻易更改。&lt;&#x2F;p&gt;
&lt;p&gt;如果你绝对必须使用 CSS 或 XPath，请尽量编写一致且通用的选择器。XPath 和 CSS 选择器很容易与页面的 dom 结构绑在一起，这就非常恶心了，因为当页面结构发生一点点改变时测试就会不通过。记住，一长串的 CSS 或 XPath 选择器是不好的，这会导致测试变得 flaky 且难以维护。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;yong-yuan-bu-yao-yi-lai-ying-deng-dai&quot;&gt;永远不要依赖硬等待&lt;&#x2F;h3&gt;
&lt;p&gt;在测试中，硬等待是指在测试逻辑中添加固定时间延迟。其想法是暂停测试执行一段时间，以等待特定操作完成。虽然这是一种直接的等待方法，但它是导致 flaky tests 的主要原因之一。&lt;&#x2F;p&gt;
&lt;p&gt;例如这个例子:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&amp;quot;Load More&amp;quot; button loads new products&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 导航到要测试的页面
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;localhost:3000&#x2F;products&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 选择“Load More”按钮
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loadMoreButton &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Load More&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 点击“Load More”按钮
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loadMoreButton&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 暂停测试执行 10 秒等待
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 新产品加载到页面上
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10000&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 计算页面上的产品元素数量
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productNodes &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.product&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 验证页面上有
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;个新产品
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productNodes&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;用例假设页面上的产品将会在 10 秒内加载完毕。如果页面的性能低于预期，这种硬等待时间将导致测试失败。同样，如果应用程序在预期时间之前就加载完成了，10 秒的延迟将使测试变得更慢且效率低下。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 通过其 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page#page-wait-for&quot;&gt;&lt;code&gt;page.waitFor()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; 方法提供了更好的替代方案。这个方法允许你明确的等待元素满足特定条件：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&amp;quot;Load More&amp;quot; button loads new products&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 导航到要测试的页面
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;localhost:3000&#x2F;products&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 选择“Load More”按钮
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loadMoreButton &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Load More&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 点击“Load More”按钮
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loadMoreButton&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 等待 10 个产品元素出现在页面上
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 来自乙醇的注释⚡️ waitForSelector方法已经不推荐使用了，请使用locator.waitFor()来替代
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForSelector&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.product:nth-of-type(10)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 计算页面上的产品元素数量
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productNodes &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.product&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 验证页面上有 10 个新产品
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productNodes&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;page.waitForSelector()&lt;&#x2F;code&gt; 方法会自动轮询 DOM，直到满足指定条件。这使得测试更具确定性并减少了硬等待带来的风险。&lt;&#x2F;p&gt;
&lt;p&gt;来自乙醇的注释：👆 上面的方法已经不推荐使用了，现在应该使用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locator#locator-wait-for&quot;&gt;locator.waitFor()&lt;&#x2F;a&gt; 方法来替代。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;li-yong-playwright-nei-zhi-zhong-shi&quot;&gt;利用 Playwright 内置重试&lt;&#x2F;h3&gt;
&lt;p&gt;正如你在前一节中看到的，当测试失败时，Playwright 会标记它为 flaky。这会在生成报告中用黄色条目高亮显示 flaky tests。为了减少 CI&#x2F;CD 流水线中的 flaky tests 干扰，你可以使用 Playwright 的 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-configuration#retries&quot;&gt;&lt;code&gt;retries&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; 选项来设置测试的重试测试：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  retries: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 当测试失败时自动重试
&lt;&#x2F;span&gt;&lt;span&gt;  use: {
&lt;&#x2F;span&gt;&lt;span&gt;    baseURL: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;localhost:3000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;配置后，当一个测试失败时它将自动重试两次。在本地，你可以设置更高的重试次数来检测 flaky tests。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 的重试功能通过确保测试用例在因为&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;短暂的网络问题&lt;&#x2F;li&gt;
&lt;li&gt;或其他外部因素&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;运行失败后，最终还可以获得正确的结果，是一种建议掌握和设置的保底方式。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;diao-zheng-chao-shi-she-zhi&quot;&gt;调整超时设置&lt;&#x2F;h3&gt;
&lt;p&gt;为了更好地控制 Playwright 的行为，你可以配置各种超时设置。调整这些超时值可以帮助你避免由于应用程序响应时间慢或网络延迟而导致的 flaky tests。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;测试超时&lt;&#x2F;strong&gt;：通过 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-configuration#test-timeout&quot;&gt;testTimeout&lt;&#x2F;a&gt; 配置单个测试的超时时间。例如：&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;60000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 单个测试的超时时间为 60 秒
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;浏览器上下文&lt;&#x2F;strong&gt;：配置 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-browser#browser-launch&quot;&gt;&lt;code&gt;launch&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; 方法的 &lt;code&gt;timeout&lt;&#x2F;code&gt; 参数以控制启动浏览器的超时时间：&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    headless: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;30000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 启动浏览器的超时时间为 30 秒
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;})();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;等待页面加载&lt;&#x2F;strong&gt;：通过 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page#page-goto&quot;&gt;&lt;code&gt;page.goto()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; 的 &lt;code&gt;timeout&lt;&#x2F;code&gt; 参数控制页面加载的超时时间：&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;localhost:3000&#x2F;products&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;  timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;45000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 页面加载的超时时间为 45 秒
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;调整这些超时配置确保了 Playwright 在不同情景中能够满足你测试的实际情况，从而减少了由于超时问题导致的 flaky tests。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bao-chi-ce-shi-shu-ju-du-li-xing&quot;&gt;保持测试数据独立性&lt;&#x2F;h3&gt;
&lt;p&gt;在 E2E 测试中，数据隔离是一个关键问题。共享状态会导致意外行为，尤其是在测试用例之间共享同一数据集时。为避免此问题，你应该确保每个测试都在干净和独立的状态下执行。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 提供了内置的 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-fixtures&quot;&gt;Fixtures&lt;&#x2F;a&gt; 支持，可以用于确保每个测试都有独立的测试数据。例如，你可以使用 Fixtures 来创建测试用户并在每个测试之后清除它们。&lt;&#x2F;p&gt;
&lt;p&gt;假设你有一个包含用户登录的测试集。你可以通过以下方式设置 Fixtures， 这样每个测试都会独立运行：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 创建一个新的用户对象
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 注册新的测试用户
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;localhost:3000&#x2F;register&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;input[name=&amp;quot;username&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testuser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;input[name=&amp;quot;password&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password123&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button[type=&amp;quot;submit&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在测试中使用新用户
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;({ username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testuser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password123&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 清除测试用户
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;localhost:3000&#x2F;admin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button[aria-label=&amp;quot;Delete testuser&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;User can login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;user &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;localhost:3000&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;input[name=&amp;quot;username&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;input[name=&amp;quot;password&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button[type=&amp;quot;submit&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;localhost:3000&#x2F;dashboard&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;通过确保测试数据的独立性，你可以避免由于共享状态导致的 flaky tests。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;he-li-shi-yong-browsercontext&quot;&gt;合理使用 BrowserContext&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-browsercontext&quot;&gt;&lt;code&gt;BrowserContext&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; 是 Playwright 中的一个强大概念。它允许在单个浏览器实例中创建独立的浏览器会话。这意味着你可以并行运行多个测试而不会相互干扰。&lt;&#x2F;p&gt;
&lt;p&gt;通过为每个测试创建新的 &lt;code&gt;BrowserContext&lt;&#x2F;code&gt;，你可以确保测试的独立性。例如：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run tests in isolated context&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 为第一个测试创建新的 BrowserContext
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context1&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page1&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;localhost:3000&#x2F;products&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 执行第一个测试的操作...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 为第二个测试创建新的 BrowserContext
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context2&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page2&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;localhost:3000&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 执行第二个测试的操作...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;通过使用 &lt;code&gt;BrowserContext&lt;&#x2F;code&gt;，你可以在单个浏览器实例中创建多个隔离的会话，从而减少 flaky tests 的发生。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xiao-jie&quot;&gt;小结&lt;&#x2F;h2&gt;
&lt;p&gt;编写稳定的 E2E 测试是确保持续交付流水线高效和可靠的关键。虽然 Playwright 是一个强大的测试工具，但它也容易受到 flaky tests 的影响。然而，通过遵循本文中讨论的最佳实践，你可以显著减少 flaky tests 并得到更一致和可靠的测试结果。&lt;&#x2F;p&gt;
&lt;p&gt;总而言之，避免 flaky tests 的关键在于：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;通过本地反复的检查和调试，多次运行测试用例来进行预防。&lt;&#x2F;li&gt;
&lt;li&gt;使用 Playwright 的内置重试策略和调整超时配置。&lt;&#x2F;li&gt;
&lt;li&gt;避免硬等待，使用 Playwright 的 &lt;code&gt;waitFor&lt;&#x2F;code&gt; 方法。&lt;&#x2F;li&gt;
&lt;li&gt;利用 Playwright 的定位器(locator)，而非静态选择器(css&#x2F;xpath selector)。&lt;&#x2F;li&gt;
&lt;li&gt;保持测试数据的独立性。&lt;&#x2F;li&gt;
&lt;li&gt;合理使用 BrowserContext 创建独立的浏览器会话。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这些策略将帮助你编写更健壮和可靠的 E2E 测试，从而提高 CI&#x2F;CD 流水线的效率和稳定性。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zuo-zhe-alexandra-zlate&quot;&gt;作者：Alexandra Zlate&lt;&#x2F;h2&gt;
&lt;p&gt;Alexandra 是 Test Automation 的成员。她热衷于帮助团队提高测试自动化的成熟度并使用最佳实践。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;semaphoreci.com&#x2F;blog&#x2F;flaky-tests-playwright&quot;&gt;URL Source&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布日期: 2024-03-13&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>全面的基于 Playwright 的ui自动化测试，使用页面对象模型的模块化框架</title>
        <published>2024-03-12T00:00:00+00:00</published>
        <updated>2024-03-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/comprehensive-playwright-powered-end-to-end-testing-modular-framework-using-page-object-model-5143db40a15b/"/>
        <id>https://playwright.itest.info/blog/comprehensive-playwright-powered-end-to-end-testing-modular-framework-using-page-object-model-5143db40a15b/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/comprehensive-playwright-powered-end-to-end-testing-modular-framework-using-page-object-model-5143db40a15b/">&lt;h2 id=&quot;guan-jian-te-xing&quot;&gt;关键特性&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;模块化设计&lt;&#x2F;strong&gt;：通过为“操作”和“断言”创建单独的类，促进代码的重用性和可维护性。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;数据生成&lt;&#x2F;strong&gt;：支持使用工具函数和 API 调用生成测试数据。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;页面对象模型&lt;&#x2F;strong&gt;：用于维护页面元素定位器的通用类。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Fixture&lt;&#x2F;strong&gt;：提供一致且高效的统一配置和钩子函数。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;he-xin-zu-jian-jie-shao&quot;&gt;核心组件介绍&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;ye-mian-dui-xiang-mo-xing-pom&quot;&gt;页面对象模型（POM）&lt;&#x2F;h3&gt;
&lt;p&gt;页面对象模型是一种设计模式，鼓励更好的测试维护并减少代码重复。它将页面细节抽象为对象，使测试人员能够编写更具可读性和更强健的测试用例，尤其适用于 Web 应用程序测试。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ye-mian-dui-xiang-pages&quot;&gt;页面对象（pages&#x2F;）&lt;&#x2F;h3&gt;
&lt;p&gt;页面对象代表被测试的 Web 应用程序的页面元素定位符。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cao-zuo-actions&quot;&gt;操作（actions&#x2F;）&lt;&#x2F;h3&gt;
&lt;p&gt;我们为操作创建了单独的类，这些操作使用页面对象与 UI 进行交互。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;duan-yan-assertions&quot;&gt;断言（assertions&#x2F;）&lt;&#x2F;h3&gt;
&lt;p&gt;我们为断言创建了单独的类，这些断言在执行操作后验证应用程序的状态，确保应用程序表现如预期。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;gong-ju-utils&quot;&gt;工具（utils&#x2F;）&lt;&#x2F;h3&gt;
&lt;p&gt;工具函数提供可在测试中重复使用的通用功能，例如生成随机图书数据。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*5qFbO9ZPFhouSWgPwAeE-Q.png&quot; alt=&quot;Image 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ce-shi-yong-li-tests-e2e&quot;&gt;测试用例（tests&#x2F;e2e&#x2F;）&lt;&#x2F;h3&gt;
&lt;p&gt;测试用例定义 ui 自动化的测试场景，聚焦于应用程序的特定功能或特性。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;用例 1&lt;&#x2F;strong&gt;：在此测试中，我们调用“generateRandomBookData”函数生成测试数据，并将其作为参数传递到测试中。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;fixtures&#x2F;addBooks_fixture.js&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;generateRandomBookData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;utils&#x2F;generateRandomBookData.js&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;serial&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;添加图书功能&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isbn&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;genre&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在所有测试之前生成一次图书数据
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;generateRandomBookData&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    ({ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isbn&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;genre&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;summary &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookData&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 解构并分配给局部变量
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:3000&#x2F;books&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;验证-&amp;gt; 新书添加&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;addBookActions&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;addBookAssertions &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在我们的测试中使用生成的图书数据
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addBookAssertions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;nodataValidationisPresent&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addBookActions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;clickaddBook&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;genre&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isbn&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addBookAssertions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;verifyBookInTable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addBookAssertions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;nodataValidationisNotPresent&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;用例 2&lt;&#x2F;strong&gt;：在此测试中，当我们无法在 UI 中生成数据，并且需要通过 API 调用获取测试数据时，可以在 before hooks 中调用 API。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;fixtures&#x2F;updateBooks_fixture.js&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;generateRandomBookData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;utils&#x2F;generateRandomBookData.js&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addBookViaAPI &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;requests&#x2F;addBooks.js&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;serial&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;查看更多，编辑，删除功能&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用requests&#x2F;addBooks.js文件生成图书数据
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;addBookViaAPI&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;generateRandomBookData&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:3000&#x2F;books&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;验证-&amp;gt; 查看更多功能&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;updateBookActions &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateBookActions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;clickviewMore&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;验证-&amp;gt; 编辑功能&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateBookActions&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateBookAssertions&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  }) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateBookActions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;clickeditBook&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateBookAssertions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;verifyUpdatedBookInTable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;_editText&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;验证-&amp;gt; 删除功能&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateBookActions&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateBookAssertions&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  }) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateBookActions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;clickdeleteBook&lt;&#x2F;span&gt;&lt;span&gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 删除在此测试中添加的书籍
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateBookAssertions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;verifyBookNotIntable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;_editText&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateBookActions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;clickdeleteBook&lt;&#x2F;span&gt;&lt;span&gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 删除剩余的另一书籍
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateBookAssertions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;nodataValidationisPresent&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;tong-guo-fixture-zeng-qiang-ce-shi&quot;&gt;通过 Fixture 增强测试&lt;&#x2F;h3&gt;
&lt;p&gt;Fixture 管理测试的配置和解变量的过程，初始化数据、配置测试环境或分配测试可能需要的资源。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-yong-fixture-de-hao-chu&quot;&gt;使用 Fixture 的好处&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;一致性&lt;&#x2F;strong&gt;：确保每个测试在相同条件下运行。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;效率&lt;&#x2F;strong&gt;：智能管理资源，加快测试过程。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;shi-xian-fixture&quot;&gt;实现 Fixture&lt;&#x2F;h3&gt;
&lt;p&gt;我们的框架利用 Playwright 的 Fixture 来增强测试工作流，管理 BrowserContext、页面对象、操作和断言的自定义 Fixture。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseTest &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;AddBookActions &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;pages&#x2F;addBooks&#x2F;addBooks_actions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;AddBooksAssertions &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;pages&#x2F;addBooks&#x2F;addBooks_assertions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;AddBookPageObjects &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;pages&#x2F;addBooks&#x2F;addBooks_pageObjects&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseTest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;addBookActions&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;AddBookActions(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;AddBookPageObjects(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;)));
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;addBookAssertions&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;AddBooksAssertions(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;AddBookPageObjects(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;)));
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseTest &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;UpdateBookActions &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;pages&#x2F;UpdateBooks&#x2F;UpdateBooks_actions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;UpdateBooksAssertions &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;pages&#x2F;UpdateBooks&#x2F;UpdateBooks_assertions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;UpdateBookPageObjects &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;pages&#x2F;UpdateBooks&#x2F;UpdateBooks_pageObjects&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用附加属性扩展基础测试Fixture以支持UpdateBookActions和UpdateBooksAssertions
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseTest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;updateBookActions&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;UpdateBookActions(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;UpdateBookPageObjects(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;)));
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;updateBookAssertions&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;UpdateBooksAssertions(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;UpdateBookPageObjects(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;)));
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;ce-shi-shu-ju-sheng-cheng&quot;&gt;测试数据生成&lt;&#x2F;h3&gt;
&lt;p&gt;项目使用定义在 utils&#x2F;generateRandomBookData.js 中的工具函数生成随机图书数据。此函数利用 randomatic 包生成随机字符串和数字，然后将它们组合形成图书数据，包括“标题、类型、ISBN 和摘要”。以下是数据生成的简要概述：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;标题&lt;&#x2F;strong&gt;：一个以“Book ”为前缀，后跟 5 个字母和数字组成的字符串。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;类型&lt;&#x2F;strong&gt;：从预定义的类型列表中随机选择。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;ISBN&lt;&#x2F;strong&gt;：一个表示为字符串的随机 13 位数字。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;摘要&lt;&#x2F;strong&gt;：一个包含生成标题的简单字符串。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这些生成的数据用于测试中，以添加、更新或验证应用程序中的书籍，确保测试具有多样性的数据点。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;https:&#x2F;&#x2F;medium.com&#x2F;@kbalaji.kks&#x2F;comprehensive-playwright-powered-end-to-end-testing-modular-framework-using-page-object-model-5143db40a15b&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2024-03-12&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在Playwright中使用分片</title>
        <published>2024-03-01T00:00:00+00:00</published>
        <updated>2024-03-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/using-sharding-with-playwright/"/>
        <id>https://playwright.itest.info/blog/using-sharding-with-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/using-sharding-with-playwright/">&lt;p&gt;大家好，在这篇简短的文章中，我们将探讨如何在 Playwright 中使用分片功能，将我们的测试分配到不同的分片上，以加快测试执行速度并减少 CI 流程中的总体运行时间。让我们开始吧，但首先我想介绍一下 Playwright 中的并行性。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;0*GOWzNZR8Lzry7bA4.jpeg&quot; alt=&quot;Image 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bing-xing-xing&quot;&gt;并行性&lt;&#x2F;h2&gt;
&lt;p&gt;在 Playwright 中，我们可以并行运行测试，这在 playwright.config.js 文件中默认设置为 true。启用此功能后，它会同时运行多个工作进程。单个文件中的测试按顺序在同一工作进程中运行。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*_b_72yxV15dfqt3y5lN3Wg.png&quot; alt=&quot;Image 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;并行测试还会在单独的工作进程中执行，无法共享任何状态或全局变量。还有很多方法可以配置测试以并行运行，如下所述：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;使用 test.describe.configure({ mode: ‘parallel’ });&lt;&#x2F;li&gt;
&lt;li&gt;我们可以使用 fullyParallel: true 来为所有测试启用并行性&lt;&#x2F;li&gt;
&lt;li&gt;或者我们可以为某些项目（如 chromium、firefox、webkit 等）启用 fullyParallel: true&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;要禁用并行性，我们可以通过配置文件将工作进程设置为 1，或者在使用 npx playwright test — workers=1 时从命令行传递相同的参数。&lt;&#x2F;p&gt;
&lt;p&gt;有关并行性的更多信息，请参阅此&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-parallel&quot;&gt;页面&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shen-me-shi-fen-pian&quot;&gt;什么是分片？&lt;&#x2F;h2&gt;
&lt;p&gt;“分片”这个词源自数据库架构概念，在该概念中，我们将大型数据库分成多个块或分片，以分散负载、优化工作负载、提高系统的效率和可扩展性。这有点类似于水平扩展，每个分片作为独立的单元，有自己的资源，并相应地管理数据，以提高查询性能，消除单独数据库服务器处理大量数据时的单点故障。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;您可以在&lt;a href=&quot;https:&#x2F;&#x2F;www.geeksforgeeks.org&#x2F;database-sharding-a-system-design-concept&#x2F;?ref=lbp&quot;&gt;这里&lt;&#x2F;a&gt;阅读更多关于分片的信息，此外，Gaurav Sen 有一个很棒的视频解释了这一点，请参考&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=5faMjKuB9bc&quot;&gt;视频&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;现在，回到 Playwright 世界，这里的分片概念可以用于通过将测试数量分散到多个分片或多台机器上同时运行来提高整体测试执行时间。我们可以使用 shard 参数来实现这一点，即 shard=x&#x2F;y，其中 x 是分片索引，y 是总分片数。&lt;&#x2F;p&gt;
&lt;p&gt;npx playwright test --shard=1&#x2F;4&lt;br &#x2F;&gt;
npx playwright test --shard=2&#x2F;4&lt;br &#x2F;&gt;
npx playwright test --shard=3&#x2F;4&lt;br &#x2F;&gt;
npx playwright test --shard=4&#x2F;4&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;在这里，我们将总测试分成 4 个分片，因此配置的工作进程将分别处理这些分片，以显著减少总体测试执行时间。&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;为了演示，我在本地机器上安装了 playwright，并将使用 tests-examples 文件夹中提供的示例代码进行测试，其中配置了一些测试在 playwright to-do mvc 网站上运行（URL:&lt;&#x2F;em&gt; &lt;a href=&quot;https:&#x2F;&#x2F;demo.playwright.dev&#x2F;todomvc&quot;&gt;&lt;em&gt;https:&#x2F;&#x2F;demo.playwright.dev&#x2F;todomvc&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;em&gt;)。我们有大约 24 个测试，在 3 个浏览器上运行，总共 72 个测试文件，我复制了相同的文件两次，所以 3 个规范文件中有 216 个测试，分别在 3 个浏览器—chromium、firefox 和 webkit 上运行。我根据下图重命名了这些规范文件。&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*tieN5wq7GB8dLZf0VJtTJg.png&quot; alt=&quot;Image 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在 playwright.config.js 中，我设置了以下配置，将并行设置为 true，工作进程数量为 6。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span&gt;fullyParallel: trueworkers: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;6 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这意味着当我运行命令：npx playwright test 时，所有 216 个测试将在我的本地和 CI 中使用 6 个工作进程运行。以下测试在本地机器上使用 6 个工作进程运行。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*L_yIK08NCHk74_SbErufnw.png&quot; alt=&quot;Image 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;现在，如果我运行命令 npx playwright test，并指定总分片数为 4，首先将 216 个测试分为 4 个块，每个块包含 54 个测试。然后，这些 54 个测试将在分片 1 中使用 6 个工作进程执行，54 个测试在分片 2 中使用 6 个工作进程执行，如下所示。由于每个分片将在其自己的代理机器上执行，并行级别更深，我们的测试将显著更快地执行，而不仅仅是使用默认配置的 6 个工作进程。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*tAoX6kbSn1njJs6d3EXLHA.png&quot; alt=&quot;Image 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;但是使用分片时，我们会遇到默认 html 报告选项的报告问题。测试执行后，当我们运行命令&#x27;npx playwright show-report&#x27;时，我们会看到生成的报告只显示最后运行的分片的详细信息，仅包含 54 个测试，如下所示。那么之前分片中执行的其余测试呢？&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*0h9DJZmMPoLbe4QOJ15y5g.png&quot; alt=&quot;Image 8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;为了解决这个问题，当我们使用分片时，需要将报告配置为‘blob’。默认情况下，每个分片会在一个名为 blob-report 的文件夹中生成自己的 blob 报告，然后我们需要合并所有这些分片的 blob 报告，以获取所有测试的综合报告。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  reporter: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;blob&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;要合并多个分片的报告，将 blob 报告文件放入一个目录，例如在我们的例子中&lt;code&gt;all-blob-reports&lt;&#x2F;code&gt;，然后最终 HTML 报告将在 playwright-report 文件夹中可用。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright merge-reports&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --reporter&lt;&#x2F;span&gt;&lt;span&gt; html .&#x2F;all-blob-reports
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;zai-ci-zhong-shi-yong-github-actions-jin-xing-fen-pian&quot;&gt;在 CI 中使用 Github Actions 进行分片&lt;&#x2F;h2&gt;
&lt;p&gt;现在，为了有效地使用分片，我们可以利用 CI 工具如 Github Actions，在这些工具中我们可以轻松设置并在多台代理机器上运行测试。为此，我们需要在 Github Actions 工作流文件中包括以下几点：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;在作业中添加一个带有矩阵选项的策略，包含 shardIndex 和 shardTotal 值。&lt;&#x2F;li&gt;
&lt;li&gt;运行 npx playwright test 命令，指定 shardindex 和 shardTotal 选项。&lt;&#x2F;li&gt;
&lt;li&gt;将单个分片的 blob 报告上传到 Github Actions 工作流工件中，以便 merge-report 作业可以拾取并生成综合 HTML 报告。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Sharding Tests
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;\[ main, master \]
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pull_request&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;\[ main, master \]jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run-tests&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;timeout-minutes&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;60
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;strategy&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;fail-fast&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;matrix&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;shardIndex&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;\[1, 2, 3, 4, 5, 6 \]
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;shardTotal&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;\[6\]
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;checkout@v3
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;setup-node@v3
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;node-version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;20
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Install dependencies
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm ci
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Install Playwright Browsers
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright install --with-deps
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run Playwright tests
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test --shard=${{ matrix.shardIndex }}&#x2F;${{ matrix.shardTotal }}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Upload blob report to GitHub Actions Artifacts
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;upload-artifact@v4
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;blob-report-${{ matrix.shardIndex }}
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;blob-report
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;retention-days&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;对于作业“run-tests”，我们将使用在矩阵下指定的总共 6 个分片，并将 fail-fast 设置为 false。然后我们将使用特定的 shardIndex 和 shardTotal 值执行 npx playwright test，例如 1&#x2F;6、2&#x2F;6 等。最后，一旦分片执行完毕，单个分片的 blob 报告将上传到 actions 工件中，名称为 blob-report 并带有 shardIndex 后缀。&lt;&#x2F;p&gt;
&lt;p&gt;接下来，我们需要添加一个“merge-reports”作业，该作业将依赖于 run-tests 作业。此作业将下载所有单个分片的 blob 报告到一个指定的文件夹中，名为 all-blob-reports，然后运行 merge-reports 命令以获取综合 HTML 报告并将其上传到 GitHub Actions 工件选项卡中。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;merge-reports&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;needs&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;\[run-tests\]runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;checkout@v3
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;setup-node@v3
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;node-version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;18
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Install dependencies
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm ci
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Download blob reports from GitHub Actions Artifacts
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;download-artifact@v4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;all-blob-reports
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pattern&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;blob-report-\*
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;merge-multiple&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Merge into HTML Report
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright merge-reports --reporter html .&#x2F;all-blob-reports
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Upload HTML report
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;upload-artifact@v4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html-report--attempt-${{ github.run\_attempt }}
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;retention-days&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;7
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;这是我的完整工作流文件及其所有作业 👇&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright tests using sharding
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;\[ main, master \]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pull_request&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;\[ main, master \]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run-tests&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;timeout-minutes&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;60
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;strategy&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;fail-fast&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;matrix&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;shardIndex&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;\[1, 2, 3, 4, 5, 6 \]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;shardTotal&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;\[6\]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;checkout@v3
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;setup-node@v3
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;node-version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;20
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Install dependencies
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm ci
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Install Playwright Browsers
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright install --with-deps
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run Playwright tests
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;npx playwright test --shard=${{ matrix.shardIndex }}&#x2F;${{ matrix.shardTotal }}- name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Upload blob report to GitHub Actions Artifacts
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;upload-artifact@v4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;blob-report-${{ matrix.shardIndex }}
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;blob-report
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;retention-days&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;merge-reports&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;needs&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;\[run-tests\]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;checkout@v3
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;setup-node@v3
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;node-version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;18
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Install dependencies
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm ci
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Download blob reports from GitHub Actions Artifacts
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;download-artifact@v4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;all-blob-reports
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pattern&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;blob-report-\*
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;merge-multiple&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Merge into HTML Report
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright merge-reports --reporter html .&#x2F;all-blob-reports
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Upload HTML report
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;upload-artifact@v4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html-report--attempt-${{ github.run\_attempt }}
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;retention-days&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;7
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;一旦工作流文件被推送到 Github，所有总测试：216 将被分布并在我们的案例中 6 台机器上的特定分片机器上运行。如下所示，36 个测试在 run-tests (1,6)机器上执行。同样，其他分片代理机器上也会使用 6 个工作进程执行 36 个测试。一旦所有测试执行完毕，merge report 作业将被触发，然后生成我们的综合 HTML 报告。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*_prdug4U48kZ06C-voWX_Q.png&quot; alt=&quot;Image 9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*8ZCSRXOpVrQaSaTsvzFj1Q.png&quot; alt=&quot;Image 10&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👇 以下是作为工作流运行的一部分上传的工件，单个分片的 blob 报告和最终的综合 html 报告。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*vkESn66s1bdwGXg5MqryBw.png&quot; alt=&quot;Image 11&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;下载并打开报告后，您会发现完整的测试运行报告，👇 其中包含在 6 台分片机器上执行的 216 个测试。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*rVylBxPlPxcdyeJv5LkfNg.png&quot; alt=&quot;Image 12&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这就是您可以在您的 Playwright 设置中使用分片的方法。感谢阅读，希望您觉得这篇文章有用！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.devops.dev&#x2F;using-sharding-with-playwright-a94e54314b75?gi=f1526f73b9b4&quot;&gt;原文&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2024-03-01&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright 视觉测试终极指南</title>
        <published>2024-02-28T00:00:00+00:00</published>
        <updated>2024-02-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/ultimate-guide-visual-testing-playwright/"/>
        <id>https://playwright.itest.info/blog/ultimate-guide-visual-testing-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/ultimate-guide-visual-testing-playwright/">&lt;p&gt;随着 Web 应用的不断完善,确保每次更新不会破坏前端展示变得愈发具有挑战性。面对众多浏览器、设备以及每个组件的无数状态,单元测试可以保证代码的一致性,ui 自动化测试可以确保系统的一致性,但它们都无法捕捉到视觉异常、布局问题或平台兼容性问题。&lt;&#x2F;p&gt;
&lt;p&gt;这就是视觉测试发挥作用的地方。视觉测试会存储被测应用 ui 的截图,并将其与应用的当前状态进行比较。不再有意外的 bug,不再需要在三种不同的浏览器和 12 种不同的 viewport 上进行开发。视觉测试自动化了这个过程,确保你的应用始终看起来完全正确。&lt;&#x2F;p&gt;
&lt;p&gt;本文将帮助你从视觉测试新手成长为专家。即使是专家也能在后面的章节中找到一些好处,因为我们将深入探讨测试、本地开发和 CI&#x2F;CD 流程的独特策略。&lt;&#x2F;p&gt;
&lt;p&gt;本文的实现基于&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;&quot;&gt;Playwright&lt;&#x2F;a&gt;,这是一个出色的开源 ui 自动化测试工具,支持所有浏览器和多种编程语言。它非常适合进行视觉测试。如果你还没有用过 Playwright,你应该尝试一下。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-jue-ce-shi-chang-jian-wen-ti&quot;&gt;视觉测试常见问题&lt;&#x2F;h2&gt;
&lt;p&gt;让我们先探讨一些关于视觉测试的常见问题。如果你只想要代码,可以直接跳到&lt;a href=&quot;https:&#x2F;&#x2F;www.browsercat.com&#x2F;post&#x2F;ultimate-guide-visual-testing-playwright#getting-started-with-visual-testing&quot;&gt;视觉测试入门&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shen-me-shi-shi-jue-ce-shi&quot;&gt;什么是视觉测试?&lt;&#x2F;h3&gt;
&lt;p&gt;视觉测试是一种通过比较应用当前截图与基准图像截图来自动验证应用视觉完整性的做法。它确保代码的更改不会在用户界面中引入意外结果。它还可以用于确保在不同浏览器和设备之间的一致性,以及确保你的应用保持可访问性和美观性。&lt;&#x2F;p&gt;
&lt;p&gt;视觉测试有时也被称为视觉回归测试、自动化视觉测试和截图测试。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-jue-ce-shi-you-shen-me-hao-chu&quot;&gt;视觉测试有什么好处?&lt;&#x2F;h3&gt;
&lt;p&gt;视觉测试可以确保你的应用在快速开发期间保证站点的质量稳定。视觉测试可以捕捉到其他测试方法完全遗漏的 bug。而且由于这个过程是自动化的,视觉测试在回溯和修复 bug 方面可以节省大量的时间和精力。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-jue-ce-shi-de-zui-jia-shi-jian-shi-shen-me&quot;&gt;视觉测试的最佳实践是什么?&lt;&#x2F;h3&gt;
&lt;p&gt;视觉测试是一个简单的过程。你只需要在各种状态下存储应用和组件的截图,然后在代码更改后将这些截图与应用进行比较。只要你定期运行测试并使用良好的策略来更新截图,视觉测试就会非常管用。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-jue-ce-shi-he-dan-yuan-ce-shi-you-shen-me-qu-bie&quot;&gt;视觉测试和单元测试有什么区别?&lt;&#x2F;h3&gt;
&lt;p&gt;单元测试专注于特定代码接口的输入和输出,而视觉测试则关注用户界面本身。单元测试非常适合验证核心行为和边缘情况,但只有视觉测试才能确保用户可以使用这些功能并看到结果。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-jue-ce-shi-he-zi-dong-hua-ce-shi-you-shen-me-qu-bie&quot;&gt;视觉测试和自动化测试有什么区别?&lt;&#x2F;h3&gt;
&lt;p&gt;自动化测试确保应用的视图和组件按预期运行,而视觉测试确保这些视图和组件按预期显示。&lt;&#x2F;p&gt;
&lt;p&gt;端到端测试(自动化测试)使用 DOM 选择器定位元素,而人类使用眼睛。因此,虽然端到端测试可以确认按钮是否可点击,但只有视觉测试才能确认按钮是否可见并且在正确的位置上。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shen-me-shi-hou-ying-gai-shi-yong-shi-jue-ce-shi&quot;&gt;什么时候应该使用视觉测试?&lt;&#x2F;h3&gt;
&lt;p&gt;随着应用的发展,视觉测试变得越来越有价值。在开发初期,你的 UI 处于不断变化中,所以视觉测试并不能提供很大的价值。但一旦你的应用开始稳定下来,视觉测试就成为了改变游戏规则的工具。&lt;&#x2F;p&gt;
&lt;p&gt;我建议逐步为那些几个月没有改变但今天需要修改的视图或组件添加视觉测试。在开始编码之前,为那些不应该因为你当前的工作而改变的组件添加视觉测试。然后你就可以自信地编码了!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-jue-ce-shi-zhi-de-zuo-ma&quot;&gt;视觉测试值得做吗?&lt;&#x2F;h3&gt;
&lt;p&gt;是的,值得。随着 web 应用的发展,视觉测试是提升效能的重要尝试。例如,你可以开发一个测试套件,简单地比较同一个视图在不同浏览器和屏幕尺寸下的表现。有了这些测试,你就可以自信地对应用进行更改,而不需要同时打开三个浏览器。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-jue-ce-shi-zui-hao-de-gong-ju-shi-shen-me&quot;&gt;视觉测试最好的工具是什么?&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;&quot;&gt;Playwright&lt;&#x2F;a&gt;被广泛认为是浏览器自动化和集成测试的最佳测试框架。视觉测试也不例外。凭借强大的配置选项、对所有浏览器和多种编程语言的支持、直观的 API 以及庞大的社区,playwright 就是第一选择。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-jue-ce-shi-ru-men&quot;&gt;视觉测试入门&lt;&#x2F;h2&gt;
&lt;p&gt;让我们从设置你的代码库开始,然后我们将编写一些测试。&lt;&#x2F;p&gt;
&lt;p&gt;这一部分应该不到 10 分钟就能开始。在继续阅读之前,我强烈建议你先完成 playwright 的安装工作,因为在指南的后面部分,你将能够亲自实践一些高级技巧。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-an-zhuang-yi-lai&quot;&gt;1. 安装依赖&lt;&#x2F;h3&gt;
&lt;p&gt;如果你已经安装了 Playwright,可以跳过这一步。&lt;&#x2F;p&gt;
&lt;p&gt;否则,安装你的依赖并创建一个示例测试文件:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Install node dependencies
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; install&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -D&lt;&#x2F;span&gt;&lt;span&gt; @playwright&#x2F;test
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; install&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -D&lt;&#x2F;span&gt;&lt;span&gt; typescript
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Install playwright browsers
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright install
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Create the test file
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -p&lt;&#x2F;span&gt;&lt;span&gt; tests
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;touch&lt;&#x2F;span&gt;&lt;span&gt; tests&#x2F;homepage.spec.ts
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;2-bian-xie-ni-de-di-yi-ge-shi-jue-ce-shi-yong-li&quot;&gt;2. 编写你的第一个视觉测试用例&lt;&#x2F;h3&gt;
&lt;p&gt;打开&lt;code&gt;tests&#x2F;homepage.spec.ts&lt;&#x2F;code&gt;并添加以下代码:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;home page visual test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.browsercat.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;3-yun-xing-ni-de-shi-jue-ce-shi&quot;&gt;3. 运行你的视觉测试&lt;&#x2F;h3&gt;
&lt;p&gt;使用以下命令运行你的测试:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;第一次运行测试时,它们会失败并显示类似这样的错误消息:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Error:&lt;&#x2F;span&gt;&lt;span&gt; A snapshot doesn&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;t exist at {TEST_OUTPUT_PATH}, writing actual.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这是符合预期的。因为 Playwright 之前没有保存过结果。&lt;code&gt;expect(page).toHaveSnapshot()&lt;&#x2F;code&gt;这段没东西可以比较,所以它必须使测试失败。但不用担心,下次运行测试时,Playwright 会将页面的当前状态与之前存储的截图进行比较。&lt;&#x2F;p&gt;
&lt;p&gt;所以再次运行你的测试:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;成功了!你应该看到类似这样的消息:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Running&lt;&#x2F;span&gt;&lt;span&gt; 1 test using 1 worker
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt; passed (6.6s)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果你的测试失败了,花几分钟时间看看能否解决问题。如果不能,请继续阅读。我在本文后面介绍了许多故障排除技巧。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;jie-tu-cun-chu-zai-na-li&quot;&gt;截图存储在哪里?&lt;&#x2F;h4&gt;
&lt;p&gt;默认情况下,测试截图与创建它们的测试文件存储在一起。&lt;&#x2F;p&gt;
&lt;p&gt;当视觉测试失败时,Playwright 会将&quot;之前&quot;图像、&quot;之后&quot;图像和&quot;差异&quot;图像存储在&lt;code&gt;.&#x2F;test-results&lt;&#x2F;code&gt;目录中。这些在后面调试失败的测试时会很有用。&lt;&#x2F;p&gt;
&lt;p&gt;(继续阅读以获取有关设置这些截图位置的建议。你可能不希望测试的截图散布在你的代码库中。)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-geng-xin-ce-shi-jie-tu&quot;&gt;4. 更新测试截图&lt;&#x2F;h3&gt;
&lt;p&gt;太棒了!你的测试现在可以确认页面没有任何变化,但如果你真的想做一些更改怎么办?&lt;&#x2F;p&gt;
&lt;p&gt;要更新你的截图,请使用&lt;code&gt;--update-snapshots&lt;&#x2F;code&gt;(或&lt;code&gt;-u&lt;&#x2F;code&gt;)标志运行你的测试:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -u
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个命令将运行你的测试用例并更新截图并保存当前的截图作为基准图像。如果你现在运行这个命令,所有用例应该正常通过。&lt;&#x2F;p&gt;
&lt;p&gt;警告:这个命令会更新所有截图。你要非常小心,因为这很容易意外更新你不打算更新的截图。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;wo-ru-he-zhi-geng-xin-bu-fen-jie-tu&quot;&gt;我如何只更新部分截图?&lt;&#x2F;h4&gt;
&lt;p&gt;要更新一部分测试的截图,我们可以从 CLI 过滤测试。只有匹配的测试用例会被更新。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Update tests with matching file name
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -u &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;**&#x2F;home*.spec.ts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Update tests with matching test name
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -u --grep &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;home&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --grep-invert &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;zzz&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Updates tests within project
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -u --project &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;chromium&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我们稍后会深入探讨截图管理,但现在,让我们专注于调试...&lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-diao-shi-shi-jue-ce-shi&quot;&gt;5. 调试视觉测试&lt;&#x2F;h3&gt;
&lt;p&gt;我们知道如何更新失败测试的截图,但如果测试因为 bug 而失败时，我们该怎么办?&lt;&#x2F;p&gt;
&lt;p&gt;为了探索这种情况,让我们更新我们的测试用例,使截图比较断言失败:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;home page visual test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, async ({page}) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  await page.goto(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.browsercat.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  await expect(page).toHaveScreenshot({
&lt;&#x2F;span&gt;&lt;span&gt;    &#x2F;&#x2F; crop the screenshot to a specific area
&lt;&#x2F;span&gt;&lt;span&gt;    clip: {x: 0, y: 0, width: 500, height: 500},
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;})&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;现在让我们运行测试,要求 playwright 生成 HTML 报告:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --reporter&lt;&#x2F;span&gt;&lt;span&gt; html
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;运行该命令后,你的浏览器会自动打开测试报告。它应该显示失败的具体结果。&lt;&#x2F;p&gt;
&lt;p&gt;滚动到页面底部,你会看到这个小东西:&lt;&#x2F;p&gt;
&lt;p&gt;视觉测试差异&lt;&#x2F;p&gt;
&lt;p&gt;&quot;差异&quot;视图提供了预期和实际截图之间的鲜明对比。然而,我发现&quot;滑块&quot;视图对于实际修复问题最有用。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;wo-ru-he-jie-shi-chai-yi-shi-tu&quot;&gt;我如何解释&quot;差异&quot;视图?&lt;&#x2F;h4&gt;
&lt;p&gt;&quot;差异&quot;视图显示了预期和实际截图之间的差异。&lt;&#x2F;p&gt;
&lt;p&gt;黄色像素表示截图之间的变化,但它们在允许的差异阈值内。&lt;&#x2F;p&gt;
&lt;p&gt;红色像素超出了允许的差异阈值。默认情况下,即使只有一个红色像素也会导致测试失败。&lt;&#x2F;p&gt;
&lt;p&gt;有关配置这些阈值的建议,请跳转到&lt;a href=&quot;https:&#x2F;&#x2F;www.browsercat.com&#x2F;post&#x2F;ultimate-guide-visual-testing-playwright#make-visual-tests-more-forgiving&quot;&gt;使视觉测试阈值更宽松这篇文章&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;wo-bi-xu-shi-yong-html-bao-gao-ma&quot;&gt;我必须使用 HTML 报告吗?&lt;&#x2F;h4&gt;
&lt;p&gt;这不是必须的(尽管我建议使用)。无论你是否生成 HTML 报告,失败的视觉测试都会自动将&quot;之前&quot;、&quot;之后&quot;和&quot;差异&quot;图像输出到&lt;code&gt;.&#x2F;test-results&lt;&#x2F;code&gt;目录。&lt;&#x2F;p&gt;
&lt;p&gt;我经常在工作时直接在 VSCode 中打开&quot;之前&quot;图像。这样我就可以让浏览器专注于我的测试用例。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;6-shi-yong-ui-mo-shi-jin-xing-kuai-su-shi-jue-ce-shi&quot;&gt;6. 使用 UI 模式进行快速视觉测试&lt;&#x2F;h3&gt;
&lt;p&gt;Playwright 有一个行业领先的&quot;UI 模式&quot;,它使处理测试变得非常简单。&lt;&#x2F;p&gt;
&lt;p&gt;虽然它有许多杀手级功能,但我最喜欢的是它在每一个步骤之后自动去截图的能力。这允许你快速调试你的测试。它还使得在正确的地方插入视觉断言变得非常容易。&lt;&#x2F;p&gt;
&lt;p&gt;要在 UI 模式下运行你的测试,使用以下命令:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --ui
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我鼓励你探索 UI 模式,因为它很有用，而且功能丰富。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;res.cloudinary.com&#x2F;browsercat&#x2F;image&#x2F;upload&#x2F;c_fill,w_768&#x2F;dpr_auto&#x2F;f_auto&#x2F;q_auto&#x2F;v1&#x2F;articles&#x2F;pw-snapshot-ui-mode.png?_a=DATAdtTEZAA0&quot; alt=&quot;图1: Playwright UI模式&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;好了!现在你已经对使用视觉测试有了初步了解,现在让我们带你从白带升级道黑带。在这个过程中,我们将使用一些 Playwright 的高级功能,编写出稳定的测试用例,微调我们的配置文件等等。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ye-mian-jie-tu-vs-yuan-su-jie-tu&quot;&gt;页面截图 vs 元素截图&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright 的视觉测试 API 允许你对整个页面或特定元素进行截图。&lt;&#x2F;p&gt;
&lt;p&gt;但什么时候应该使用前者，什么时候该使用后者呢?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shen-me-shi-hou-ying-gai-shi-yong-ye-mian-jie-tu&quot;&gt;什么时候应该使用页面截图?&lt;&#x2F;h3&gt;
&lt;p&gt;页面截图非常适合验证整个页面是否按预期工作。使用页面截图来测试布局、响应是否正确和可访问性。&lt;&#x2F;p&gt;
&lt;p&gt;但要注意:页面截图的用例可能不是那么稳定。毕竟,如果 viewport 内的任何内容发生变化,整个截图都会失败。我们稍后会介绍最小化这些影响的策略,但现在,明智的做法是将页面截图视为一种强大但粗糙的工具。&lt;&#x2F;p&gt;
&lt;p&gt;你已经看到了页面截图的实际应用。这里是一个例子:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;page snapshot&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.browsercat.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;shen-me-shi-hou-ying-gai-shi-yong-yuan-su-jie-tu&quot;&gt;什么时候应该使用元素截图?&lt;&#x2F;h3&gt;
&lt;p&gt;元素截图,正如你所期望的,专注于单个页面元素。这使它们非常适合测试单独的组件，或者验证元素在特定上下文中行为是否符合预期。元素截图比页面截图更不容易出现问题,但它们需要更多的前期工作来配置。毕竟,它们覆盖的范围比较有限，这也意味着你需要更多的截图才能覆盖与页面截图相同的范围。&lt;&#x2F;p&gt;
&lt;p&gt;以下是一个元素截图的例子:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;element snapshot&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.browsercat.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$button&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;shi-yong-ye-mian-jie-tu&quot;&gt;使用页面截图&lt;&#x2F;h2&gt;
&lt;p&gt;让我们探索一些页面截图的有用功能和常见用例...&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cai-jian-ye-mian-jie-tu&quot;&gt;裁剪页面截图&lt;&#x2F;h3&gt;
&lt;p&gt;有时候，验证测试结果不需要用到整个屏幕。因为屏幕的部分设计常常会变化，这可能导致原本稳定的测试变得不可靠。&lt;&#x2F;p&gt;
&lt;p&gt;在这些情况下,最好将你的截图裁剪到具体的区域。这里有一个例子:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cropped snapshot&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.browsercat.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;width&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;height &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;viewportSize&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; square at the center of the page
&lt;&#x2F;span&gt;&lt;span&gt;    clip: {
&lt;&#x2F;span&gt;&lt;span&gt;      x: (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;width &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      y: (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;height &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      width: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      height: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; top slice, maximum possible width
&lt;&#x2F;span&gt;&lt;span&gt;    clip: { x: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, y: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, width: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;Infinity&lt;&#x2F;span&gt;&lt;span&gt;, height: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;16 &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;dui-zheng-ge-ye-mian-jin-xing-jie-tu-er-bu-shi-dang-qian-ping-mu-jie-tu&quot;&gt;对整个页面进行截图，而不是当前屏幕截图&lt;&#x2F;h3&gt;
&lt;p&gt;默认情况下,Playwright 会对当前屏幕进行截图。&lt;&#x2F;p&gt;
&lt;p&gt;我们也可以对整个页面截图（自动滚动到页面的最下方），具体使用场景是: 例如,如果你要测试一个页面在不同浏览器中看起来是否相同,最简单的解决方案就是对整个页面进行截图。而且对于这种测试,你不会存储之前运行的截图,所以你的用例相对稳定。&lt;&#x2F;p&gt;
&lt;p&gt;以下是如何进行完整页面截图:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;full page snapshot&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.browsercat.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    fullPage: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;zai-jin-xing-ye-mian-jie-tu-zhi-qian-gun-dong&quot;&gt;在进行页面截图之前滚动&lt;&#x2F;h3&gt;
&lt;p&gt;在使用页面截图时,你经常会想在视觉断言之前把页面滚动到某个具体的元素。&lt;&#x2F;p&gt;
&lt;p&gt;以下是方法:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;scroll before snapshot&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.browsercat.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    document
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;querySelector&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#your-element&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      ?.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;scrollIntoView&lt;&#x2F;span&gt;&lt;span&gt;({ behavior: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;instant&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;注意:虽然 Playwright 有一个&lt;code&gt;.scrollIntoViewIfNeeded()&lt;&#x2F;code&gt;方法,但它不会将元素滚动到 viewport 的顶部。所以我推荐上面的解决方案。它将充分利用你的 viewport 并确保你的截图在不同运行之间保持一致。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-yuan-su-jie-tu&quot;&gt;使用元素截图&lt;&#x2F;h2&gt;
&lt;p&gt;元素截图比页面截图更&quot;深入细节&quot;。它们带来了很多力量和灵活性。&lt;&#x2F;p&gt;
&lt;p&gt;让我们探索一些例子...&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ce-shi-yuan-su-jiao-hu-xing&quot;&gt;测试元素交互性&lt;&#x2F;h3&gt;
&lt;p&gt;随着你的组件库的增长,越来越难跟踪库中每个元素的每种状态。&lt;&#x2F;p&gt;
&lt;p&gt;在下面的例子中,我们对表单输入在各种状态下进行截图:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;test(&amp;#39;element states&amp;#39;, async ({page}) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  await page.goto(&amp;#39;https:&#x2F;&#x2F;www.browsercat.com&#x2F;contact&amp;#39;);
&lt;&#x2F;span&gt;&lt;span&gt;  const $textarea = page.locator(&amp;#39;textarea&amp;#39;).first();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  await expect($textarea).toHaveScreenshot();
&lt;&#x2F;span&gt;&lt;span&gt;  await $textarea.hover();
&lt;&#x2F;span&gt;&lt;span&gt;  await expect($textarea).toHaveScreenshot();
&lt;&#x2F;span&gt;&lt;span&gt;  await $textarea.focus();
&lt;&#x2F;span&gt;&lt;span&gt;  await expect($textarea).toHaveScreenshot();
&lt;&#x2F;span&gt;&lt;span&gt;  await $textarea.fill(&amp;#39;Hey, cool cat!&amp;#39;);
&lt;&#x2F;span&gt;&lt;span&gt;  await expect($textarea).toHaveScreenshot();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;ce-shi-yuan-su-xiang-ying-xing&quot;&gt;测试元素响应性&lt;&#x2F;h3&gt;
&lt;p&gt;在处理响应式设计时,确保你的元素在全范围的屏幕尺寸下看起来都很好是很重要的。&lt;&#x2F;p&gt;
&lt;p&gt;使用元素截图来确保你的组件在各种断点下看起来都很好。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;test(&amp;#39;element responsiveness&amp;#39;, async ({page}) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  const viewportWidths = [960, 760, 480];
&lt;&#x2F;span&gt;&lt;span&gt;  await page.goto(&amp;#39;https:&#x2F;&#x2F;www.browsercat.com&#x2F;blog&amp;#39;);
&lt;&#x2F;span&gt;&lt;span&gt;  const $post = page.locator(&amp;#39;main article&amp;#39;).first();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  for (const width of viewportWidths) {
&lt;&#x2F;span&gt;&lt;span&gt;    await page.setViewportSize({width, height: 800});
&lt;&#x2F;span&gt;&lt;span&gt;    await expect($post).toHaveScreenshot(`post-${width}.png`);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;gao-ji-jie-tu-ji-qiao&quot;&gt;高级截图技巧&lt;&#x2F;h2&gt;
&lt;p&gt;页面和元素截图共享许多常见的配置选项。让我们探讨其中最有用的...&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ping-bi-jie-tu-de-bu-fen-nei-rong&quot;&gt;屏蔽截图的部分内容&lt;&#x2F;h3&gt;
&lt;p&gt;有时,你会想要排除截图的某些部分。一个子元素或子区域可能经常变化,包含敏感信息,或者与测试无关。例如,时间戳、动画、用户电子邮件地址或轮换广告。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 提供了&quot;屏蔽&quot;这些区域的能力,用一种不太可能与你网站内容混淆的鲜艳颜色替换它们。&lt;&#x2F;p&gt;
&lt;p&gt;这里有一个例子:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;test(&amp;#39;masked snapshots&amp;#39;, async ({page}) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  await page.goto(&amp;#39;https:&#x2F;&#x2F;www.browsercat.com&amp;#39;);
&lt;&#x2F;span&gt;&lt;span&gt;  const $hero = page.locator(&amp;#39;main &amp;gt; header&amp;#39;);
&lt;&#x2F;span&gt;&lt;span&gt;  const $footer = page.locator(&amp;#39;body &amp;gt; footer&amp;#39;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  await expect(page).toHaveScreenshot({
&lt;&#x2F;span&gt;&lt;span&gt;    mask: [
&lt;&#x2F;span&gt;&lt;span&gt;      $hero.locator(&amp;#39;img[src$=&amp;quot;.svg&amp;quot;]&amp;#39;),
&lt;&#x2F;span&gt;&lt;span&gt;      $hero.locator(&amp;#39;a[target=&amp;quot;_blank&amp;quot;]&amp;#39;),
&lt;&#x2F;span&gt;&lt;span&gt;    ],
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  await expect($footer).toHaveScreenshot({
&lt;&#x2F;span&gt;&lt;span&gt;    mask: [
&lt;&#x2F;span&gt;&lt;span&gt;      $footer.locator(&amp;#39;svg&amp;#39;),
&lt;&#x2F;span&gt;&lt;span&gt;    ],
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这是第一个屏蔽截图的样子:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;res.cloudinary.com&#x2F;browsercat&#x2F;image&#x2F;upload&#x2F;c_fill,w_768&#x2F;dpr_auto&#x2F;f_auto&#x2F;q_auto&#x2F;v1&#x2F;articles&#x2F;pw-snapshot-masked.png?_a=DATAdtTEZAA0&quot; alt=&quot;图2: 屏蔽截图&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zai-jie-tu-qi-jian-bao-chi-yang-shi-bu-bian&quot;&gt;在截图期间保持样式不变&lt;&#x2F;h3&gt;
&lt;p&gt;视觉测试之所以有价值，是因为它们可以捕捉到应用外观的意外变化。但有些页面元素本身就不稳定，无法直接包含在测试中。&lt;&#x2F;p&gt;
&lt;p&gt;幸运的是,我们可以在截图期间加载一些基本的 CSS,以限制或隐藏页面中的有问题元素或者难搞定的元素。&lt;&#x2F;p&gt;
&lt;p&gt;这里是方法:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;consistent styles&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.browsercat.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$hero &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;main &amp;gt; header&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    stylePath: [
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;hide-dynamic-elements.css&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;disable-scroll-animations.css&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    ],
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$hero&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    stylePath: [
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;hide-dynamic-elements.css&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;disable-scroll-animations.css&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    ],
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;zi-dong-zhong-shi-bu-wen-ding-de-yong-li&quot;&gt;自动重试不稳定的用例&lt;&#x2F;h3&gt;
&lt;p&gt;在处理动画或动态内容时，视觉测试可能会变得不稳定。特别是大型页面截图容易受到影响。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 可以在一定时间内自动重试失败的视觉测试，直到找到有效的匹配。启用该功能的方法如下：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;retry snapshots&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.browsercat.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$hero &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;main &amp;gt; header&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; retry snapshot until timeout is reached
&lt;&#x2F;span&gt;&lt;span&gt;    timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1000 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;60&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$hero&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; retry snapshot until timeout is reached
&lt;&#x2F;span&gt;&lt;span&gt;    timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1000 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;60&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;xia-zai-tu-pian-jin-xing-bi-jiao-duan-yan&quot;&gt;下载图片进行比较断言&lt;&#x2F;h3&gt;
&lt;p&gt;在 99.9%的情况下，页面和元素截图可以满足你的需求。但有时你可能需要断言任意图像在不同测试运行之间保持一致。&lt;&#x2F;p&gt;
&lt;p&gt;例如，你的应用可能生成二维码或社交分享卡片，或者你可能需要压缩和转换用户上传的头像。在这些情况下，你会希望确保这些功能运行正常，不会出问题。&lt;&#x2F;p&gt;
&lt;p&gt;对此使用&lt;code&gt;expect().toMatchSnapshot()&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;buffer &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;stream&#x2F;consumers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;arbitrary snapshot&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; generates custom avatars — fun!
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;getavataaars.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;main form button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; download the avatar
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;avatar &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForEvent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;download&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;dl&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dl&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createReadStream&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;stream&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;buffer&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;stream&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;avatar&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toMatchSnapshot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;avatar.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;bi-jiao-bu-tong-liu-lan-qi-zhi-jian-de-jie-tu&quot;&gt;比较不同浏览器之间的截图&lt;&#x2F;h3&gt;
&lt;p&gt;到目前为止，我们编写的所有测试都是比较代码更改前后应用的状态。但如果你想比较应用在不同浏览器和设备之间的状态呢？&lt;&#x2F;p&gt;
&lt;p&gt;为了实现这一点，我们将利用 Playwright 的“项目”功能。项目允许你定义具有独特配置的自定义测试套件。在成熟的代码库中，你可能会有很多这样的项目用于不同的设备、环境和测试策略。&lt;&#x2F;p&gt;
&lt;p&gt;让我们来点魔法！&lt;&#x2F;p&gt;
&lt;p&gt;首先，更新你的 playwright.config.ts。如果你还没有，请在项目根目录创建它：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;crossBrowserConfig &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  testDir: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;tests&#x2F;cross-browser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  snapshotPathTemplate: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.test&#x2F;cross&#x2F;{testFilePath}&#x2F;{arg}{ext}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  expect: {
&lt;&#x2F;span&gt;&lt;span&gt;    toHaveScreenshot: { maxDiffPixelRatio: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.1 &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; other config here...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  projects: [
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cross-chromium&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      use: { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Desktop Chrome&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] },
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;crossBrowserConfig&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cross-firefox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      use: { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Desktop Firefox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] },
&lt;&#x2F;span&gt;&lt;span&gt;      dependencies: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cross-chromium&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;crossBrowserConfig&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cross-browser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      use: { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Desktop Safari&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] },
&lt;&#x2F;span&gt;&lt;span&gt;      dependencies: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cross-firefox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;crossBrowserConfig&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在配置 snapshotPathTemplate 以确保所有浏览器的截图存储在相同的位置后，每个测试都将其截图与相同的源图像进行比较。&lt;&#x2F;p&gt;
&lt;p&gt;接下来，在 .&#x2F;tests&#x2F;cross-browser&#x2F;homepage.spec.ts 创建一个新的测试文件：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cross-browser snapshots&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.browsercat.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;:has(&amp;gt; a figure)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;$el&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$el&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;remove&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;home-page.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    fullPage: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;为了避免失败,让我们初始化我们的新截图:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --project&lt;&#x2F;span&gt;&lt;span&gt; cross-browser&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -u
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然后让我们运行测试:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --project&lt;&#x2F;span&gt;&lt;span&gt; cross-browser
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;你的所有测试都通过了吗？可能并没有，因为在不同环境下，不同的浏览器会以不同方式渲染字体、颜色和图像，即使你的应用表现正常。&lt;&#x2F;p&gt;
&lt;p&gt;如果你的测试失败了，你可能需要调整截图的 &lt;code&gt;maxDiffPixelRatio&lt;&#x2F;code&gt; 和 &lt;code&gt;threshold&lt;&#x2F;code&gt; 选项。想要调试这个问题，请查看&lt;a href=&quot;https:&#x2F;&#x2F;www.browsercat.com&#x2F;post&#x2F;ultimate-guide-visual-testing-playwright#make-visual-tests-more-forgiving&quot;&gt;如何使视觉测试更宽松&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-jue-ce-shi-de-ge-chong-xuan-xiang&quot;&gt;视觉测试的各种选项&lt;&#x2F;h2&gt;
&lt;p&gt;因为视觉测试非常敏感,所以掌握它们的配置很重要。让我们探讨一些最有用的选项...&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zi-ding-yi-jie-tu-wen-jian-ming&quot;&gt;自定义截图文件名&lt;&#x2F;h3&gt;
&lt;p&gt;Playwright 根据测试的名称自动命名你的截图。如果你只将截图用于视觉测试,那就没问题。&lt;&#x2F;p&gt;
&lt;p&gt;然而,许多用户喜欢将这些图像重新用于文档和测试报告。这些任务久需要自定义文件名。&lt;&#x2F;p&gt;
&lt;p&gt;这样命名你的截图:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;custom snapshot names&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.browsercat.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$hero &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;main &amp;gt; header&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;home-page.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$hero&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;home-hero.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$footer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;body &amp;gt; footer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;footImg &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$footer&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;screenshot&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;footImg&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toMatchSnapshot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;home-foot.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;注意:自定义截图名称并不能完全控制文件名,除非你还配置了自定义目录。使用默认配置,Playwright 会在你的文件名后添加一个后缀,以确保它们在不同项目中是唯一的。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zi-ding-yi-jie-tu-mu-lu&quot;&gt;自定义截图目录&lt;&#x2F;h3&gt;
&lt;p&gt;默认情况下,Playwright 将截图存储在创建它们的测试文件所在的目录中。这种方法有许多缺点:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;你的代码库会变得很乱。&lt;&#x2F;li&gt;
&lt;li&gt;很难从版本控制中排除截图文件。&lt;&#x2F;li&gt;
&lt;li&gt;不容易在 CI&#x2F;CD 运行之间缓存结果。&lt;&#x2F;li&gt;
&lt;li&gt;对自定义截图文件名不友好。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;所以让我们告诉 Playwright 将我们的截图存储在一个自定义目录中。更新你的&lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt;文件:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  snapshotPathTemplate: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.test&#x2F;snaps&#x2F;{projectName}&#x2F;{testFilePath}&#x2F;{arg}{ext}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;使用上面的配置,如果我们的测试文件路径是&lt;code&gt;.&#x2F;tests&#x2F;homepage.spec.ts&lt;&#x2F;code&gt;,Playwright 会将我们的截图存储如下:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;.test&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;snaps&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tests&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homepage.spec.ts&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;home-page.png
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;home-hero.png
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;home-foot.png
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;使用这种模式,你可以从版本控制中排除你的截图,可以在 CI&#x2F;CD 中缓存你的结果,并且可以在它们出现时轻松查看失败的测试。&lt;&#x2F;p&gt;
&lt;p&gt;阅读 Playwright 文档中关于&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-testconfig#test-config-snapshot-path-template&quot;&gt;截图路径模板&lt;&#x2F;a&gt;中提供的更多选项。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-shi-jue-ce-shi-geng-kuan-song&quot;&gt;使视觉测试更宽松&lt;&#x2F;h3&gt;
&lt;p&gt;默认情况下,视觉测试非常严格。如果有一个像素失败,你的测试就会失败。幸运的是,Playwright 提供了多个选项来调整你的视觉测试的敏感度。&lt;&#x2F;p&gt;
&lt;p&gt;具体选项有:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;threshold&lt;&#x2F;code&gt;: 单个像素必须变化多少才被认为是不同的。值是从&lt;code&gt;0&lt;&#x2F;code&gt;到&lt;code&gt;1&lt;&#x2F;code&gt;的百分比,默认为&lt;code&gt;0.2&lt;&#x2F;code&gt;。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;maxDiffPixels&lt;&#x2F;code&gt;: 在测试仍然通过的情况下可以不同的最大像素数。默认情况下,此选项是禁用的。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;maxDiffPixelRatio&lt;&#x2F;code&gt;: 在测试仍然通过的情况下可以不同的最大像素百分比。值是从&lt;code&gt;0&lt;&#x2F;code&gt;到&lt;code&gt;1&lt;&#x2F;code&gt;的百分比,但默认情况下此选项是禁用的。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;你可以全局或基于每个断言去调整这些选项。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;quan-ju-pei-zhi-yu-zhi&quot;&gt;全局配置阈值&lt;&#x2F;h4&gt;
&lt;p&gt;还是在&lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt;文件:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  expect: {
&lt;&#x2F;span&gt;&lt;span&gt;    toHaveScreenshot: {
&lt;&#x2F;span&gt;&lt;span&gt;      threshold: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.25&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      maxDiffPixelRatio: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.025&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      maxDiffPixels: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;25&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    toMatchSnapshot: {
&lt;&#x2F;span&gt;&lt;span&gt;      threshold: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.25&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      maxDiffPixelRatio: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.025&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      maxDiffPixels: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;25&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;zhong-xie-mei-ge-duan-yan-de-yu-zhi&quot;&gt;重写每个断言的阈值&lt;&#x2F;h4&gt;
&lt;p&gt;这样做可以重写全局截图设置的值:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;forgiving snapshots&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.browsercat.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$hero &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;main &amp;gt; header&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    maxDiffPixelRatio: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.15&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$hero&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveScreenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    maxDiffPixels: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;wo-ru-he-diao-zheng-zhe-xie-xuan-xiang&quot;&gt;我如何调整这些选项?&lt;&#x2F;h4&gt;
&lt;p&gt;要找到适合你应用的最佳设置，请仔细查看测试生成的&quot;差异&quot;图像。在这些图像中，黄色像素表示在可接受范围内的差异，而红色像素则表示导致测试失败的差异。&lt;&#x2F;p&gt;
&lt;p&gt;首先，尝试提高&lt;code&gt;threshold&lt;&#x2F;code&gt;值，看是否能消除截图中的红色像素。但要谨慎：过高的&lt;code&gt;threshold&lt;&#x2F;code&gt;可能会导致错误地忽略重要差异。建议不要将其设置超过&lt;code&gt;0.35&lt;&#x2F;code&gt;，这已经是相当高的值了。&lt;&#x2F;p&gt;
&lt;p&gt;如果调整&lt;code&gt;threshold&lt;&#x2F;code&gt;没有效果，就重点关注&lt;code&gt;maxDiffPixelRatio&lt;&#x2F;code&gt;和&lt;code&gt;maxDiffPixels&lt;&#x2F;code&gt;这两个参数。在使用这些选项时，要权衡利弊。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;maxDiffPixelRatio&lt;&#x2F;code&gt;是相对于图像大小的比例，因此更可能在各种不同大小的图像中都能产生良好的结果。这使它成为全局设置的理想选择——但前提是要谨慎设置！毕竟，允许整个页面图像 10%的差异可能会导致过大的变化被忽视。&lt;&#x2F;p&gt;
&lt;p&gt;相比之下，&lt;code&gt;maxDiffPixels&lt;&#x2F;code&gt;是一个固定的像素值，可以让你更精确地控制单个测试。但如果将其应用于全局设置，即使数值很小也可能带来风险。对于小图像来说，较高的&lt;code&gt;maxDiffPixels&lt;&#x2F;code&gt;可能会占据图像的很大比例。&lt;&#x2F;p&gt;
&lt;p&gt;在调整这些参数时，要根据你的具体需求和图像特征，找到最佳平衡点。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ci-cd-zhong-de-shi-jue-ce-shi&quot;&gt;CI&#x2F;CD 中的视觉测试&lt;&#x2F;h2&gt;
&lt;p&gt;太棒了!你已经在本地成功运行了视觉测试,并对结果很有信心。现在是时候将这些测试整合到你的 CI&#x2F;CD 流程中了。&lt;&#x2F;p&gt;
&lt;p&gt;你可能会觉得这听起来不太容易。毕竟,你需要一种方法在不同的测试运行期间保存截图,还需要让你的 CI&#x2F;CD 流程知道何时应该更新截图缓存。&lt;&#x2F;p&gt;
&lt;p&gt;不过别担心,我已经为你准备好了解决方案。:)&lt;&#x2F;p&gt;
&lt;p&gt;在这个例子中,我们假设你使用的是 GitHub Actions。不过即使你使用其他 CI&#x2F;CD 服务提供商,操作步骤也大同小异。&lt;&#x2F;p&gt;
&lt;p&gt;那么,让我们开始吧!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-wei-ci-cd-pei-zhi-playwright&quot;&gt;1. 为 CI&#x2F;CD 配置 Playwright&lt;&#x2F;h3&gt;
&lt;p&gt;你的 CI&#x2F;CD 环境与本地开发环境存在差异,我们需要在 Playwright 配置中对此作出相应调整。&lt;&#x2F;p&gt;
&lt;p&gt;请使用以下设置 来更新你的 &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; 文件。你可以将这些新选项与你之前设置的其他选项自由组合:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isCI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= !!&lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1000 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;60&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  workers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isCI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;50%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  retries: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isCI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  forbidOnly: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isCI&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  outputDir: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.test&#x2F;spec&#x2F;output&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  snapshotPathTemplate:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.test&#x2F;spec&#x2F;snaps&#x2F;{projectName}&#x2F;{testFilePath}&#x2F;{arg}{ext}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  testMatch: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;*.spec.{ts,tsx}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  reporter: [
&lt;&#x2F;span&gt;&lt;span&gt;    [
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      {
&lt;&#x2F;span&gt;&lt;span&gt;        outputFolder: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.test&#x2F;spec&#x2F;results&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        open: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;never&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    ],
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isCI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;github&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;line&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这些配置选项主要考虑了以下几个方面:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;CI 环境资源限制:
CI 环境通常资源有限,这会导致测试运行速度变慢,稳定性降低。因此,我们需要调整相关参数来适应这种情况。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;测试输出整理:
我们将所有测试输出都组织到&lt;code&gt;.test&lt;&#x2F;code&gt;目录中,这样可以方便进行缓存管理。记得将&lt;code&gt;.test&#x2F;&lt;&#x2F;code&gt;添加到你的&lt;code&gt;.gitignore&lt;&#x2F;code&gt;文件中,因为这些内容不应该提交到代码仓库。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;报告生成:
在 CI&#x2F;CD 环境中,我们会生成两种类型的报告:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;html&lt;&#x2F;code&gt;报告: 这种报告对于深入调试非常有用。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;github&lt;&#x2F;code&gt;报告: 这种报告提供简洁的输出,便于快速查看结果。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;2-zai-ci-cd-zhong-yun-xing-ni-de-ce-shi&quot;&gt;2. 在 CI&#x2F;CD 中运行你的测试&lt;&#x2F;h3&gt;
&lt;p&gt;接下来,让我们创建一个基本的流水线。它还不能做所有事情,但它是一个很好的快速开始模版。&lt;&#x2F;p&gt;
&lt;p&gt;在&lt;code&gt;.github&#x2F;workflows&#x2F;visual-tests.yml&lt;&#x2F;code&gt;创建一个文件:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Visual Tests
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pull_request&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run-tests&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;timeout-minutes&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;15
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Checkout
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;checkout@v4
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Set up Node
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;setup-node@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;cache&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pnpm
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;node-version-file&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.nvmrc
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;check-latest&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Install deps
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          npm install
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          npx playwright install
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Test
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test --ignore-snapshots
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;注意我们使用&lt;code&gt;--ignore-snapshots&lt;&#x2F;code&gt;标志运行测试。由于我们没有办法在测试运行之间存储截图,这实际上是让我们的测试通过的唯一方法。&lt;&#x2F;p&gt;
&lt;p&gt;如果你现在将你的代码推送到 Github,工作流将直接运行,并且它会通过,但它不会做任何的测试验证。&lt;&#x2F;p&gt;
&lt;p&gt;让我们来解决这个问题...&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-zai-yun-xing-zhi-jian-cun-chu-jie-tu&quot;&gt;3. 在运行之间存储截图&lt;&#x2F;h3&gt;
&lt;p&gt;为了确保我们的视觉测试能够正常运行，我们需要在不同的测试运行之间保存截图。为此，我们将利用 GitHub 的&quot;artifacts&quot;功能来缓存这些截图。&lt;&#x2F;p&gt;
&lt;p&gt;其次，我们还要编写一个步骤，用于在截图不存在时生成新的截图。这样可以解决我们的测试在新分支上首次运行时可能出现的失败问题。&lt;&#x2F;p&gt;
&lt;p&gt;最后，我们将重新启用截图断言，让视觉测试发挥其应有的作用。&lt;&#x2F;p&gt;
&lt;p&gt;以下是具体的更新内容：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Install deps
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    npm install
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    npx playwright install
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Set up cache
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cache
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;cache@v4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cache&#x2F;${{github.repository}}&#x2F;${{github.ref}}
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;restore-keys&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cache&#x2F;${{github.repository}}&#x2F;refs&#x2F;heads&#x2F;master
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.test&#x2F;**
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Initialize snapshots
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{steps.cache.outputs.cache-hit != &amp;#39;true&amp;#39;}}
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test --update-snapshots
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Test
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;请注意，我们的缓存键不仅包含当前分支的信息，还包括了对&lt;code&gt;master&lt;&#x2F;code&gt;分支的回退机制。这样设计的好处是，当新分支首次推送时，它也能有一些基本的东西可以使用。&lt;&#x2F;p&gt;
&lt;p&gt;如果你现在触发 CI&#x2F;CD 流程，你会观察到以下情况：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在第一次运行时：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;系统会生成新的截图&lt;&#x2F;li&gt;
&lt;li&gt;测试会通过&lt;&#x2F;li&gt;
&lt;li&gt;缓存会被更新&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;在后续的运行中：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;系统会命中缓存&lt;&#x2F;li&gt;
&lt;li&gt;不会生成新的截图&lt;&#x2F;li&gt;
&lt;li&gt;测试仍然会通过&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;到目前为止，一切看起来都很顺利。但是，我们还需要考虑一个重要问题：当截图需要更新时，我们该如何处理呢？&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-hong-fa-jie-tu-geng-xin&quot;&gt;4. 触发截图更新&lt;&#x2F;h3&gt;
&lt;p&gt;现在我们有了真正有效的视觉测试,让我们更新我们的工作流,以便我们可以按需更新截图。&lt;&#x2F;p&gt;
&lt;p&gt;首先,让我们参数化我们的工作流...&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pull_request&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Allow updating snapshots during manual runs
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;workflow_call&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;inputs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;update-snapshots&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Update snapshots?&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;boolean
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Allow updating snapshots during automatic runs
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;workflow_dispatch&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;inputs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;update-snapshots&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Update snapshots?&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;boolean
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;workflow_call&lt;&#x2F;code&gt;允许我们手动触发工作流,选择是否更新截图。&lt;code&gt;workflow_dispatch&lt;&#x2F;code&gt;允许其他 Github 工作流使用相同的配置。&lt;&#x2F;p&gt;
&lt;p&gt;以下是手动小组件的样子:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;res.cloudinary.com&#x2F;browsercat&#x2F;image&#x2F;upload&#x2F;c_fill,w_768&#x2F;dpr_auto&#x2F;f_auto&#x2F;q_auto&#x2F;v1&#x2F;articles&#x2F;pw-snapshot-github-trigger.png?_a=DATAdtTEZAA0&quot; alt=&quot;图3: 手动Github工作流&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;让我们对&quot;Initialize snapshots&quot;（初始化截图）步骤进行更新，使其能够在特定参数启用时触发截图更新。具体更新如下：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Initialize snapshots
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{steps.cache.outputs.cache-hit != &amp;#39;true&amp;#39; || inputs.update-snapshots == &amp;#39;true&amp;#39;}}
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test --update-snapshots --reporter html
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;最后，让我们把生成的 HTML 报告作为 artifact 上传。这样做有两个重要的好处：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;在决定是否需要更新工作流中的截图之前，我们可以先参考这份报告。&lt;&#x2F;li&gt;
&lt;li&gt;如果遇到问题需要调试，这份报告也会成为很有价值的参考资料。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Test
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;continue-on-error&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Upload test report
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;upload-artifact@v4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.test&#x2F;spec&#x2F;results&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;retention-days&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;30
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;overwrite&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;注意我们的&quot;Test&quot;步骤现在启用了&lt;code&gt;continue-on-error&lt;&#x2F;code&gt;,这样即使测试失败,我们也可以上传我们的报告。&lt;&#x2F;p&gt;
&lt;p&gt;我们新增了一个&quot;Upload test report&quot;（上传测试报告）步骤，它的作用是将报告作为 artifact 上传。这样做之后，你会发现在工作流的结果页面中出现了一个新的链接。这个链接会直接指向一个 zip 文件，里面包含了我们刚刚生成的测试报告。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;res.cloudinary.com&#x2F;browsercat&#x2F;image&#x2F;upload&#x2F;c_fill,w_768&#x2F;dpr_auto&#x2F;f_auto&#x2F;q_auto&#x2F;v1&#x2F;articles&#x2F;pw-snapshot-github-artifact.png?_a=DATAdtTEZAA0&quot; alt=&quot;图4: Github工作流报告&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-da-fu-ti-sheng-ni-de-ci-cd-liu-shui-xian-su-du&quot;&gt;5. 大幅提升你的 CI&#x2F;CD 流水线速度！&lt;&#x2F;h3&gt;
&lt;p&gt;目前一切进展顺利，但我们还能做一件事让视觉测试更上一层楼：&lt;em&gt;让它们飞速运行&lt;&#x2F;em&gt;！&lt;&#x2F;p&gt;
&lt;p&gt;你可能已经注意到两个步骤正在拖慢你的流水线：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;安装 Playwright 浏览器。&lt;&#x2F;li&gt;
&lt;li&gt;串行运行测试。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;幸运的是，我们可以不用花钱而很容易的解决这两个问题。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.browsercat.com&#x2F;hosted-playwright&quot;&gt;BrowserCat&lt;&#x2F;a&gt; 在云端托管了一批 Playwright 浏览器，你只需几行代码就能连接使用。&lt;&#x2F;p&gt;
&lt;p&gt;如果你这样做，就不必在 CI&#x2F;CD 环境中安装浏览器，而且可以完全并行运行测试。&lt;&#x2F;p&gt;
&lt;p&gt;最棒的是？BrowserCat 提供了一个&lt;em&gt;超赞的&lt;&#x2F;em&gt;永久免费计划。除非你有一个庞大的团队，否则你可能永远不用花一分钱。&lt;&#x2F;p&gt;
&lt;p&gt;那么让我们开始吧！&lt;&#x2F;p&gt;
&lt;p&gt;首先，在 &lt;a href=&quot;https:&#x2F;&#x2F;app.browsercat.com&#x2F;&quot;&gt;BrowserCat&lt;&#x2F;a&gt; 注册一个免费账户。&lt;&#x2F;p&gt;
&lt;p&gt;其次，创建一个 &lt;a href=&quot;https:&#x2F;&#x2F;app.browsercat.com&#x2F;keys&quot;&gt;API 密钥&lt;&#x2F;a&gt;，并将其作为名为 &lt;code&gt;BROWSERCAT_API_KEY&lt;&#x2F;code&gt; 的秘密存储在你的 Github 仓库中。你可以通过导航到你的仓库，点击&quot;Settings&quot;，然后&quot;Secrets&quot;，再点&quot;Actions&quot;，最后点&quot;New repository secret&quot;来完成这一步。&lt;&#x2F;p&gt;
&lt;p&gt;第三，让我们更新 &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; 以使用 BrowserCat：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isCI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= !!&lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;useBC &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= !!&lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;BROWSERCAT_API_KEY&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1000 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;60&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  workers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;useBC &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isCI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;50%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  retries: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;useBC &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isCI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  maxFailures: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;useBC &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp;&amp;amp; !&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isCI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  forbidOnly: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isCI&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  use: {
&lt;&#x2F;span&gt;&lt;span&gt;    connectOptions: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;useBC &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      wsEndpoint: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;wss:&#x2F;&#x2F;api.browsercat.com&#x2F;connect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      headers: {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Api-Key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;BROWSERCAT_API_KEY&lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在上面的更新中，你会注意到我们在使用 BrowserCat 时将并行化增加到了 10 个工作进程。这是一个不错的起点，但你可以毫无问题地大幅增加这个数值。&lt;&#x2F;p&gt;
&lt;p&gt;注意，只要定义了 &lt;code&gt;BROWSERCAT_API_KEY&lt;&#x2F;code&gt;，这个配置就会连接到 BrowserCat。这对于在本地和 CI&#x2F;CD 中运行测试都很有用。&lt;&#x2F;p&gt;
&lt;p&gt;好了，现在是最后一点魔法...让我们停止每次运行流水线时安装 Playwright 的浏览器：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Install deps
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm install
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果你现在运行你的流水线，即使你的测试套件中只有一个测试，你也会看到显著的速度提升。而且每月 1000 个免费积分，你可以连续运行流水线数小时，还有大量时间剩余。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-xia-lai-de-bu-zou&quot;&gt;接下来的步骤...&lt;&#x2F;h2&gt;
&lt;p&gt;呼！你已经走了很长的路。&lt;&#x2F;p&gt;
&lt;p&gt;到这一步，你能做的唯一一件提高视觉测试技能的事就是练习。赶紧去截图吧！&lt;&#x2F;p&gt;
&lt;p&gt;这里有一些很棒的链接可以帮助你：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-snapshots&quot;&gt;Playwright 视觉比对&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-pageassertions#page-assertions-to-have-screenshot-1&quot;&gt;expect(page).toHaveScreenshot()&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locatorassertions#locator-assertions-to-have-screenshot-1&quot;&gt;expect(locator).toHaveScreenshot()&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-snapshotassertions#snapshot-assertions-to-match-snapshot-1&quot;&gt;expect(image).toMatchSnapshot()&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-testconfig#test-config-expect&quot;&gt;TestConfig.expect&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.browsercat.com&#x2F;blog&quot;&gt;BrowserCat 博客&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;祝你自动化测试愉快！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;www.browsercat.com&#x2F;post&#x2F;ultimate-guide-visual-testing-playwright&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2024-02-28&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>提升自动化测试：当 Playwright 遇上 ChatGPT</title>
        <published>2024-02-27T00:00:00+00:00</published>
        <updated>2024-02-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/elevating-automated-testing-playwright-meets-chatgpt/"/>
        <id>https://playwright.itest.info/blog/elevating-automated-testing-playwright-meets-chatgpt/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/elevating-automated-testing-playwright-meets-chatgpt/">&lt;p&gt;由 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;christine-pinto&#x2F;&quot;&gt;Christine Pinto&lt;&#x2F;a&gt;撰写&lt;&#x2F;p&gt;
&lt;p&gt;在快速发展的软件开发领域,&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;&quot;&gt;Playwright&lt;&#x2F;a&gt; 凭借其与 Visual Studio Code 和 GitHub 的强大集成以及不断壮大的社区支持,重塑了自动化测试。我转向使用这个框架的过程因为结合了 ChatGPT 而得到显著的效率提升,标志着我今后编写和调试 UI 测试方式的一个转折点。&lt;&#x2F;p&gt;
&lt;p&gt;我采用 Playwright 和 ChatGPT 的旅程清楚地展示了 AI 在连接不同测试环境和编码语言之间的强大作用。这种组合不仅简化了迁移过程;它还丰富了工程师的工具箱,实现了更流畅和适应性更强的自动化测试方法。自动化工程师的经验和知识仍然至关重要,但 ChatGPT 作为一个强大的盟友,指导我们学习新技术和能力。&lt;&#x2F;p&gt;
&lt;p&gt;通过本文,我们将探讨 ChatGPT 如何促进这些转变,突出其中的优势和挑战。这证明了人类专业知识和人工智能如何在软件测试领域不断发展及协作。&lt;&#x2F;p&gt;
&lt;p&gt;自从 2020 年微软推出 Playwright 以来， 改工具因其对 Google Chrome、Firefox、Microsoft Edge 和 WebKit 等多种浏览器的全面支持而备受赞誉。Playwright 以其无缝集成多种编程语言中，其确保跨浏览器一致性结果的能力非常突出,现在已经成为了现代软件开发工作流程中的主要工具。其快速采纳技术社区的积极反馈得到了广泛的赞誉，这点通过不断增长的用户基础和 GitHub 星标数量可以得到完美的体现。Playwright 对提升自动化测试实践的贡献展示了其在该领域的重要性和创新性,使其成为有追求的测试工程师的首选工具。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zai-ce-shi-zi-dong-hua-zhong-li-yong-chatgpt&quot;&gt;在测试自动化中利用 ChatGPT&lt;&#x2F;h2&gt;
&lt;p&gt;在本节中,我将向你展示 ChatGPT-4 如何显著简化我的编码实践。它帮助我找到错误消息的解决方案,将代码片段从一种编程语言转换为另一种,甚至仅根据几个输入和预定义的输出就能提出函数原型。我收集了一些我与 ChatGPT-4 实际对话中最喜欢的例子,向你展示它如何在现实情况下工作。如果你想看这些对话并了解更多关于 ChatGPT-4 如何帮助你编写自己的测试,请继续阅读!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jiang-xiang-fa-he-dai-ma-pian-duan-zhuan-huan-wei-typescript&quot;&gt;将想法和代码片段转换为 TypeScript&lt;&#x2F;h2&gt;
&lt;p&gt;由于主要使用 TypeScript 以外的语言,我使用 ChatGPT 帮助我将一些现有的编码项目和想法转换为 typescript。事实证明，它非常有用,指导我转换代码片段并在 TypeScript 中实现了各种概念 。这个 AI 工具平滑了我的学习曲线,使学习和使用 TypeScript 变得更加的高效可控。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;我的第一个提示:&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;elevating-automated-testing-playwright-meets-chatgpt&#x2F;.&#x2F;1.png&quot; alt=&quot;图片 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;这是 ChatGPT 的结果:&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;elevating-automated-testing-playwright-meets-chatgpt&#x2F;.&#x2F;2.png&quot; alt=&quot;图片 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这是一个很好的开始,特别是因为 ChatGPT 包含了 UI 测试所需的 Playwright 库的各种导入。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;接下来,我寻求一些帮助来完成我想执行的任务:&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;elevating-automated-testing-playwright-meets-chatgpt&#x2F;.&#x2F;3.png&quot; alt=&quot;图片 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;结果非常详细:&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;elevating-automated-testing-playwright-meets-chatgpt&#x2F;.&#x2F;4.png&quot; alt=&quot;图片 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;学习新的编程语言通常意味着要适应不同的数据类型,这可能很棘手。幸运的是,ChatGPT 也能在这方面提供帮助,使学习过程更加的顺畅:&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;elevating-automated-testing-playwright-meets-chatgpt&#x2F;.&#x2F;5.png&quot; alt=&quot;图片 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这是三个快速示例,展示了如何借助 ChatGPT 将思路或代码片段转换为 TypeScript。要深入了解对话和逐步过程,请查看此处的完整对话:&lt;a href=&quot;https:&#x2F;&#x2F;chat.openai.com&#x2F;share&#x2F;9656950e-b8a8-477d-afbc-f555a11db748&quot;&gt;https:&#x2F;&#x2F;chat.openai.com&#x2F;share&#x2F;9656950e-b8a8-477d-afbc-f555a11db748&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tong-guo-chatgpt-jiang-playwright-yu-github-actions-ji-cheng&quot;&gt;通过 ChatGPT 将 Playwright 与 GitHub Actions 集成&lt;&#x2F;h2&gt;
&lt;p&gt;UI 测试启动并运行后,现在是时候通过 GitHub Actions 为我们的代码库建立自动化测试工作流了。这一步确保我们的测试无缝集成到开发周期中,提高 CI&#x2F;CD 流水线的效率和可靠性。&lt;&#x2F;p&gt;
&lt;p&gt;基于这个目标,我写了以下提示:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;如何使用 GitHub actions 在每次合并到主分支时执行 playwright UI 测试用例&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;ChatGPT 提供了一个简单的指南来设置 GitHub Action 工作流,使过程变得简单明了:&lt;&#x2F;p&gt;
&lt;p&gt;为了从 GitHub action 中访问 Playwright 报告,我写了以下提示:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;如何保存在 GitHub action 期间创建的 playwright 报告,以便稍后可以访问它&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;ChatGPT 推荐使用 GitHub 的 artifact 上传功能来完成我的任务。然而,我遇到了一个问题,当 UI 测试报错时候,上传任务就不会执行,这促使我问以下问题:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;我想让 GitHub action 在出错时也上传 artifact&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;em&gt;ChatGPT 的建议很有帮助,但它导致了一个假阳性问题,那就是即使 UI 测试代码出错而失败,GitHub Action 也显示成功。这需要我进一步完善提示:&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果只想在失败时导出 playwright 报告,如何将其添加到 GitHub action 中&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;总结一下,很明显为 UI 测试设置精简的工作流对任何项目都是一个游戏规则的改变者。它不仅确保测试成为开发周期的一个连贯部分,还提高了 CI&#x2F;CD 流水线的整体效率和可靠性。在整个过程中,与 ChatGPT 的互动在完善设计方案上面被证明非常有价值,突出了清晰沟通和迭代反馈对达成有效解决方案的必要性。这种经历强调了明确测试意图的价值,以及让我们看到了与 ChatGPT 合作解决问题的可能,引导我们实现量身定制的方案，最终达到集成的效果。&lt;&#x2F;p&gt;
&lt;p&gt;要深入了解对话和 ChatGPT 提供的分步指导,请查看此 URL 的聊天:&lt;a href=&quot;https:&#x2F;&#x2F;chat.openai.com&#x2F;share&#x2F;2998ae7d-3d76-49a4-87ae-f16793c0171e&quot;&gt;https:&#x2F;&#x2F;chat.openai.com&#x2F;share&#x2F;2998ae7d-3d76-49a4-87ae-f16793c0171e&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-chatgpt-jin-xing-cuo-wu-chu-li&quot;&gt;使用 ChatGPT 进行错误处理&lt;&#x2F;h2&gt;
&lt;p&gt;我们都经历过这种情况——面对一条错误消息,我们陷入无休止的 Google 搜索、浏览论坛和阅读文章以寻找解决方案的漫长过程。这可能是绝对的浪费时间。幸运的是,ChatGPT 提供了一种更快的方法来应对这些情况。以下是 ChatGPT 如何帮助我解决在设置 GitHub action 工作流时遇到的错误(我继续之前的聊天),证明在高效故障排除方面，使用 ai 的优势十分明显。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;这是我的错误消息:&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;elevating-automated-testing-playwright-meets-chatgpt&#x2F;.&#x2F;6.png&quot; alt=&quot;图片 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;elevating-automated-testing-playwright-meets-chatgpt&#x2F;.&#x2F;7.png&quot; alt=&quot;图片 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;ChatGPT 提供了两种解决方案:在无界面模式下运行浏览器和使用 xvfb-run 模拟显示环境。考虑到我的测试涉及 Chrome 扩展,需要在有界面模式下运行,我决定探索 xvfb-run 方法。通常,当我遇到错误消息时,我会深入搜索许多论坛和文章以找到解决方法。这次,ChatGPT 直接切入主题,快速给我两个可靠的选择。我选择了第二个想法,尝试 xvfb-run,并且它成功了!这为我节省了大量时间和精力来弄清楚问题。&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tan-suo-chatgpt-ji-cheng-de-xie-tong-xiao-ying-he-tiao-zhan&quot;&gt;探索 ChatGPT 集成的协同效应和挑战&lt;&#x2F;h2&gt;
&lt;p&gt;将 ChatGPT 与 Playwright 集成带来了丰富的实际应用成果，展示了当前 AI 工具在测试自动化中的潜力和局限性。利用 AI 进行错误处理和将概念性想法转化为可执行的 TypeScript 代码等任务的转变，简化了编程过程的许多方面。这种方法不仅提高了效率，还促进了更动态和响应迅速的开发周期，使迭代更快、质量更高。&lt;&#x2F;p&gt;
&lt;p&gt;尽管取得了这些进展和好处，我在这个过程中也遇到了不少挑战。最大的挑战之一是我最初对完全由 AI 生成的解决方案的依赖。这些解决方案虽然创新，但需要仔细审查以验证其相关性和适用性。这一经历表明，人工监督在解释和完善 AI 生成的输出，以适应各种不同项目的独特需求方面仍然不可或缺。&lt;&#x2F;p&gt;
&lt;p&gt;从这种集成中，我学到了提示工程的重要性——这是一项在优化与 ChatGPT 交互方面至关重要的技能。有效的提示工程涉及制定能引导 AI 产生更准确和相关输出的查询，提高 ChatGPT 在编程任务中的实用性。学习这项技能不仅帮助我克服了与 AI 局限性相关的一些挑战，还极大地提高了利用 ChatGPT 进行测试自动化的效率。&lt;&#x2F;p&gt;
&lt;p&gt;此外，在利用 AI 能力和依赖自动化工程师的批判性思维和经验之间保持平衡的需求变得异常清晰。虽然 AI 可以自动化某些任务，但它无法模仿自动化工程师的细微理解和适应性问题解决技能。因此，利用 AI 导航测试自动化的复杂性需要一个持续的学习过程，其中理解提示工程的复杂性成为充分利用 ChatGPT 等 AI 工具的关键能力。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ai-zai-ce-shi-zi-dong-hua-zhong-de-wei-lai-fang-xiang&quot;&gt;AI 在测试自动化中的未来方向&lt;&#x2F;h2&gt;
&lt;p&gt;展望 AI 在测试自动化中的未来，认识到 Auto Playwright 和 &lt;a href=&quot;https:&#x2F;&#x2F;zerostep.com&#x2F;&quot;&gt;ZeroStep&lt;&#x2F;a&gt; 等框架的潜力至关重要。这些工具旨在简化测试过程，为没有专门自动化工程师的团队提供了有趣的可能性。然而，重要的是理解，这些框架尽管创新，不能取代自动化工程师的宝贵技能。正如前面提到的，AI 在其当前状态下无法复制人类专家的知识深度、批判性思维和经验。在简要了解这些框架后，我仍然对它们与熟练专业人士相比的表现持怀疑态度，尽管我还没有亲自验证它们。&lt;&#x2F;p&gt;
&lt;p&gt;保持对 AI 进展的了解非常重要，因为在未来，平衡自动化技术和人类专业知识之间的关系将变得更加关键。我毫不怀疑，AI 将继续在识别稳定选择器等任务中发挥越来越多的支持作用，这是测试自动化中的一个常见挑战。任何处理过查找可靠选择器的人都知道这种困境，而 AI 快速识别不稳定选择器并提供有效修复的能力可能会大大改善我们工作的这一方面。这不仅会使过程更快更容易，还会增强自动化工程师向开发人员提供的反馈，从而提高整体软件质量。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-lun&quot;&gt;结论&lt;&#x2F;h2&gt;
&lt;p&gt;希望通过本文，你能了解 ChatGPT 如何显著加速了我向新测试框架和编程语言的过渡。它在错误处理和简化复杂任务方面的帮助是非常大的，使我进入新领域的旅程变得更快更易管理。使用 ChatGPT 所获得的见解和效率让我相信，在未来的项目中继续使用这个 AI 助手，特别是在错误处理和其他编程挑战方面。&lt;&#x2F;p&gt;
&lt;p&gt;我强烈建议同行的自动化工程师和开发人员亲自测试，探索 AI 助手如 ChatGPT 在测试自动化工作流程中的所有潜力。这些工具在增强成果、减少手动工作和为开发周期注入速度方面的价值不可低估。无论是简化技术之间的过渡、改进错误处理还是生成代码片段，将 AI 集成到测试自动化中的影响只会不断增长。&lt;&#x2F;p&gt;
&lt;p&gt;最后，我很乐意听到你在测试自动化中使用 AI 的经验！你是否发现了类似的好处，或者有自己独特的挑战？AI 助手如何改变了你自己的测试方法？让我们就在测试自动化中使用这些创新实践展开讨论。通过交流我们的故事和策略，我们可以集体推动我们领域的可能性，朝着技术和人类专业知识结合以实现软件质量显著提升和突破的未来迈进。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;adventuresinqa.com&#x2F;2024&#x2F;02&#x2F;27&#x2F;elevating-automated-testing-playwright-meets-chatgpt&#x2F;&quot;&gt;URL 来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2024-02-27&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright 选择器终极指南</title>
        <published>2024-01-24T00:00:00+00:00</published>
        <updated>2024-01-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-selectors/"/>
        <id>https://playwright.itest.info/blog/playwright-selectors/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-selectors/">&lt;h2 id=&quot;xie-zai-qian-mian&quot;&gt;写在前面&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;👀 乙醇来自 2024 年的评论。这篇文章不是特别推荐，里面讲了很多 xpath 和 css 选择器的高级应用，不过 xpath 是 playwright 不推荐使用的方式，css 在 playwright 里的地位也不高，playwright 最推荐的定位器其实是&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators#quick-guide&quot;&gt;这几个&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在 Playwright 中,选择器是用于定位和交互网页元素的字符串标识符。它们对于执行表单填写、网页爬取和自动化测试等任务至关重要。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 是一个开源的自动化库,专为端到端测试、网页爬取和 ui 自动化测试而设计。它支持无头和有头浏览器模式,并通过快速易用的 API 提供出色的性能。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 兼容多种浏览器,包括 Chrome、Microsoft Edge (使用 Chromium)、Safari (使用 WebKit) 和 Mozilla Firefox。Playwright 提供不同类型的选择器,包括 CSS 选择器、XPath 选择器和文本选择器,每种选择器都有其优势和适用场景。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;bugbug-homepage.s3.eu-central-1.amazonaws.com&#x2F;playwright_meme_api_17fe7e4658.jpeg&quot; alt=&quot;图片 1: playwright-meme&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gai-yao&quot;&gt;&lt;strong&gt;概要&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;本指南涵盖了不同类型的选择器,包括 CSS、XPath 和文本选择器,并解释了它们的用法和优势。&lt;&#x2F;li&gt;
&lt;li&gt;还提供了创建可靠和可维护的自动化脚本的最佳实践。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;另请查看:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;bugbug.io&#x2F;blog&#x2F;software-testing&#x2F;codeless-automation-testing-tools&#x2F;&quot;&gt;15 大无代码自动化测试工具&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;bugbug.io&#x2F;blog&#x2F;test-automation-tools&#x2F;testim-alternatives&#x2F;&quot;&gt;Testim 替代品&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;bugbug.io&#x2F;blog&#x2F;test-automation-tools&#x2F;cypress-alternative&#x2F;&quot;&gt;Cypress 替代品&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwright-ru-men&quot;&gt;Playwright 入门&lt;&#x2F;h2&gt;
&lt;p&gt;要开始使用 Playwright,你需要安装它并设置一个项目。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;以下是步骤指南:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;为你的 Playwright 项目创建一个新文件夹。&lt;&#x2F;li&gt;
&lt;li&gt;在终端或命令行界面中打开该文件夹。&lt;&#x2F;li&gt;
&lt;li&gt;运行命令 &lt;code&gt;npm init -y&lt;&#x2F;code&gt; 来初始化一个新的 Node.js 项目。&lt;&#x2F;li&gt;
&lt;li&gt;通过运行命令 &lt;code&gt;npm install playwright&lt;&#x2F;code&gt; 来安装 Playwright。&lt;&#x2F;li&gt;
&lt;li&gt;导入 Playwright 并创建一个新的浏览器实例。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; your code here
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;})();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;安装好 Playwright 后,你就可以开始使用选择器与网页元素进行交互了。如果你想了解更多关于使用 Playwright 的信息,可以查看我们的 &lt;a href=&quot;https:&#x2F;&#x2F;bugbug.io&#x2F;blog&#x2F;software-testing&#x2F;playwright-cheat-sheet&#x2F;&quot;&gt;Playwright 速查表&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwright-zhong-de-ding-wei-qi-api&quot;&gt;Playwright 中的定位器 API&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright 的定位器 API 是自动等待和重试功能的核心,使得与网页元素的交互更加稳定。以下是关于它的一些要点:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;创建定位器&lt;&#x2F;strong&gt;: 定位器使用 &lt;code&gt;page.locator()&lt;&#x2F;code&gt; 方法创建。此方法接受一个描述如何在页面中查找元素的选择器。Playwright 支持 CSS 和 XPath 选择器,如果省略 &lt;code&gt;css=&lt;&#x2F;code&gt; 或 &lt;code&gt;xpath=&lt;&#x2F;code&gt; 前缀,它会自动检测。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;可操作性检查&lt;&#x2F;strong&gt;: 在执行操作之前,Playwright 会对元素进行可操作性检查。这包括确保元素是可见的、attached 的和稳定的。例如,在点击一个元素时,Playwright 会等待它变为可点击状态。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;具体的操作行为&lt;&#x2F;strong&gt;: 在执行诸如 &lt;code&gt;click&lt;&#x2F;code&gt; 或 &lt;code&gt;dblclick&lt;&#x2F;code&gt; 等操作时,你可以指定各种选项:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;position&lt;&#x2F;code&gt;: 相对于元素左上角的 x 和 y 坐标。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;timeout&lt;&#x2F;code&gt;: 等待操作完成的最长时间(以毫秒为单位)。默认为 30 秒,但你可以传入 0 来禁用超时。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;trial&lt;&#x2F;code&gt;: 当设置为 true 时,该方法会执行可操作性检查而不执行实际的操作。这对于确保元素已准备好进行交互很有用。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;&lt;strong&gt;点击和双击&lt;&#x2F;strong&gt;: &lt;code&gt;click&lt;&#x2F;code&gt; 方法用于点击元素,你可以指定鼠标按钮、点击次数、鼠标按下和释放之间的延迟,以及修饰键(如 Shift、Control)等选项。&lt;code&gt;dblclick&lt;&#x2F;code&gt; 方法类似,但执行双击操作。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;元素数量&lt;&#x2F;strong&gt;: &lt;code&gt;count&lt;&#x2F;code&gt; 方法返回匹配给定选择器的元素数量,有助于断言检查页面上特定元素的数量。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;通过 text、Alt text titile 以及 testID 定位&lt;&#x2F;strong&gt;: 你以根据元素的文本内容、alt 文本、title 属性或 textID 来定位元素。这对于查找非交互元素(如 &lt;code&gt;div&lt;&#x2F;code&gt;、&lt;code&gt;span&lt;&#x2F;code&gt;、&lt;code&gt;p&lt;&#x2F;code&gt; 等)以及具有特定标题或 textID 的图像和元素特别有用。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Shadow DOM&lt;&#x2F;strong&gt;: Playwright 中的定位器默认可以与 Shadow DOM 中的元素一起工作,但 XPath 选择器 不支持这个特性。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;filter&lt;&#x2F;strong&gt;: 你可以通过文本、子元素是否存在或其他条件来过滤定位器。当你需要从列表或一组元素中选择特定项目时,这很有用。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Playwright 中的定位器 API 旨在尽可能模仿用户交互,使其成为编写可靠和可维护测试用例的强大工具。如需更详细的信息,你可以参考 Playwright 官方文档中关于&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators&quot;&gt;定位器&lt;&#x2F;a&gt;和定位器方法的部分。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwright-css-xuan-ze-qi&quot;&gt;Playwright CSS 选择器&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;CSS 选择器&lt;&#x2F;strong&gt;是 Web 开发中的一个主要工具,在 Playwright 中广泛用于定位网页中的元素。这些选择器是代表不同元素的字符串,基于元素的属性、class 或 ID。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Playwright CSS 选择器&lt;&#x2F;strong&gt;与常规 CSS 中的选择器类似,但为 Web 自动化进行了增强,允许精确定位元素。它们能够匹配包含指定属性或类的任何元素,其语法与传统 CSS 非常相似。这包括复杂的选择器,如伪类,它们是 Playwright 中的实验性 CSS 功能。&lt;&#x2F;p&gt;
&lt;p&gt;此外,Playwright 支持属性选择器操作符,并可以解析将多个空格转换为一个空格的行为,确保空格始终被标准化。&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;基本 CSS 选择器用法&lt;&#x2F;strong&gt;:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Playwright 可以使用标准 CSS 语法定位元素。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;await page.locator(&#x27;css=button&#x27;).click();&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;穿透 Shadow DOM&lt;&#x2F;strong&gt;:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;ul&gt;
&lt;li&gt;Playwright 中的 CSS 选择器可以穿透开放的 Shadow DOM,允许访问 shadow dom 元素。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;strong&gt;自定义伪类&lt;&#x2F;strong&gt;:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;ul&gt;
&lt;li&gt;Playwright 引入了 &lt;code&gt;:visible&lt;&#x2F;code&gt;、&lt;code&gt;:has-text()&lt;&#x2F;code&gt;、&lt;code&gt;:has()&lt;&#x2F;code&gt;、&lt;code&gt;:is()&lt;&#x2F;code&gt;、&lt;code&gt;:nth-match()&lt;&#x2F;code&gt; 等伪类。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;:visible&lt;&#x2F;code&gt; 只针对可见元素。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;code&gt;await page.locator(&#x27;button:visible&#x27;).click();&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:has-text()&lt;&#x2F;code&gt; 匹配包含指定文本的元素,对于不区分大小写、去除空格的子字符串匹配很有用。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;await page.locator(&#x27;article:has-text(&quot;Playwright&quot;)&#x27;).click();&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;&lt;strong&gt;通过文本匹配&lt;&#x2F;strong&gt;:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:text()&lt;&#x2F;code&gt; 伪类匹配包含指定文本的元素。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;await page.locator(&#x27;#nav-bar :text(&quot;Home&quot;)&#x27;).click();&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:text-is()&lt;&#x2F;code&gt; 匹配具有完全相同文本的元素。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;await page.locator(&#x27;#nav-bar :text-is(&quot;Home&quot;)&#x27;).click();&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:text-matches()&lt;&#x2F;code&gt; 匹配文本内容符合类似 JavaScript 正则表达式的元素。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;await page.locator(&#x27;#nav-bar :text-matches(&quot;reg?ex&quot;, &quot;i&quot;)&#x27;).click();&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;&lt;strong&gt;包含其他元素的元素&lt;&#x2F;strong&gt;:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:has()&lt;&#x2F;code&gt; 伪类是 Playwright 中的实验性 CSS 功能。如果元素包含由选择器指定的其他元素,它会返回该元素。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;await page.locator(&#x27;article:has(div.promo)&#x27;).textContent();&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;&lt;strong&gt;基于布局匹配元素&lt;&#x2F;strong&gt;:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;ul&gt;
&lt;li&gt;Playwright 的布局伪类如 &lt;code&gt;:right-of()&lt;&#x2F;code&gt;、&lt;code&gt;:left-of()&lt;&#x2F;code&gt;、&lt;code&gt;:above()&lt;&#x2F;code&gt;、&lt;code&gt;:below()&lt;&#x2F;code&gt; 和 &lt;code&gt;:near()&lt;&#x2F;code&gt; 可用于根据元素与其他元素的空间关系选择元素。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;code&gt;await page.locator(&#x27;input:right-of(:text(&quot;Password&quot;))&#x27;).fill(&#x27;value&#x27;);&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;&lt;strong&gt;从查询结果中选择第 N 个匹配项&lt;&#x2F;strong&gt;:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;:nth-match()&lt;&#x2F;code&gt; 伪类从多个匹配项中选择特定实例的元素。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;code&gt;await page.locator(&#x27;:nth-match(:text(&quot;Buy&quot;), 3)&#x27;).click();&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这些功能使 Playwright 中的 CSS 选择器成为 Web 自动化的强大工具,不仅允许基于元素的属性和文本内容精确定位元素,还可以根据它们的可见性、包含关系和相对位置进行定位。&lt;&#x2F;p&gt;
&lt;p&gt;这种多样性在复杂的 Web 应用程序中特别有用,因为标准选择器功能有限，扩展之后才更好用一点。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwright-zhong-de-xpath-xuan-ze-qi&quot;&gt;Playwright 中的 XPath 选择器&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;XPath 选择器&lt;&#x2F;strong&gt;为 Playwright 中的元素定位提供了另一种强大的方法。与 CSS 选择器不同,XPath 选择器基于网页的 dom 结构。它们允许你根据元素的位置、属性和其他条件来查找元素,xpath 可以精确地遍历 DOM。这在 CSS 选择器可能不够用的情况下特别有用,比如复杂布局或需要查找包含指定文本的元素的时候。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Playwright 中的 XPath&lt;&#x2F;strong&gt; 还支持自定义选择器引擎,为选择很难通过 CSS 去定位的元素提供了灵活性。&lt;&#x2F;p&gt;
&lt;p&gt;你可以使用 BugBug 的&lt;strong&gt;无代码&lt;&#x2F;strong&gt; &lt;a href=&quot;https:&#x2F;&#x2F;bugbug.io&#x2F;xpath-selector-builder&#x2F;&quot;&gt;&lt;strong&gt;在线 XPath 生成器&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;来创建稳定的 xpath 选择器。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;通过 XPath 选择元素&lt;&#x2F;strong&gt;:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;你可以使用元素的 XPath 来选择元素,这允许基于 DOM 结构进行更灵活和精确的定位。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;const elementByXPath = await page.$x(&#x27;&#x2F;&#x2F;div[@class=&quot;example&quot;]&#x27;);&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;评估 XPath 表达式&lt;&#x2F;strong&gt;:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Playwright 可以解析 XPath 表达式并返回匹配的元素。$x 是 Playwright 为此目的提供的一种简写方法。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;const elements = await page.$x(&#x27;&#x2F;&#x2F;button[contains(text(), &quot;Submit&quot;)]&#x27;);&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;在 XPath 中使用 Axes&lt;&#x2F;strong&gt;:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;XPath 的 Axes 方法可以用于以各种方式遍历 DOM,例如选择相对于当前节点的父元素、兄弟元素或子元素。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;const siblingElement = await page.$x(&#x27;&#x2F;&#x2F;input[@id=&quot;username]&#x2F;following-sibling::input&#x27;);&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;带条件表达式的 XPath&lt;&#x2F;strong&gt;:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;XPath 选择器可以包含条件来匹配具有特定属性或文本内容的元素。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;const elementWithCondition = await page.$x(&#x27;&#x2F;&#x2F;div[@data-status=&quot;active&quot; and text()=&quot;Active&quot;]&#x27;);&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;选择 Shadow DOM 内的元素&lt;&#x2F;strong&gt;:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;虽然 XPath 本身不支持 Shadow DOM,但 Playwright 允许使用 CSS 和 XPath 选择器的组合来实现定位 shadow dom 中的元素。&lt;&#x2F;p&gt;
&lt;p&gt;另外请查看我们的比较: &lt;a href=&quot;https:&#x2F;&#x2F;bugbug.io&#x2F;blog&#x2F;test-automation&#x2F;xpath-vs-css-selectors&#x2F;&quot;&gt;XPath vs CSS 选择器&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwright-zhong-de-text-xuan-ze-qi&quot;&gt;Playwright 中的 text 选择器&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright 中的&lt;strong&gt;text 选择器&lt;&#x2F;strong&gt;专注于根据可见文本内容定位元素。这些选择器非常适合定位包含特定文本节点的元素,或搜索具有精确内容的文本节点。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 中文本选择器的&lt;strong&gt;语法&lt;&#x2F;strong&gt;非常直观,允许你直接指定要匹配的文本,可以用单引号或双引号转义。这个功能在网页自动化脚本中特别有用,因为通过文本内容识别元素比使用属性或 XPath 更实用。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zai-playwright-zhong-zu-he-xuan-ze-qi&quot;&gt;在 Playwright 中组合选择器&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright 还支持多个选择器的组合,这个功能增强了元素选择的精确度。通过将选择器链接在一起,你可以创建更具体的查询。这可能涉及组合 CSS 和 XPath 选择器,或使用同一类型的多个选择器并增加过滤条件来精确定位元素。&lt;&#x2F;p&gt;
&lt;p&gt;例如,你可以根据元素的布局和相对于其他元素的位置来选择元素,或者结合属性选择器和文本选择器来查找包含指定文本的元素。链式选择器解析满足链中所有条件的元素,组合的方式可以非常的灵活。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;定位器 API&lt;&#x2F;strong&gt; 在 Playwright 中简化了与网页元素交互的过程,提供了一种更稳定和强大的方式来查询和操作页面元素。这在动态页面应用中,以及需要复杂交互的场景中特别有用。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwright-zhong-de-gao-ji-xuan-ze-qi&quot;&gt;Playwright 中的高级选择器&lt;&#x2F;h2&gt;
&lt;p&gt;除了 CSS 选择器和 XPath 选择器,Playwright 还支持一系列高级选择器,提供更大的灵活性。这些高级选择器包括 React 选择器、可访问性选择器、CSS 伪类和 XPath 函数。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;以下是 Playwright 中一些高级选择器的概述:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;React 选择器&lt;&#x2F;strong&gt;: 用于识别 React 应用程序中的元素。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;可访问性选择器&lt;&#x2F;strong&gt;: 用于根据元素的可访问性属性定位元素。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;CSS 伪类&lt;&#x2F;strong&gt;: 用于根据各种状态或条件选择元素。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;XPath 函数&lt;&#x2F;strong&gt;: 用于使用 XPath 函数执行复杂查询。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Playwright 的高级选择器允许你微调元素选择并处理更复杂的场景。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zai-playwright-zhong-shi-yong-xuan-ze-qi-de-zui-jia-shi-jian&quot;&gt;在 Playwright 中使用选择器的最佳实践&lt;&#x2F;h2&gt;
&lt;p&gt;为了充分利用 Playwright 选择器,遵循最佳实践很重要。以下是一些需要记住的提示:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;使用唯一标识符&lt;&#x2F;strong&gt;: 尽可能依赖唯一标识符(如 ID 或数据属性)来定位元素。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;避免不稳定的选择器&lt;&#x2F;strong&gt;: 应避免使用容易变化的选择器,比如基于位置关系的选择器。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;定期更新选择器&lt;&#x2F;strong&gt;: 定期检查并更新你的选择器,以适应网站结构的任何变化。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;通过遵循这些最佳实践,你可以确保自动化脚本的稳定性和可靠性。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zai-playwright-zhong-shi-yong-xuan-ze-qi-jin-xing-zi-dong-hua&quot;&gt;在 Playwright 中使用选择器进行自动化&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright 选择器对于表单填写、网页爬虫和 ui 自动化测试等任务非常有价值。让我们探讨几个在 Playwright 中使用选择器进行自动化的例子:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;biao-dan-tian-xie&quot;&gt;表单填写&lt;&#x2F;h3&gt;
&lt;p&gt;这个脚本演示了浏览器自动化的基本套路,特别是启动浏览器、跳转到页面和与网页元素交互。需要注意的是,这个脚本并没有导航到特定的 URL,所以如果你打算将它用于特定网站,你需要添加一行使用 &lt;code&gt;page.goto(&#x27;被测网站 URL&#x27;)&lt;&#x2F;code&gt; 来访问该网站。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;usernameInput &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;usernameInput&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;johndoe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;passwordInput &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;passwordInput&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password123&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;();\
&lt;&#x2F;span&gt;&lt;span&gt;})();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;wang-ye-pa-chong&quot;&gt;网页爬虫&lt;&#x2F;h3&gt;
&lt;p&gt;下面的代码展示了如何使用 Playwright 的自动化功能提取和打印网页上所有链接 &#x27;a&#x27; 元素的文本内容。&lt;&#x2F;p&gt;
&lt;p&gt;脚本首先从 Playwright 导入 Chromium 模块,然后启动一个 Chromium 浏览器实例并打开一个新页面。它使用 &lt;code&gt;$$eval&lt;&#x2F;code&gt; 函数选择页面上的所有超链接元素,并将它们的文本内容映射到一个数组。然后遍历这个数组,将每个链接的文本内容打印到控制台。&lt;&#x2F;p&gt;
&lt;p&gt;最后,脚本关闭浏览器页面。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;links &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;$$eval&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;links&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;links&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;textContent&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;linkText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;of &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;links&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;linkText&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;})();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;ui-zi-dong-hua-ce-shi&quot;&gt;ui 自动化测试&lt;&#x2F;h3&gt;
&lt;p&gt;下面的代码是一个使用 Chromium 浏览器进行网页登录的 Playwright 脚本。&lt;&#x2F;p&gt;
&lt;p&gt;脚本首先导入 Playwright 模块并启动 Chromium 浏览器。然后它打开一个新的浏览器页面。&lt;&#x2F;p&gt;
&lt;p&gt;接下来,脚本通过各自的 ID &lt;code&gt;#username&lt;&#x2F;code&gt; 和 &lt;code&gt;#password&lt;&#x2F;code&gt; 定位页面上的用户名和密码输入字段,并用预定义的数据 &#x27;johndoe&#x27; 和 &#x27;password123&#x27; 填充它们。&lt;&#x2F;p&gt;
&lt;p&gt;填写登录信息后,脚本定位并点击登录按钮 &lt;code&gt;#loginButton&lt;&#x2F;code&gt;。然后它等待退出登录按钮 &lt;code&gt;#logoutButton&lt;&#x2F;code&gt; 出现,把这个当成是成功登录的断言。&lt;&#x2F;p&gt;
&lt;p&gt;最后,脚本关闭浏览器页面。这个自动化脚本是 Playwright 如何用于自动化网页应用登录程序的基本示例。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{\
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;usernameInput &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;usernameInput&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;johndoe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;passwordInput &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;passwordInput&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password123&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginButton &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#loginButton&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginButton&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logoutButton &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#logoutButton&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;logoutButton&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;();\
&lt;&#x2F;span&gt;&lt;span&gt;})();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;zai-playwright-zhong-shi-yong-xuan-ze-qi-chu-li-shadow-dom&quot;&gt;在 Playwright 中使用选择器处理 Shadow DOM&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright 的 CSS 和文本引擎可以穿透 Shadow DOM,允许你与元素交互,就像 shadow dom 不存在一样。这种能力简化了在 Shadow DOM 内选择和操作元素的过程。&lt;&#x2F;p&gt;
&lt;p&gt;对于定位 Shadow DOM 中的元素,可以直接使用 Playwright 的标准定位器,如 &lt;code&gt;page.locator()&lt;&#x2F;code&gt;、&lt;code&gt;page.getByText()&lt;&#x2F;code&gt; 等。例如,如果你有一个带有 shadow root 的自定义 Web 组件,想要点击其中的特定元素,你可以使用 &lt;code&gt;page.getByText(&#x27;Details&#x27;).click()&lt;&#x2F;code&gt; 来定位文本为 &#x27;Details&#x27; 的元素。同样,要点击像 &lt;code&gt;&amp;lt;x-details&amp;gt;&lt;&#x2F;code&gt; 这样的自定义元素,你可以使用 &lt;code&gt;page.locator(&#x27;x-details&#x27;, { hasText: &#x27;Details&#x27; }).click()&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 还支持高级选择器策略,比如通过文本、子&#x2F;后代元素过滤定位器,或使用布局伪类如 &lt;code&gt;:right-of()&lt;&#x2F;code&gt;、&lt;code&gt;:left-of()&lt;&#x2F;code&gt;、&lt;code&gt;:above()&lt;&#x2F;code&gt;、&lt;code&gt;:below()&lt;&#x2F;code&gt; 和 &lt;code&gt;:near()&lt;&#x2F;code&gt; 来根据元素相对于其他元素的布局定位元素。当元素缺乏明显的属性或文本内容时,这些伪类特别有用,允许你指定它们的相对位置以进行更精确的选择。&lt;&#x2F;p&gt;
&lt;p&gt;此外,Playwright 为 React 和 Vue 应用程序提供了专门的定位器,分别以 &lt;code&gt;_react&lt;&#x2F;code&gt; 和 &lt;code&gt;_vue&lt;&#x2F;code&gt; 前缀识别。这些定位器允许你根据组件名称和属性值查找元素,这在测试这些框架的现代 Web 应用程序时特别方便。&lt;&#x2F;p&gt;
&lt;p&gt;重要的是要注意,虽然 XPath 可以在 Playwright 中使用,但它不能穿 shadow root,并且通常不鼓励使用,我们还是要使用更稳定、对用户可见的定位器。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-lun&quot;&gt;结论&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright 选择器是进行自动化网页测试、网页抓取和用户界面测试任务的强大工具。通过利用 CSS 选择器、XPath 选择器和文本选择器,你可以精确定位网页上的元素并与之交互。&lt;&#x2F;p&gt;
&lt;p&gt;通过遵循最佳实践并利用 Playwright 选择器的高级功能,你可以简化自动化脚本并提高其可靠性。今天就开始使用 Playwright 选择器,释放你的自动化工作的全部潜力。&lt;&#x2F;p&gt;
&lt;p&gt;记住要定期更新你的选择器,使用唯一标识符,并探索 Playwright 选择器引擎中的各种选项,以使你的自动化脚本更加高效和稳健。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;祝你(自动化)测试愉快!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;chang-jian-wen-ti-jie-da-playwright-xuan-ze-qi-he-ding-wei-qi&quot;&gt;常见问题解答: Playwright 选择器和定位器&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;playwright-zhi-chi-na-xie-xuan-ze-qi&quot;&gt;Playwright 支持哪些选择器?&lt;&#x2F;h3&gt;
&lt;p&gt;Playwright 支持多种选择器来选择元素,以满足不同的 Web 自动化需求。这些包括:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;CSS 选择器: 根据 CSS 属性定位元素。Playwright 的 CSS 选择器工作方式类似于常规 CSS,但针对自动化进行了增强。&lt;&#x2F;li&gt;
&lt;li&gt;XPath 选择器: 适用于复杂的 DOM 结构,XPath 选择器可以根据元素的 XML 路径导航和查找元素。&lt;&#x2F;li&gt;
&lt;li&gt;文本选择器: 定位包含指定文本的元素,非常适合查找文本节点或具有精确文本内容的元素。&lt;&#x2F;li&gt;
&lt;li&gt;Playwright 特定选择器: 这包括像 data-test-id 这样的选择器,用于以 Playwright 框架特定的方式定位元素。&lt;&#x2F;li&gt;
&lt;li&gt;React 选择器: 为 React 应用程序设计的选择器,允许与 React 组件交互。&lt;&#x2F;li&gt;
&lt;li&gt;属性选择器: 这些选择器使用属性选择器操作符,可以根据属性匹配任何元素。&lt;&#x2F;li&gt;
&lt;li&gt;链式选择器: 组合多个选择器,如 CSS 和 XPath,以解决更复杂的选择场景。&lt;&#x2F;li&gt;
&lt;li&gt;布局选择器: 依赖元素的布局和相对位置,使用诸如边界客户矩形等进行计算。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;ru-he-zai-playwright-zhong-xuan-ze-yuan-su&quot;&gt;如何在 Playwright 中选择元素?&lt;&#x2F;h3&gt;
&lt;p&gt;在 Playwright 中选择元素通常涉及使用代表上述选择器之一的字符串。例如:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;CSS 选择器: &lt;code&gt;page.$(&#x27;.className&#x27;)&lt;&#x2F;code&gt; 根据类选择元素。&lt;&#x2F;li&gt;
&lt;li&gt;XPath 选择器: &lt;code&gt;page.$x(&#x27;&#x2F;&#x2F;div&#x27;)&lt;&#x2F;code&gt; 使用 XPath 语法查找元素。&lt;&#x2F;li&gt;
&lt;li&gt;文本选择器: &lt;code&gt;page.$(&#x27;text=Example&#x27;)&lt;&#x2F;code&gt; 查找包含指定文本的元素。&lt;&#x2F;li&gt;
&lt;li&gt;组合选择器: 链接选择器如 &lt;code&gt;page.$(&#x27;div.className &amp;gt; span&#x27;)&lt;&#x2F;code&gt; 以定位特定元素内的元素。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;shen-me-shi-playwright-ding-wei-qi-huo-xuan-ze-qi&quot;&gt;什么是 Playwright 定位器或选择器?&lt;&#x2F;h3&gt;
&lt;p&gt;Playwright 定位器或选择器是用于在 Web 自动化脚本中选择元素的基于字符串的工具。选择器是 Playwright 识别要交互的元素的方式,无论是点击、读取内容还是输入数据。它们可以很简单,比如文本选择器,也可以很复杂,比如链接 CSS 和 XPath 的链式选择器。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wo-men-ke-yi-zai-playwright-zhong-shi-yong-xpath-ma&quot;&gt;我们可以在 Playwright 中使用 XPath 吗?&lt;&#x2F;h3&gt;
&lt;p&gt;是的,XPath 在 Playwright 框架中得到完全支持。它可以用于执行精确的元素选择,特别是在 CSS 选择器可能不够用的场景中。Playwright 中的 XPath 选择器可以使用各种 Axes、谓词和函数来导航 DOM,定位根据结构和内容(包括文本节点和属性)定位元素非常有用。例如,&lt;code&gt;page.$x(&#x27;&#x2F;&#x2F;button[@id=&quot;submit&quot;]&#x27;)&lt;&#x2F;code&gt; 将定位 ID 为 &quot;submit&quot; 的按钮。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;bugbug.io&#x2F;blog&#x2F;testing-frameworks&#x2F;playwright-selectors&#x2F;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>🎭Playwright: 如何正确使用 fixture 构建页面对象</title>
        <published>2024-01-22T00:00:00+00:00</published>
        <updated>2024-01-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395/"/>
        <id>https://playwright.itest.info/blog/playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395/">&lt;p&gt;本文适合测试自动化工程师或参与测试的开发人员阅读,特别是那些希望提升测试框架技能,使用页面对象模型来组织、维护测试并处理它们之间关系的人。对于已经在使用这种方法但想听听不同观点的工程师来说,本文也很有价值。&lt;&#x2F;p&gt;
&lt;p&gt;让我们来看一个简单的测试场景:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395&#x2F;.&#x2F;1.png&quot; alt=&quot;图片1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;首先,我们需要打开主页(&#x27;https:&#x2F;&#x2F;www.saucedemo.com&#x2F;&#x27;)。页面打开后,我们对其进行验证。然后,我们需要获取适当的选择器来填写用户名和密码字段,并点击&quot;登录&quot;按钮。
但是,如果我们想添加新的测试用例,使用另一种类型的用户(比如管理员用户)来进行登录的话，我们至少需要复制一次代码。
更糟糕的是,如果前端应用中的定位器发生变化,比如从&lt;strong&gt;data-test=&quot;login-button&quot;&lt;strong&gt;变成&lt;&#x2F;strong&gt;data-test=&quot;sign-in-button&quot;&lt;&#x2F;strong&gt;,我们就需要检查所有使用这个定位器的测试场景并进行修复。这种错误的修正非常耗时。&lt;&#x2F;p&gt;
&lt;p&gt;这个问题的解决方案就是使用页面对象模型。
简单解释如下:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395&#x2F;.&#x2F;2.png&quot; alt=&quot;图片2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;网上有大量关于页面对象模型的文章、视频和教程。现在,我想分享我对如何正确构建页面的看法。&lt;&#x2F;p&gt;
&lt;p&gt;(如果你需要更多示例,请访问 &amp;gt; &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;VadimNastoyashchy&#x2F;playwright-saucedemo.com&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;VadimNastoyashchy&#x2F;playwright-saucedemo.com&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395&#x2F;.&#x2F;3.png&quot; alt=&quot;图片3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆test.js&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395&#x2F;.&#x2F;4.png&quot; alt=&quot;图片4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆 登录页面&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395&#x2F;.&#x2F;5.png&quot; alt=&quot;图片5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆 页面抽象&lt;&#x2F;p&gt;
&lt;p&gt;对于组件,我倾向于使用相同的粒度:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395&#x2F;.&#x2F;6.png&quot; alt=&quot;图片6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆header 组件&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395&#x2F;.&#x2F;7.png&quot; alt=&quot;图片7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;组件抽象 👆&lt;&#x2F;p&gt;
&lt;p&gt;现在看起来好多了。但是,我们仍然需要在每个测试块中使用{ page }fixture 来初始化我们的页面:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Login with &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\\&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;standard&lt;&#x2F;span&gt;&lt;span&gt;\\&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt; user with set cookies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;LoginPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inventoryPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;InventoryPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;接下来,我将给出类和方法的例子。让我们仔细看看 Fixtures!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395&#x2F;.&#x2F;8.png&quot; alt=&quot;图片8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;首先,我们需要创建一个文件。让我们命名为 FixtureConfig.ts
然后,我们需要定义页面的类型并覆盖项目的 fixture 配置:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;LoginPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;pages&#x2F;LoginPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;InventoryPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;pages&#x2F;InventoryPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ApiService &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;ApiService&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span&gt;MyFixtures &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;LoginPage,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inventoryPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;InventoryPage,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiService&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;ApiService,
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;extend &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;MyFixtures &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  {
&lt;&#x2F;span&gt;&lt;span&gt;    loginPage: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;LoginPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    inventoryPage: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inventoryPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;InventoryPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inventoryPage&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    apiService: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiService &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;ApiService(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiService&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;最后,你需要在测试文件中更改导入语句
从:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395&#x2F;.&#x2F;9.png&quot; alt=&quot;图片9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;改为:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395&#x2F;.&#x2F;10.png&quot; alt=&quot;图片10&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;现在,你可以在测试中使用 Fixture,通过包含所有在 FixtureConfig.ts 文件中定义的页面的 fixture 对象:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Login and Logout test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Login with &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\\&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;standard&lt;&#x2F;span&gt;&lt;span&gt;\\&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt; user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;inventoryPage &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getPageUrl&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;logInWithCredentials&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Credentials&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getUserCredentials&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;AccountType&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Standard&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inventoryPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getPageUrl&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;文章中使用的资源:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-fixtures&quot;&gt;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-fixtures&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;VadimNastoyashchy&#x2F;playwright-saucedemo.com&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;VadimNastoyashchy&#x2F;playwright-saucedemo.com&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;来源 URL: https:&#x2F;&#x2F;medium.com&#x2F;@vadym_nastoiashhyi&#x2F;playwright-how-to-build-page-object-correctly-using-fixtures-ff0a8c05b395&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2024-01-22T07:01:37.131Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright 自定义缩进列表测试报告 📖</title>
        <published>2024-01-08T00:00:00+00:00</published>
        <updated>2024-01-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/custom-playwright-indent-list-reporter/"/>
        <id>https://playwright.itest.info/blog/custom-playwright-indent-list-reporter/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/custom-playwright-indent-list-reporter/">&lt;p&gt;你是否曾想要让你的命令行界面更加 🌶️ 呢？好消息是，&lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;syzanakajtazaj&#x2F;&quot;&gt;Syzana Bicaj&lt;&#x2F;a&gt;发布了一个 npm 包，不仅允许你自定义颜色，还可以在终端中缩进显示 spec 文件，便于阅读！&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2024&#x2F;01&#x2F;image-2.png&quot; alt=&quot;图片1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;要使用默认设置安装该包，你需要在 Playwright 目录下运行以下命令：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; install indent-list-reporter&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --save-dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这将安装该库，之后你只需要在&lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt;文件中更新&lt;code&gt;[&quot;indent-list-reporter&quot;]&lt;&#x2F;code&gt;。我下面的例子使用了三元运算符，首先检查环境变量 CI 是否为 true 或 undefined&#x2F;false。如果为 true，则运行带有&quot;?&quot;的顶行；如果为 false（从本地机器运行 playwright 命令），则使用&quot;html&quot;和&quot;indent-list-reporter&quot;报告。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;APIRequestOptions &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span&gt;TestOptions&amp;gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;reporter&lt;&#x2F;span&gt;&lt;span&gt;: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span&gt;[[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;list&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@currents&#x2F;playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;[[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;indent-list-reporter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;设置完成后，你可以运行测试，在测试运行完成后看到漂亮的结果。是的，这有一个缺点，原始的测试报告的好处: 是它会在每个测试完成时显示结果，而使用&lt;code&gt;indent-list-reporter&lt;&#x2F;code&gt;，除非用例全部执行完，否则你将无法看到测试结果（除非你同时保留自带的测试报告)。单独使用这个测试报告模版时，这是一个小小的权衡。&lt;&#x2F;p&gt;
&lt;p&gt;你还可以选择更改配色方案，在报告配置的数组中，你可以添加一个包含新基础颜色的对象。查看&lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;indent-list-reporter&quot;&gt;文档&lt;&#x2F;a&gt;了解提供的不同颜色。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;APIRequestOptions &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span&gt;TestOptions&amp;gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;reporter&lt;&#x2F;span&gt;&lt;span&gt;: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span&gt;[[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;list&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@currents&#x2F;playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;[
&lt;&#x2F;span&gt;&lt;span&gt;        [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;        [
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;indent-list-reporter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;          {
&lt;&#x2F;span&gt;&lt;span&gt;            baseColors: {
&lt;&#x2F;span&gt;&lt;span&gt;              specFileNameColor: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;white&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;              suiteDescriptionColor: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;blue&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;              testCaseTitleColor: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;magenta&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            },
&lt;&#x2F;span&gt;&lt;span&gt;          },
&lt;&#x2F;span&gt;&lt;span&gt;        ],
&lt;&#x2F;span&gt;&lt;span&gt;      ],
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2024&#x2F;01&#x2F;image-1.png&quot; alt=&quot;图片2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;npm 包可以在这里找到：&lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;indent-list-reporter&quot;&gt;indent-list-reporter&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;custom-playwright-indent-list-reporter&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2024-01-08T13:30:14.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright 使用技巧与诀窍 #2</title>
        <published>2024-01-08T00:00:00+00:00</published>
        <updated>2024-01-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-tips-and-tricks-2/"/>
        <id>https://playwright.itest.info/blog/playwright-tips-and-tricks-2/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-tips-and-tricks-2/">&lt;p&gt;我之前写了一篇&lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-tips-and-tricks-1&#x2F;&quot;&gt;关于使用技巧和诀窍的文章&lt;&#x2F;a&gt;,获得了很多好评。因此,我决定再写一篇。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-ru-he-zai-playwright-zhong-chu-li-ye-mian-wan-quan-jia-zai-hou-cai-chu-xian-de-yuan-su&quot;&gt;1. 如何在 Playwright 中处理页面完全加载后才出现的元素&lt;&#x2F;h3&gt;
&lt;p&gt;有时候,即使 Playwright 等待页面完全加载,某些元素可能仍然在之后才出现在 DOM 中。这种情况下,我推荐以下几个技巧:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;方法一,等待网络空闲: &lt;code&gt;page.goto(&#x27;https:&#x2F;&#x2F;playwright.dev&#x2F;&#x27;, {waitUntil: &#x27;networkidle&#x27;})&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;方法二,等待元素达到特定状态。使用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locator?ref=blog.martioli.com#locator-wait-for&quot;&gt;element.waitFor(state)&lt;&#x2F;a&gt;。默认值是&quot;visible&quot;,但你可以更改为等待元素出现在 DOM 中,比如先执行 &lt;code&gt;const element = page.locator(&#x27;#locator&#x27;)&lt;&#x2F;code&gt;,然后 &lt;code&gt;await element.waitFor(&quot;attached&quot;)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;2-zi-ding-yi-expect-xiao-xi&quot;&gt;2. 自定义 expect 消息&lt;&#x2F;h3&gt;
&lt;p&gt;你知道吗?当断言失败时,你可以使用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions?ref=blog.martioli.com#custom-expect-message&quot;&gt;自定义的 expect 消息&lt;&#x2F;a&gt;: &lt;code&gt;await expect(locatorOrValue, &#x27;执行某操作失败&#x27;).toBe()&lt;&#x2F;code&gt;。这在自定义方法或页面方法中特别有用,可以提供更详细的失败信息。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-ru-he-zai-playwright-zhong-duan-yan-shou-dao-liao-you-jian&quot;&gt;3. 如何在 Playwright 中断言收到了邮件&lt;&#x2F;h3&gt;
&lt;p&gt;假设你有一个注册表单,提交后会发送一封带确认号&#x2F;链接的邮件。由于邮件不会立即出现在收件箱,你可以使用 Playwright 的 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions?ref=blog.martioli.com#expectpoll&quot;&gt;expect.poll&lt;&#x2F;a&gt; 进行&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Polling_&#x2F;(computer_science&#x2F;)?ref=blog.martioli.com&quot;&gt;轮询&lt;&#x2F;a&gt;。请看下面的注释,我会解释其工作原理。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect
&lt;&#x2F;span&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;poll&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;allEmails &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.email.com&#x2F;allEmails&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      );
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在这里进行逻辑处理,例如搜索包含注册表单用户名&#x2F;邮箱的邮件
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;of &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;allEmails&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;to&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;registrationForm@email.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 找到邮件后,编写逻辑从邮件内容中提取链接&#x2F;代码
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;emailCode&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这部分是轮询配置,都是可选的
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 自定义错误消息,超时后触发
&lt;&#x2F;span&gt;&lt;span&gt;      message: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;未能在邮件中找到确认链接&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 探测, 等待1秒, 探测, 等待2秒, 探测, 等待10秒, 探测, 等待10秒, 探测
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ... 默认为 [100, 250, 500, 1000]
&lt;&#x2F;span&gt;&lt;span&gt;      intervals: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1_000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2_000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10_000&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; intervals 中的最后一个值会重复直到超时。超时后会抛出带有自定义消息的错误
&lt;&#x2F;span&gt;&lt;span&gt;      timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;60_000&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  )
&lt;&#x2F;span&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeTruthy&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;请记住,每次未找到邮件时,expect 都会像循环一样继续执行。只有在 60 秒后仍未返回预期的真值时,才会失败。你可以轻松将 &lt;code&gt;toBeTruthy()&lt;&#x2F;code&gt; 更改为任何 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions?ref=blog.martioli.com#non-retrying-assertions&quot;&gt;jest expect&lt;&#x2F;a&gt; 类型的方法,以满足你的特定需求和返回逻辑。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-ru-he-rang-duan-yan-shi-bai-dan-bu-dao-zhi-ce-shi-shi-bai&quot;&gt;4. 如何让断言失败但不导致测试失败?&lt;&#x2F;h3&gt;
&lt;p&gt;首先,可以看看&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions?ref=blog.martioli.com#soft-assertions&quot;&gt;软断言&lt;&#x2F;a&gt;,这可能是你想要的。&lt;&#x2F;p&gt;
&lt;p&gt;或者,你可能在期望某个随机时间间隔出现的东西。Playwright 有一个 expect 方法可以重复执行多个断言,直到全部通过,它叫做 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions?ref=blog.martioli.com#expecttopass&quot;&gt;expect.toPass()&lt;&#x2F;a&gt;。只需将所有断言放在 expect.toPass() 内即可。这个方法的工作原理类似于上面解释的 expect.poll(),但这次我们可以在 expect 方法内执行一个或多个可能失败几次才成功的 expect()。条件是它们必须全部通过。因此,代码块会重复执行,直到满足条件(或超时)。这里有个例子:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;元素在 5 秒后出现&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;webdriveruniversity.com&#x2F;Accordion&#x2F;index.html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;LOADING&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;COMPLETE.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  }).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toPass&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    intervals: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1_000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5_000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10_000&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;60_000&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;注意配置现在放在 toPass({}) 内&lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-ru-he-zai-playwright-zhong-lan-jie-wang-luo-diao-yong&quot;&gt;5. 如何在 Playwright 中拦截网络调用?&lt;&#x2F;h3&gt;
&lt;p&gt;这里我们不讨论 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page?ref=blog.martioli.com#page-route&quot;&gt;page.route()&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;端到端测试有两种类型:&lt;a href=&quot;https:&#x2F;&#x2F;smartbear.com&#x2F;learn&#x2F;automated-testing&#x2F;how-to-perform-end-to-end-testing&#x2F;?ref=blog.martioli.com#vertical-vs-horizontal-endtoend-testing&quot;&gt;水平和垂直&lt;&#x2F;a&gt;。最常见的是水平测试,例如测试购买产品的流程。这是一个用户流程。较少人知道的是垂直测试,它测试 &lt;code&gt;用户操作 -&amp;gt; 前端(UI) -&amp;gt; 后端 -&amp;gt; 数据库&lt;&#x2F;code&gt; 或 &lt;code&gt;数据库 -&amp;gt; 后端 -&amp;gt; 前端 -&amp;gt; 用户所见&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;举个例子,假设点击按钮生成发票,值显示在你账户的 UI 层。前端可能对从后端接收的数据进行计算后显示值。如果显示的值不正确,谁该负责?前端开发人员会说是后端问题,后端开发人员会说是前端问题。听起来熟悉吗?要找到这种 bug,你可以这样做:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;对前端,对将在 UI 中显示的值进行断言&lt;&#x2F;li&gt;
&lt;li&gt;对后端,监听网络调用,在数据到达前端之前断言后端通过 API 发送的内容&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这将在两个层面上进行验证。那么,我们如何在 Playwright 中拦截调用呢?使用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page?ref=blog.martioli.com#page-wait-for-response&quot;&gt;waitForResponse&lt;&#x2F;a&gt;。让我给你一段代码作为进一步说明:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 前面的双星号用于在多个环境中实现动态等待
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 注意 waitForResponse 前面没有 await。它只是标记从何时开始监听该调用
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;invoiceCall &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForResponse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;**&#x2F;invoices&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Generate Invoice&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 默认会等待该调用 30 秒
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;invoiceCall&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 现在将响应转为 json 以便轻松读取
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseAsJson &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseAsJson&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;invoice&lt;&#x2F;span&gt;&lt;span&gt;.value).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;355&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;为什么要关心&quot;从何时开始&quot;这部分?这在你想拦截来自同一 api 接口 的多个调用时很有用。稍后会详细讨论。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;6-ru-he-zai-playwright-zhong-xiang-zhuan-ye-ren-shi-yi-yang-diao-shi&quot;&gt;6. 如何在 Playwright 中像专业人士一样调试?&lt;&#x2F;h3&gt;
&lt;p&gt;这个主题其实值得单独写一篇博文,&lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;debugging-tests-with-playwright&#x2F;&quot;&gt;我在这里写过&lt;&#x2F;a&gt;。但我想指出一点,几乎每个人都忽略了 Playwright 自带的调试器,&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;debug?ref=blog.martioli.com#playwright-inspector&quot;&gt;叫做 inspector&lt;&#x2F;a&gt;,能提供比你想象的多得多的信息。让我告诉你我的意思。假设你有一行代码,你就是搞不明白为什么无法达到你想要的效果。你可以这样做,用 &lt;code&gt;npx playwright test path&#x2F;to&#x2F;yourTest.spec.js --debug&lt;&#x2F;code&gt; 启动测试,然后进入 inspector,打开你想了解更多的操作,你就会看到 Playwright 在后台执行的所有操作:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;playwright-tips-and-tricks-2&#x2F;img&#x2F;1730512502.104141inspector-under-the-hood.png&quot; alt=&quot;Image 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;小提示:我不是很喜欢这个 inspector,因为在目前的 v1.40 版本中,它有点 buggy。但有时它还是很有用的。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;7-ru-he-zai-playwright-zhong-huo-qu-wen-ben-bing-cun-chu-zai-bian-liang-zhong-yi-gong-hou-xu-shi-yong&quot;&gt;7. 如何在 Playwright 中获取文本并存储在变量中以供后续使用?&lt;&#x2F;h3&gt;
&lt;p&gt;Playwright 会建议你将元素传给 expect() 并使用其 toHaveText() 方法来断言元素是否包含某个文本,但有时你只需要从元素中获取文本并在之后的操作中重用它。这种情况通常发生在值不是固定的,而是动态生成的时候。你可以通过 &lt;code&gt;const elementText = await page.locator(locator).innerText()&lt;&#x2F;code&gt; 来实现。这会将元素文本作为字符串存储在 elementText 中,供后续使用。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意,innerText() 只会给你可见的文本。如果文本在 DOM 中但不可见,它不会返回那个值。要包括隐藏的文本,请使用 textContent()&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;如果你觉得这些信息有用,请点赞。如果你想给我更多动力,&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;adrianmaciuc?ref=blog.martioli.com&quot;&gt;买杯咖啡&lt;&#x2F;a&gt;也是个不错的选择。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-tips-and-tricks-2&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2024-01-08&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>使用Playwright进行API契约测试</title>
        <published>2023-12-25T00:00:00+00:00</published>
        <updated>2023-12-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/api-contract-testing-on-frontend-with-playwright-4509b74b3008/"/>
        <id>https://playwright.itest.info/blog/api-contract-testing-on-frontend-with-playwright-4509b74b3008/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/api-contract-testing-on-frontend-with-playwright-4509b74b3008/">&lt;p&gt;有时作为测试工程师,业务对测试的要求可能相当奇怪,你必须在一个测试套件中采用不同类型的测试。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:1000&#x2F;1*ZjhaucB1PZLN6UVIKR4A7Q.jpeg&quot; alt=&quot;图片2: Midjourney提示&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shen-me-shi-qi-yue-ce-shi&quot;&gt;什么是契约测试&lt;&#x2F;h2&gt;
&lt;p&gt;契约测试是一种软件测试类型,专注于验证单独组件&#x2F;服务之间的交互(通常是两个微服务)。当两个微服务通过 API 交互时,一个服务以预定义的格式发送请求,另一个以预定义的格式响应。这种格式被称为&quot;契约&quot; - 服务(甚至开发团队)之间关于如何承诺相互通信的协议。&lt;&#x2F;p&gt;
&lt;p&gt;&quot;契约&quot;可以是 API 规范,但更常见的是仅仅是作为 JSON 文件的请求和响应体架构,这些文件在两个服务之间共享,它们都根据这些架构测试自己的 API - 这种方法甚至被区分为一种单独的测试方法&quot;&lt;a href=&quot;https:&#x2F;&#x2F;pactflow.io&#x2F;blog&#x2F;contract-testing-using-json-schemas-and-open-api-part-1&#x2F;&quot;&gt;基于架构的契约测试&lt;&#x2F;a&gt;&quot;。&lt;&#x2F;p&gt;
&lt;p&gt;在客户端-服务器架构的情况下,前端可以作为各种 API 的消费者或提供者,反之亦然:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:1000&#x2F;1*2jB1pd0jqbgO9DqfPDRFww.png&quot; alt=&quot;图片3: 前端应用作为契约测试的消费者或提供者&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在许多文章中(参见文章末尾的链接),契约测试与集成测试或端到端测试相对立。但在本文中,我想展示&lt;strong&gt;契约测试可以是端到端测试的一部分&lt;&#x2F;strong&gt; - 它可以只是特定检查的工具。&lt;&#x2F;p&gt;
&lt;p&gt;这种情况可能发生在&lt;strong&gt;前端自动测试的特定业务需求&lt;&#x2F;strong&gt;的情况下,例如检查你的前端是否以特定格式向第三方 API 发出特定请求。换句话说,确保 UI 发送正确的数据。&lt;&#x2F;p&gt;
&lt;p&gt;此外,这些第三方 API&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@adequatica&#x2F;layers-of-defense-against-data-modification-d73e9e93bdf7&quot;&gt;可能不允许在测试期间被请求&lt;&#x2F;a&gt;。这看起来显然需要用模拟来&quot;关闭&quot;这些第三方 API,但如果你的测试项目上没有任何复杂的模拟基础设施(和&#x2F;或不想有)怎么办?如果你的&lt;a href=&quot;https:&#x2F;&#x2F;adequatica.medium.com&#x2F;pros-and-cons-of-the-ways-of-end-to-end-automated-testing-in-ci-9bec51e231cb#552a&quot;&gt;测试在实时的模拟环境中运行&lt;&#x2F;a&gt;,可能就是这种情况。在这种情况下,你可以在&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;network&quot;&gt;网络级别通过 Playwright&lt;&#x2F;a&gt;来&quot;关闭&quot;对第三方 API 的请求。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;在互联网上找到类似的项目并不是问题。有很多 DeFi 初创公司使用开放 API 作为他们的基础设施,但这些 API 大多是 GraphQL 和 JSON-RPC - 这给示例增加了一点复杂性。描述它们与 REST API 的区别不是本文的主题。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;zen-me-zuo-qi-yue-ce-shi&quot;&gt;怎么做契约测试&lt;&#x2F;h2&gt;
&lt;p&gt;至少我找到了&lt;a href=&quot;https:&#x2F;&#x2F;www.sushi.com&#x2F;swap&quot;&gt;Sushi&lt;&#x2F;a&gt;加密货币交换页面,其前端只向第三方 API 发出几个所需的 POST 请求(API 的 URL 与当前网站不同):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:1000&#x2F;1*n66Kj3vIydqm_7FfG1Fe2Q.png&quot; alt=&quot;图片4: 网站的前端向第三方API发出POST请求&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;同样的情况在图示表示中如下所示:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:1000&#x2F;1*DYYfRmMtf_T6Qsz2DFTYGw.png&quot; alt=&quot;图片5: 网站的前端向第三方API发出POST请求&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;让我提醒你,我关注第三方 API 是因为检查内部 API 不是本文的主题 - 你可以通过内部 API 测试和&#x2F;或集成测试来检查你的内部 API。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;在契约测试中,假定每个组件&#x2F;服务都是相互隔离的。在这里,你可以使用 Playwright 的网络功能轻松地将前端与第三方 API 隔离:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;粗略地&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;network#abort-requests&quot;&gt;中止请求&lt;&#x2F;a&gt; - 请求&lt;strong&gt;不会&lt;&#x2F;strong&gt;发送到外部 API;&lt;&#x2F;li&gt;
&lt;li&gt;或者&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;mock#mock-api-requests&quot;&gt;模拟它&lt;&#x2F;a&gt;。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;对于第二种情况,如果你只使用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-route#route-fulfill&quot;&gt;fulfill()&lt;&#x2F;a&gt;类,你可以通过中止请求来修改响应。但如果你将&lt;code&gt;fulfill()&lt;&#x2F;code&gt;与&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-route#route-fetch&quot;&gt;fetch()&lt;&#x2F;a&gt;一起使用,请求将被发送到外部 API。无论哪种方式,&lt;strong&gt;当你用 JSON 填充响应体时 - 你就在进行契约测试&lt;&#x2F;strong&gt;(检查客户端是否正在处理填充的响应),如果这个 JSON 模式与外部 API 端用于测试的模式相同。&lt;&#x2F;p&gt;
&lt;p&gt;对于这两种情况,&lt;strong&gt;你通过&lt;&#x2F;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page#page-wait-for-request&quot;&gt;&lt;strong&gt;waitForRequest()&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;strong&gt;类拦截请求,以测试 POST 的&lt;&#x2F;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-request#request-post-data&quot;&gt;&lt;strong&gt;请求体&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&lt;strong&gt;是否符合你的契约&lt;&#x2F;strong&gt;(当然,对于 PUT 或 PATCH 方法也是如此):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:1000&#x2F;1*MQiNBfNwaPX0LmbR8anUhw.png&quot; alt=&quot;图片6: 通过Playwright拦截HTTP请求&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果你的请求体是 JSON 格式(我认为这种情况会占 90%),你可以立即使用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-request#request-post-data-json&quot;&gt;postDataJSON()&lt;&#x2F;a&gt;类,通过你喜欢的工具比较 JSON 模式:&lt;a href=&quot;https:&#x2F;&#x2F;ajv.js.org&#x2F;json-schema.html&quot;&gt;Ajv&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;zod.dev&#x2F;&quot;&gt;Zod&lt;&#x2F;a&gt;,或者如果由于某些原因你决定直接比较两个 JSON 对象,可以使用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-genericassertions#generic-assertions-to-equal&quot;&gt;toEqual()&lt;&#x2F;a&gt;断言。&lt;&#x2F;p&gt;
&lt;p&gt;当你只检查请求的契约时,你可能不需要响应,可以简单地中止它(注意,正确的行为取决于你的应用,也许你必须模拟响应以防止应用崩溃):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:1000&#x2F;1*auBhQDfBeHiJFU1s_9HVFw.png&quot; alt=&quot;图片7: route.abort()在Playwright Inspector中的工作原理&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这里是这样一个测试的&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;adequatica&#x2F;ui-testing&#x2F;blob&#x2F;main&#x2F;tests&#x2F;sushi-swap-contract-testing.spec.ts&quot;&gt;代码示例&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;type Page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;z &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;zod&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 契约
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schema &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;z&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  jsonrpc: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;z&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;  id: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;z&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;  method: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;z&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;  params: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;z&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;z&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;union&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;z&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;(), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;z&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;boolean&lt;&#x2F;span&gt;&lt;span&gt;()])),
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lb&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;drpc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;org&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ogrpc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\?&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;network=ethereum&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;method&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;abort&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Open Sushi Swap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 等待请求应该在.goto()方法之前,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 因为所需的请求可能在页面完全加载之前完成。
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestPromise &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForRequest&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lb.drpc.org&#x2F;ogrpc?network=ethereum&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp;&amp;amp;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;method&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;swap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestPromise&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schema&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;postDataJSON&lt;&#x2F;span&gt;&lt;span&gt;()),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Should have a request by the contract&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  ).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;not&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toThrowError&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;其中,&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;const schema&lt;&#x2F;code&gt;是&lt;a href=&quot;https:&#x2F;&#x2F;zod.dev&#x2F;&quot;&gt;Zod&lt;&#x2F;a&gt;格式的模式声明;&lt;&#x2F;li&gt;
&lt;li&gt;在&lt;code&gt;beforeAll&lt;&#x2F;code&gt;钩子中,所有匹配&lt;code&gt;https:&#x2F;&#x2F;lb.drpc.org&#x2F;ogrpc?network=ethereum&amp;amp;dkey=Ak765fp4zUm6uVwKu4annC8M80dnCZkR7pAEsm6XXi_w&lt;&#x2F;code&gt;的 POST 请求都被阻止;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;const requestPromise&lt;&#x2F;code&gt;接收匹配&lt;code&gt;https:&#x2F;&#x2F;lb.drpc.org&#x2F;ogrpc?network=ethereum&amp;amp;dkey=Ak765fp4zUm6uVwKu4annC8M80dnCZkR7pAEsm6XXi_w&lt;&#x2F;code&gt;的第一个请求的数据;&lt;&#x2F;li&gt;
&lt;li&gt;在&lt;code&gt;expect()&lt;&#x2F;code&gt;断言中,参考模式与请求的数据进行解析。如果解析&#x2F;验证过程没有失败,测试就通过 - &lt;code&gt;[toThrowError()](https:&#x2F;&#x2F;jestjs.io&#x2F;docs&#x2F;expect#tothrowerror)&lt;&#x2F;code&gt;。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;上面呈现的测试可能包含更多步骤和检查,因为契约检查可能只是端到端套件的一部分。&lt;&#x2F;p&gt;
&lt;p&gt;阅读更多关于契约测试的内容:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;pactflow.io&#x2F;blog&#x2F;what-is-contract-testing&#x2F;&quot;&gt;什么是契约测试,为什么我应该尝试它&lt;&#x2F;a&gt;?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;pactflow.io&#x2F;blog&#x2F;contract-testing-vs-integration-testing&#x2F;&quot;&gt;契约测试 vs 集成测试&lt;&#x2F;a&gt;;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;testsigma.com&#x2F;blog&#x2F;api-contract-testing&#x2F;&quot;&gt;API 契约测试完全指南&lt;&#x2F;a&gt;;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.postman.com&#x2F;api-contract-testing-4-things-to-validate&#x2F;&quot;&gt;API 契约测试:4 个需要验证以满足期望的事项&lt;&#x2F;a&gt;;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=RSl_JcWKE3M&quot;&gt;契约测试:解锁 CI&#x2F;CD 管道中 E2E 测试瓶颈的关键&lt;&#x2F;a&gt;。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;此外,理论上,相同的模拟方法可以应用于前端的所有 HTTP API 请求:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:1000&#x2F;1*3pflE2qXx7QlMt7b4A1enQ.png&quot; alt=&quot;图片8: 模拟API&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;adequatica.medium.com&#x2F;api-contract-testing-on-frontend-with-playwright-4509b74b3008&quot;&gt;来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-12-25&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright 会取代 Cypress 吗?</title>
        <published>2023-12-18T00:00:00+00:00</published>
        <updated>2023-12-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-ask-me-anything-debrief-will-playwright-replace-cypress/"/>
        <id>https://playwright.itest.info/blog/playwright-ask-me-anything-debrief-will-playwright-replace-cypress/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-ask-me-anything-debrief-will-playwright-replace-cypress/">&lt;p&gt;上周我有幸参加了由 Ministry of Testing 主办的&lt;a href=&quot;https:&#x2F;&#x2F;www.ministryoftesting.com&#x2F;events&#x2F;testing-ask-me-anything-on-playwright&quot;&gt;Playwright 交流活动&lt;&#x2F;a&gt;。在一小时的活动中,我们收到了大量问题,我认为值得在几篇文章中深入探讨。&lt;&#x2F;p&gt;
&lt;p&gt;今天我计划简要介绍,并回答以下问题:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Playwright 会取代 Cypress 吗?使用 Playwright 进行 Web 应用自动化测试时,最大的挑战是什么?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;playwright-hui-qu-dai-cypress-ma&quot;&gt;Playwright 会取代 Cypress 吗?&lt;&#x2F;h2&gt;
&lt;p&gt;不会的...&lt;&#x2F;p&gt;
&lt;p&gt;为什么呢?过去 5 年里,数以千计的团队已经在项目中采用了 Cypress 作为测试框架。对大多数团队和项目来说,为了获得微小的商业价值而将这些项目转换为 Playwright,成本上并不允许。更有可能的是,团队从切换框架中获得的价值主要是能够并行运行测试,这直接影响开发和质量团队,而不是增加实际的业务价值。在大多数组织中,这样的项目很难获得批准。&lt;&#x2F;p&gt;
&lt;p&gt;只要 Cypress 能提供价值和有用的反馈,我认为它就不会消失。社区已经建立了许多解决方案,包括通过使用&lt;code&gt;cy.task()&lt;&#x2F;code&gt;在 Cypress 测试中运行 Playwright&#x2F;Selenium 的能力。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;eleks.com&#x2F;blog&#x2F;cypress-framework-playwright-selenium-injections&#x2F;&quot;&gt;增强 Cypress 框架&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hui-you-geng-duo-de-tuan-dui-zai-kai-shi-xin-xiang-mu-shi-hui-xuan-ze-shi-yong-playwright-ma&quot;&gt;会有更多的团队在开始新项目时会选择使用 Playwright 吗?&lt;&#x2F;h2&gt;
&lt;p&gt;是的,我相信会的!Playwright 相对于 Cypress 的优势在于,它允许我按照自己的意愿在任何地方运行测试,使用任何自定义 reporter,这使得选择 使用 playwright 成了一个非常自然的决定。&lt;&#x2F;p&gt;
&lt;p&gt;对我来说,Cypress.io 还有很多未知因素。我们知道他们曾&lt;a href=&quot;https:&#x2F;&#x2F;www.prnewswire.com&#x2F;news-releases&#x2F;cypressio-raises-40m-in-series-b-to-deliver-the-next-era-of-software-testing-301194275.html&quot;&gt;募集 4000 万美元 B 轮融资&lt;&#x2F;a&gt;,我相信他们正在寻找建立可持续开展业务的方法(写这篇文章时,距离 B 轮融资已经过去 3 年了)。&lt;&#x2F;p&gt;
&lt;p&gt;虽然 Playwright 的亲爹是微软,仍有一些未知因素,微软的项目也是有可能被砍掉的。但当我看到他们为开发者社区构建和提供的工具时,&lt;strong&gt;我会把赌注押在微软身上&lt;&#x2F;strong&gt;。看看 TypeScript、VS Code、.Net Core 等,我对 Playwright 的未来并不太担心。&lt;&#x2F;p&gt;
&lt;p&gt;通常不会...&lt;&#x2F;p&gt;
&lt;p&gt;一般情况下除非工具或者框架进入生命周期的末期，我们通常不用担心它会被砍掉!但是，最近的例子是&lt;a href=&quot;https:&#x2F;&#x2F;blog.testproject.io&#x2F;2022&#x2F;11&#x2F;17&#x2F;testproject-end-of-life-your-questions-answered&#x2F;#:~:text=When%20and%20how%20does%20this,the%20platform%20will%20be%20revoked.&quot;&gt;Test Project&lt;&#x2F;a&gt;。该团队宣布这个工具不维护了,并提供了将测试用例迁移到他们拥有的其他工具的方法。这个工具最初是一个用于 Web、移动和 API 测试的免费自动化测试平台。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;12&#x2F;image-4.png&quot; alt=&quot;图片3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;na-selenium-ni&quot;&gt;那 Selenium 呢?&lt;&#x2F;h2&gt;
&lt;p&gt;我不能不提到老牌的 Selenium。实际上,我认为 Selenium 是一个很棒的项目。它不像 Playwright (TS&#x2F;JS)和 Cypress.io 那样是一个测试框架,selenium 是一个允许你与 Web 浏览器交互的通用工具。如果你想把它用作测试框架,你需要自带测试框架。&lt;&#x2F;p&gt;
&lt;p&gt;有太多项目已经有了数百甚至上千个测试,出于和第一部分相同的原因,迁移 1000 个正在为组织带来价值的 UI 测试并没有业务价值,因此不应该被优先考虑。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ni-de-ce-shi-shi-fou-ti-gong-jia-zhi&quot;&gt;你的测试是否提供价值?&lt;&#x2F;h2&gt;
&lt;p&gt;我提到的一个重要问题是...你的测试是否为团队提供了价值?无论它们是用什么语言或框架编写的,重要的是它们是否有价值。&lt;&#x2F;p&gt;
&lt;p&gt;如果答案是否定的,那就放弃它们;如果答案是肯定的,那就想办法来维护它们,并继续让它们发光发热。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读!如果你觉得这篇文章有帮助,请在&lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt;上联系我,或考虑&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;为我买杯咖啡&lt;&#x2F;a&gt;。如果你想获得更多内容直接发送到你的邮箱,请在下方订阅,别忘了留下 ❤️ 表示支持。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;playwright-ask-me-anything-debrief-will-playwright-replace-cypress&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-12-18T13:30:09.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>使用 Playwright 的feature map框架改进 UI测试用例的可观测性</title>
        <published>2023-12-11T00:00:00+00:00</published>
        <updated>2023-12-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/tracking-automated-ui-testing-using-a-feature-map-with-playwright/"/>
        <id>https://playwright.itest.info/blog/tracking-automated-ui-testing-using-a-feature-map-with-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/tracking-automated-ui-testing-using-a-feature-map-with-playwright/">&lt;p&gt;几周前，&lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;ben-f-44778426&#x2F;&quot;&gt;Ben Fellows&lt;&#x2F;a&gt; 创建了一个 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;posts&#x2F;ben-f-44778426_as-a-qa-service-provider-building-automation-activity-7130958531586916352-qygv&quot;&gt;LinkedIn 帖子&lt;&#x2F;a&gt;，询问他在工作中经常遇到的问题的解决方案。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;作为构建自动化的 QA 服务提供商，我面临的一个挑战是让客户看到并参与测试计划。我尝试过 TestPad、TestRail、Sheets，甚至是像 Jira 这样更项目导向的方法。有一段时间，我只是使用 Playwright 报告，但遭到了反对。有没有人认为有一个很好的解决方案可以让非技术参与者查看自动化的实时测试套件？&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;我回应了我的解决方案，利用 feature map(特性地图)来跟踪我的自动化进度。在本文中，我将演示如何将 &lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;feature-map&quot;&gt;feature-map&lt;&#x2F;a&gt; npm 包实现到 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-practicesoftwaretesting.com&quot;&gt;playwright-practicesoftwaretesting.com&lt;&#x2F;a&gt; 仓库中。特性地图最初是作为一个 Excel 电子表格开始的，在那里我跟踪系统的各个区域和我们想要用自动化覆盖的场景。从那里，&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;author&#x2F;sergei&#x2F;&quot;&gt;Sergei Gapanovich&lt;&#x2F;a&gt; 接受了这个想法，并编写了驱动 feature-map 包的代码。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gai-shu-feature-map-bao&quot;&gt;概述：feature-map 包&lt;&#x2F;h2&gt;
&lt;p&gt;feature-map 包是一个基本的库，它允许您创建一个包含网站内不同操作&#x2F;特性的 yaml 文件，并添加一个 &lt;code&gt;true&lt;&#x2F;code&gt; 或 &lt;code&gt;false&lt;&#x2F;code&gt; 值以指示该特性是否有自动化覆盖。&lt;&#x2F;p&gt;
&lt;p&gt;这个工具的主要目的是创建一个通过 UI 可以在 web 应用程序中执行的所有不同操作的地图，并对其进行映射。基于此地图，我们可以指示是否有任何自动化覆盖来行使该功能，如果有，可以标记为 &lt;code&gt;true&lt;&#x2F;code&gt;。通过这种功能，我们可以跟踪我们正在测试的 UI 的整体测试覆盖率。我喜欢把它想象成一个测量棒。我们创建一个项目列表，然后我可以用它来衡量我或我的团队的测试覆盖率进度。&lt;&#x2F;p&gt;
&lt;p&gt;这个方法的好处是所有这些都可以通过 yaml 文件创建和维护，该文件也会提交到与您的测试代码相同的仓库中。因此，当您添加覆盖新操作的自动化覆盖时，您也可以更新 feature-map，并获得有关有多少特性有任何自动化覆盖的反馈。&lt;&#x2F;p&gt;
&lt;p&gt;你说的百分比是测试覆盖率百分比吗？嗯，某种程度上是。我知道有些人不喜欢讨论测试覆盖率或自动化覆盖率的百分比。当你真的思考它时，将这种未知的 &lt;a href=&quot;https:&#x2F;&#x2F;www.workroom-productions.com&#x2F;wicked-problems&#x2F;&quot;&gt;棘手问题&lt;&#x2F;a&gt; 的绝对百分比放在一起是非常困难的。所以当我思考和讨论百分比时，它是确定我们想要衡量的事物（网站中的操作），将它们添加到 yaml 中，并衡量是否有任何覆盖这些操作的覆盖率。我对这种“自动化覆盖”和跟踪方式感到满意，就像我们正在构建的测量棒一样。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;12&#x2F;image-2.png&quot; alt=&quot;Image 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆 复古测量 📏&lt;&#x2F;p&gt;
&lt;p&gt;一旦我们开始实现 feature-map 包，我们的输出将如下所示。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;12&#x2F;image-1.png&quot; alt=&quot;Image 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-xian-feature-map-bao&quot;&gt;实现 feature-map 包&lt;&#x2F;h2&gt;
&lt;p&gt;现在进入有趣的部分，下面您可以找到 npm 包的链接，其中包含关于如何将其实现到您自己的仓库中的详细说明。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;feature-map&quot;&gt;feature-map&lt;&#x2F;a&gt; 是手动跟踪项目中特性自动化覆盖进度的工具。&lt;&#x2F;p&gt;
&lt;p&gt;我们的第一步是将该包安装到我们的项目中，为此，请从目录的根目录运行以下命令。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;npm install feature-map
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;下一步是在您的仓库中创建一个 yaml 文件，这将是我们的特性地图。我创建了一个名为 &lt;code&gt;featureMap.yml&lt;&#x2F;code&gt; 的文件，并开始构建我项目中的 UI 中发现的特性。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;auth&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;sign in with google&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;register your account&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;forgot password&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;auth&#x2F;forgot-password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;set new password&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;auth&#x2F;register&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;first name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;last name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;date of birth&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;address&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;postcode&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;city&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;country&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;phone rate&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;e-mail address&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;register button&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;category&#x2F;hand-tools&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;header&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;sidebar&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;sort&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;filters&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;by brand&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;product card&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;image zoom&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;price&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pagination&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;previous&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;product&#x2F;{id}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;header&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;product details&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;price&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;quantity&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;add to cart&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;add to favorites&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;related products&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;more information&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;footer&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我们还没有在特性地图上列出网站内的所有操作，这将是一个待办事项，我们将在构建测试指标时继续构建。&lt;&#x2F;p&gt;
&lt;p&gt;在我们深入了解 yaml 文件是什么以及每个部分代表什么之前，有两个关键术语需要理解，以便完全理解 yaml 文件。我们将在 yaml 文件中使用 &lt;code&gt;Collections&lt;&#x2F;code&gt;。集合可以是：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;序列（列表&#x2F;数组）&lt;&#x2F;li&gt;
&lt;li&gt;映射（字典&#x2F;散列）&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;首先，每当您看到短划线 (&lt;code&gt;-&lt;&#x2F;code&gt;) 时，它用于表示列表项或元素。因此，在最高层次上，我们使用这些序列（列表）来组织我们文件中的页面。&lt;&#x2F;p&gt;
&lt;p&gt;映射可以被认为是 yaml 文件中的键值对。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;- page: &quot;&#x2F;auth&#x2F;login&quot;&lt;&#x2F;code&gt;：这行定义了一个新的 &lt;strong&gt;列表&lt;&#x2F;strong&gt;，URL 路径为 &quot;&#x2F;auth&#x2F;login&quot;。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;features:&lt;&#x2F;code&gt; 这行开始一个与当前页面相关的 &lt;strong&gt;特性&lt;&#x2F;strong&gt; 列表。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;sign in with google: false&lt;&#x2F;code&gt;：这行是一个 &lt;strong&gt;映射&lt;&#x2F;strong&gt; 示例，定义了一个名为 &quot;sign in with google&quot; 的特性&#x2F;操作，并将其值设置为 &lt;code&gt;false&lt;&#x2F;code&gt;。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这种结构的好处是，您可以在页面内映射每个特性，甚至在需要时深入到多个弹出窗口，以便跟踪覆盖情况，重要的是每个列表（序列）都以键值对（映射）结束。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 有效
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;category&#x2F;hand-tools&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;header&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;sidebar&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;sort&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;filters&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;by brand&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;product_popup&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;additional details&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;high resolution image&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;download button&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 无效
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;category&#x2F;hand-tools&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;header&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;sidebar&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;sort&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;filters&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;by brand&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;product_popup&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;additional details&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;high resolution image&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;download button&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 注意这里没有列出键&#x2F;值对
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;更多关于 yaml 文件格式的详细信息可以在下面找到！
&lt;a href=&quot;https:&#x2F;&#x2F;www.freecodecamp.org&#x2F;news&#x2F;what-is-yaml-the-yml-file-format&#x2F;#yaml&quot;&gt;yaml 介绍&lt;&#x2F;a&gt;
接下来，我们将介绍如何与 Playwright 集成。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yu-playwright-de-ji-cheng&quot;&gt;与 Playwright 的集成&lt;&#x2F;h2&gt;
&lt;p&gt;在 yaml 文件中创建特性列表后，下一步是更新我们的测试计划，以确保我们有覆盖这些特性的自动化测试，并更新 feature-map。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-practicesoftwaretesting.com&quot;&gt;playwright-practicesoftwaretesting.com&lt;&#x2F;a&gt; 仓库中有一个 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-practicesoftwaretesting.com&#x2F;blob&#x2F;main&#x2F;tests&#x2F;registration.ts&quot;&gt;测试用例&lt;&#x2F;a&gt;，它覆盖了我们特性文件中的 &lt;code&gt;&#x2F;auth&#x2F;register&lt;&#x2F;code&gt; 页面，我们将确保更新我们的特性文件，并在项目中测试我们的 yaml 文件是否按预期工作。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;featureMap &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;feature-map&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;My feature test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Registration page - All fields and elements exist&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  }) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;auth&#x2F;register&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;yamlMap &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;featureMap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;path&#x2F;to&#x2F;your&#x2F;yaml&#x2F;file&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;input[name=&amp;quot;first name&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;John&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;yamlMap&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;set&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;auth&#x2F;register&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;first name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;input[name=&amp;quot;last name&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Doe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;yamlMap&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;set&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;auth&#x2F;register&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;last name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Similarly for other fields
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Save updated map
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;yamlMap&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;save&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我们添加了一些指令来更新 &lt;code&gt;featureMap.yml&lt;&#x2F;code&gt; 文件中各个字段的值。添加字段并填写它们的内容时，将相应的 yaml 项目值更新为 &lt;code&gt;true&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;重要的是要记住，我们在特性映射文件中指定的每个元素和特性都必须在我们的自动化测试中得到适当的验证和覆盖。&lt;&#x2F;p&gt;
&lt;p&gt;您可能需要根据项目和测试套件的需求调整路径。&lt;&#x2F;p&gt;
&lt;p&gt;这就是我们实现特性地图以更好地跟踪自动化进度的方法。通过这种方法，我们可以在项目中创建一个交互式和透明的自动化覆盖地图。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-lun&quot;&gt;结论&lt;&#x2F;h2&gt;
&lt;p&gt;这个简单的工具和方法可以在更大程度上与您的团队和利益相关者一起工作，确保他们参与并了解 UI 测试的覆盖情况。这不仅可以提高自动化测试的质量，还可以确保项目中的所有关键功能都被覆盖。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;tracking-automated-ui-testing-using-a-feature-map-with-playwright&#x2F;&quot;&gt;来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-12-11&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright 使用技巧与诀窍  #1</title>
        <published>2023-12-07T00:00:00+00:00</published>
        <updated>2023-12-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-tips-and-tricks-1/"/>
        <id>https://playwright.itest.info/blog/playwright-tips-and-tricks-1/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-tips-and-tricks-1/">&lt;p&gt;当你使用一个框架工作时，你会开始遇到各种可以学习的情况。有些事情非常重要，但并不容易仅通过阅读文档或跟随教程就能发现。这些都是通过经验积累的。我想分享一些我最近关于 Playwright 学到的知识。&lt;&#x2F;p&gt;
&lt;p&gt;在下面的陈述中，我将谈论使用&lt;code&gt;page.locator()&lt;&#x2F;code&gt;作为定位器方法的占位符，但这并不意味着我建议只使用&lt;code&gt;locator()&lt;&#x2F;code&gt;。关于这个话题，我实际上推荐使用 Playwright 创建的内置定位器方法，主要是&lt;code&gt;getByTestId()&lt;&#x2F;code&gt;，如果你尝试了所有方法都不起作用，那么再使用&lt;code&gt;.locator()&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;1-ru-he-zai-zhi-you-fu-yuan-su-you-wei-yi-id-de-qing-kuang-xia-cha-zhao-zi-yuan-su&quot;&gt;1. 如何在只有父元素有唯一 ID 的情况下查找子元素&lt;&#x2F;h3&gt;
&lt;p&gt;例如，如果你有 &lt;code&gt;uniqueIDParent &amp;gt; div1 &amp;gt; div2 &amp;gt; span&lt;&#x2F;code&gt;（其中 span 有你想要的&quot;文本&quot;），但有多个具有不同文本的子元素（spans），如果你给 Playwright 它的父元素、祖父元素或更高级的祖先，它将遍历所有子元素并提取所有文本。&lt;code&gt;expect(uniqueID).toHaveText(&quot;text you want&quot;)&lt;&#x2F;code&gt; 将起作用。同时，&lt;code&gt;page.getByTestId(uniqueIDParent).filter({ hasText: &quot;text you want&quot; })&lt;&#x2F;code&gt; 如果你只针对具有你想要文本的子元素，也能很好地工作。&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators?ref=blog.martioli.com#filtering-locators&quot;&gt;过滤&lt;&#x2F;a&gt;效果神奇。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;2-wei-shen-me-you-shi-zai-playwright-zhong-hui-chu-xian-the-error-browser-has-been-closed-cuo-wu&quot;&gt;2. 为什么有时在 Playwright 中会出现 the error browser has been closed 错误&lt;&#x2F;h3&gt;
&lt;p&gt;原因可能是你可能在某处忘记了&lt;code&gt;await&lt;&#x2F;code&gt;。Playwright 以异步方式工作，意味着它全是 Promise，但你需要你的测试步骤按顺序、从上到下运行并按该顺序执行操作。这是通过使用&lt;code&gt;await&lt;&#x2F;code&gt;关键字来解决 Promise 并保持步骤顺序，以避免竞争条件问题。有时 VS Code 会建议你不需要一些 await，请忽略它，你&lt;strong&gt;确实&lt;&#x2F;strong&gt;需要它们。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;3-zi-dong-deng-dai&quot;&gt;3. 自动等待&lt;&#x2F;h3&gt;
&lt;p&gt;以下是 Selenium 中一些典型的断言，在 playwright 里你不再需要这么做&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;你不必在与元素交互之前断言元素可见。Playwright 将在交互前执行以下操作：&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;检查元素是否可见&lt;&#x2F;li&gt;
&lt;li&gt;检查是否附加到 DOM&lt;&#x2F;li&gt;
&lt;li&gt;检查是否稳定，动画已完成&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;当你打开页面或点击链接重定向到页面时，你不必断言页面已加载才能与元素交互，playwright 会先等待页面完全加载&lt;&#x2F;li&gt;
&lt;li&gt;当你等待元素出现或等待元素消失时，你不必编写显式等待。Playwright 有&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-timeouts?ref=blog.martioli.com&quot;&gt;内置超时&lt;&#x2F;a&gt;（你在 Selenium 中熟悉的显式等待），它将等待一个元素并在设定的时间间隔内尝试找到它（例如：默认情况下，&lt;code&gt;expect(locator).toBeVisible()&lt;&#x2F;code&gt;的等待时间是 5 秒）&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;4-you-shi-hui-chu-xian-chao-shi-bu-qi-zuo-yong-de-fei-chang-te-shu-de-qing-kuang&quot;&gt;4. 有时会出现超时不起作用的非常特殊的情况&lt;&#x2F;h3&gt;
&lt;p&gt;或者你就是无法弄清楚该怎么办，你还有最后一个选择，就是简单地使用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;next&#x2F;api&#x2F;class-frame?ref=blog.martioli.com#frame-wait-for-timeout&quot;&gt;waitForTimeout()&lt;&#x2F;a&gt;等待固定的秒数。通常不建议这样做，但有时你就是别无选择。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;5-ru-he-zai-playwright-zhong-duan-yan-zi-fu-chuan-shu-zu&quot;&gt;5. 如何在 Playwright 中断言字符串数组？&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;expect(locator).toHaveText(array)&lt;&#x2F;code&gt; - 它实际上可以接受一个数组，并且在底层会遍历它以断言项目存在。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;6-ru-he-zai-playwright-zhong-chu-li-duo-ge-yuan-su&quot;&gt;6. 如何在 Playwright 中处理多个元素？&lt;&#x2F;h3&gt;
&lt;p&gt;Playwright 定位器将使用相同的方法找到一个或多个具有相同特征的元素，取决于你提供的内容。如果你想处理多个元素（在 Selenium 中，你会使用&lt;code&gt;findElements&lt;&#x2F;code&gt;，它会返回一个元素数组），在 Playwright 中，如果你执行&lt;code&gt;page.locator(multipleElements)&lt;&#x2F;code&gt;，这将返回多个元素，但&lt;strong&gt;仅仅&lt;&#x2F;strong&gt;是 Playwright 在底层的多个元素，而不是为你返回（至少不是简单的对象数组）。你得到的只是一个单一的定位器（对象）。&lt;strong&gt;为什么会这样？&lt;strong&gt;因为你可能想找到一个单一元素并尝试点击它，所以当你执行操作时，如果属性属于多个元素，它不会让你点击多个元素，并且会抛出错误，建议你如何修复你的测试。但如果你&lt;&#x2F;strong&gt;真的&lt;&#x2F;strong&gt;想处理所有元素，怎么做？你必须在末尾添加&lt;code&gt;.all()&lt;&#x2F;code&gt;。示例：&lt;code&gt;page.locator(multipleElements).all()&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;7-ru-he-chu-li-jiao-da-de-wen-ben-kuai&quot;&gt;7. 如何处理较大的文本块？&lt;&#x2F;h3&gt;
&lt;p&gt;有时你可能有多个具有文本的项目，如果你给 Playwright 所有项目的父元素，那么 Playwright 可以将这些文本提取为数组。你可以通过&lt;code&gt;page.locator(parentOfElementsWithText).allTextContents()&lt;&#x2F;code&gt;或&lt;code&gt;.allInnerTexts()&lt;&#x2F;code&gt;来实现。这种方法的缺点是不建议断言文本的精确匹配，主要是因为有时会获取换行符（&#x2F;n）、逗号或额外空格，但它可以与&lt;code&gt;expect(locator).toContain()&lt;&#x2F;code&gt;一起使用。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;8-ru-he-zai-playwright-zhong-duan-yan-yuan-su-bu-cun-zai&quot;&gt;8. 如何在 Playwright 中断言元素不存在？&lt;&#x2F;h3&gt;
&lt;p&gt;你可以在 stackoverflow 上找到这个小技巧：&lt;code&gt;expect(locator).toHaveCount(0)&lt;&#x2F;code&gt;。这类似于 Selenium 的&lt;code&gt;findElements&lt;&#x2F;code&gt;，如果没有找到元素，它只返回一个空数组。这很好，因为它不会使测试失败。但是，我建议使用 Playwright 内置的&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;next&#x2F;test-assertions?ref=blog.martioli.com#negating-matchers&quot;&gt;NOT 运算符&lt;&#x2F;a&gt;，这才是你实际应该使用的。示例：&lt;code&gt;locator(element).not.toBeVisible()&lt;&#x2F;code&gt;。作为一般实践，所有断言方法都可以与&lt;strong&gt;NOT&lt;&#x2F;strong&gt;一起使用。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;9-ru-he-chu-li-jian-dan-shi-yong-getbytestid-bu-gou-de-qing-kuang&quot;&gt;9. 如何处理简单使用 getByTestId()不够的情况？&lt;&#x2F;h3&gt;
&lt;p&gt;有各种场景，比如表格，你需要组合父元素和子 Web 元素以获取所需数据。因此，在转向&lt;code&gt;locator()&lt;&#x2F;code&gt;并构建超复杂的&lt;code&gt;css选择器&lt;&#x2F;code&gt;或&lt;code&gt;xpath&lt;&#x2F;code&gt;之前，考虑使用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locator?ref=blog.martioli.com#locator-and&quot;&gt;and 运算符&lt;&#x2F;a&gt;，例如&lt;code&gt;page.getByText(elem).and(page.getByText(elem))&lt;&#x2F;code&gt;，或者你可以这样做&lt;code&gt;page.getByTestId(elem).getByTestId(elem)&lt;&#x2F;code&gt;。另一个选择是使用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;next&#x2F;locators?ref=blog.martioli.com#filtering-locators&quot;&gt;过滤定位器&lt;&#x2F;a&gt;。过滤器也可以与&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;next&#x2F;locators?ref=blog.martioli.com#filter-by-not-having-text&quot;&gt;NOT 运算符&lt;&#x2F;a&gt;混合使用。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;10-shi-yong-yi-ge-fu-yuan-su-zhi-xing-duo-ge-cao-zuo&quot;&gt;10. 使用一个父元素执行多个操作&lt;&#x2F;h3&gt;
&lt;p&gt;你可以将元素存储在&lt;code&gt;const element = page.locator()&lt;&#x2F;code&gt;中。注意，这是少数几种不需要在&lt;code&gt;page.locator()&lt;&#x2F;code&gt;前使用&lt;code&gt;await&lt;&#x2F;code&gt;的场景之一。你可以将其视为占位符。稍后在你的代码中，你可以执行&lt;code&gt;element.click()&lt;&#x2F;code&gt;或通过其子元素搜索&lt;code&gt;element.getByTestId(child)&lt;&#x2F;code&gt;。关于这个的&lt;strong&gt;酷&lt;&#x2F;strong&gt;特性是，每次调用元素时，它都会重新查询 DOM。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;11-bu-yao-shi-yong-locator-huo-multiple&quot;&gt;11. 不要使用$(locator)或$$(multiple)&lt;&#x2F;h3&gt;
&lt;p&gt;在 Playwright 中使用$或$$是&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-elementhandle?ref=blog.martioli.com&quot;&gt;元素句柄&lt;&#x2F;a&gt;，这是Playwright强烈不推荐的。主要原因是你可能遇到使用$只引用 DOM 先前版本中的元素的情况。你可能最终会看到众所周知的 Selenium 错误&lt;code&gt;StaleElementReferenceException&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;12-ru-guo-bi-xu-deng-dai-ying-yong-xiang-ying-bing-qie-xiang-ying-shi-jian-chao-guo-tong-chang-de-5-miao-zen-me-ban&quot;&gt;12. 如果必须等待应用响应，并且响应时间超过通常的 5 秒怎么办？&lt;&#x2F;h3&gt;
&lt;p&gt;你可能会遇到这种情况，例如当你执行一个操作、点击、提交，并且获得响应需要更长的时间，可能会出现&quot;旋转加载器&quot;或类似情况。如果你知道会发生这种情况，你可以增加等待响应的超时时间，仅针对单个特定操作，例如&lt;code&gt;expect(locator).toBeVisible({ timeout: 20000 })&lt;&#x2F;code&gt;。通过在方法内传递超时时间，这将只覆盖该行代码的默认 Playwright 配置（希望你不再看到错误&lt;code&gt;error: timed out 5000ms waiting for expect(locator).tobevisible()&lt;&#x2F;code&gt;，并且你的测试将通过）。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;13-wei-shen-me-you-shi-expect-mei-you-tong-chang-de-tohavetext-fang-fa&quot;&gt;13. 为什么有时 expect 没有通常的 toHaveText()方法？&lt;&#x2F;h3&gt;
&lt;p&gt;但如果我尝试&lt;code&gt;toBe()&lt;&#x2F;code&gt;，它就有效了。这取决于你给&lt;code&gt;expect()&lt;&#x2F;code&gt;的对象。如果你给它一个标准定位器，它将具有所有&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;best-practices?ref=blog.martioli.com#use-web-first-assertions&quot;&gt;Web 优先断言&lt;&#x2F;a&gt;，但如果你给它一个修改后的对象，类似于&lt;code&gt;page.locator(elementWithText).innerText()&lt;&#x2F;code&gt;，由于&lt;code&gt;innerText()&lt;&#x2F;code&gt;方法，这不再是标准的 Playwright 对象，而对于其他对象，它使用&lt;a href=&quot;https:&#x2F;&#x2F;jestjs.io&#x2F;docs&#x2F;expect?ref=blog.martioli.com&quot;&gt;jest expect 方法&lt;&#x2F;a&gt;。Playwright 的 expect 对象将检测你给它的对象类型，并将使用 jest expect 的&lt;code&gt;toBe()&lt;&#x2F;code&gt;或 Playwright 方法的&lt;code&gt;toHaveText()&lt;&#x2F;code&gt;。只需记住 Playwright 方法有自动等待重试，而 jest 方法没有。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;14-web-yuan-su-shang-de-duo-ge-shu-ju-ce-shi-id-shu-xing&quot;&gt;14. Web 元素上的多个数据测试 ID 属性&lt;&#x2F;h3&gt;
&lt;p&gt;我们可以在 Playwright 中配置使用自定义测试 ID 属性。使用&lt;code&gt;getByTestId()&lt;&#x2F;code&gt;方法时，Playwright 默认为&lt;code&gt;data-testid=&quot;selector&quot;&lt;&#x2F;code&gt;，因此如果你的 Web 应用程序有不同的默认 ID 定位器，你必须首先&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-testoptions?ref=blog.martioli.com#test-options-test-id-attribute&quot;&gt;设置 testIdAttribute&lt;&#x2F;a&gt;。这包括如果你的网站在&lt;code&gt;id=&quot;selector&quot;&lt;&#x2F;code&gt;格式下有唯一 ID。但是&lt;strong&gt;你能配置使用多个测试 ID 属性吗&lt;&#x2F;strong&gt;？答案是&lt;strong&gt;不&lt;&#x2F;strong&gt;。不过，有一个小技巧。你可以在一般配置中使用不同的&lt;code&gt;testIdAttribute&lt;&#x2F;code&gt;，并在项目中使用不同的属性。或者你可以有具有不同 ID 的不同项目。当你有两个团队或团队组，他们开发了使用一种类型唯一 ID 的应用程序，另一个使用不同的唯一 ID 时，这很有用。这可能是从旧版应用程序迁移的情况。下面是一个示例：&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;  projects: [
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &amp;quot;new-mega-awesome-app&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;      testDir: &amp;quot;.&#x2F;tests&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;      use: {
&lt;&#x2F;span&gt;&lt;span&gt;        ...devices[&amp;quot;Desktop Chrome&amp;quot;],
&lt;&#x2F;span&gt;&lt;span&gt;        testIdAttribute: &amp;quot;id&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        baseURL: &amp;quot;https:&#x2F;&#x2F;newapp.domain.com&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &amp;quot;legacy-app&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;      testDir: &amp;quot;.&#x2F;tests-for-legacy-app&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;      use: {
&lt;&#x2F;span&gt;&lt;span&gt;        ...devices[&amp;quot;Desktop Chrome&amp;quot;],
&lt;&#x2F;span&gt;&lt;span&gt;        testIdAttribute: &amp;quot;data-testid&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        baseURL: &amp;quot;https:&#x2F;&#x2F;legacyapp.domain.com&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果你觉得这很有用，请点赞。或者如果你想更多地激励我，甚至可以&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;adrianmaciuc?ref=blog.martioli.com&quot;&gt;请我喝咖啡&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-tips-and-tricks-1&#x2F;&quot;&gt;https:&#x2F;&#x2F;blog.martioli.com&#x2F;playwright-tips-and-tricks-1&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>The Definitive Guide to API Test Automation With Playwright: Part 16 - Adding CI&#x2F;CD Through GitHub Actions</title>
        <published>2023-11-27T00:00:00+00:00</published>
        <updated>2023-11-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-16-adding-ci-cd-through-github-actions/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-16-adding-ci-cd-through-github-actions/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-16-adding-ci-cd-through-github-actions/">&lt;p&gt;This is the final planned article of this series, where we will discuss Adding CI&#x2F;CD through GitHub Actions. In this guide I&#x27;ll walk through the basics of GitHub Actions along with ways you can scale your tests as the suite grows.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re just joining us now go check out the &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;introduction post&lt;&#x2F;a&gt; which links to all parts we&#x27;ve covered so far. For our examples we will use the playwright-api-test-demo repository linked below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&quot;&gt;GitHub - playwrightsolutions&#x2F;playwright-api-test-demo&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For this guide I&#x27;m using &lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;actions&quot;&gt;GitHub Actions&lt;&#x2F;a&gt;. You can think of GitHub Actions  yml files which I&#x27;ll provide below as your instructions on what code you want to run in the cloud, via &lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;actions&#x2F;using-github-hosted-runners&#x2F;about-github-hosted-runners&#x2F;about-github-hosted-runners&quot;&gt;GitHub hosted runners&lt;&#x2F;a&gt;. I&#x27;ll use GHA in place of GitHub Actions going forward. With each example there are 4 primary areas that are needed for a GHA yml file, which include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;name&lt;&#x2F;strong&gt; - Name of the GHA that shows up in the web portal.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;on&lt;&#x2F;strong&gt; - How and when to run the GHA.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;permissions (optional)&lt;&#x2F;strong&gt; - What permissions should the hosted runners run with.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;jobs&lt;&#x2F;strong&gt; - What software&#x2F;libraries and commands will be run.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;ll be walking through the two different GHA workflows I have built for this project. The first will run the full suite daily and on demand while the other one will run a subset of tests that changed from a pull request. What this article won&#x27;t cover is:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;How to &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;in-playwright-test-is-there-an-easy-way-to-loop-the-test-multiple-times-to-check-for-flakiness&#x2F;&quot;&gt;run your tests multiple times to check for flakiness&lt;&#x2F;a&gt;. this can easily be implemented in the on pull request job by adding &lt;code&gt;repeat-each=5&lt;&#x2F;code&gt; within the npx playwright command.&lt;&#x2F;li&gt;
&lt;li&gt;How to make a &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-do-i-trigger-playwright-tests-to-run-across-repositories-using-github-actions&#x2F;&quot;&gt;GHA from another repository trigger the Playwright Tests to run on this repo.&lt;&#x2F;a&gt; I&#x27;ve written about this in the past, and it&#x27;s worth looking into if that is a problem you are trying to solve. This is very useful if you have your automation code in a separate repository than the web application code your testing against.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Now on with the show!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;full-suite-daily-runs&quot;&gt;Full Suite Daily Runs&lt;&#x2F;h2&gt;
&lt;p&gt;This first action is really the bread and butter of our CI (Continuous Integration) Pipeline. It will run once a day (early in the morning) to give us constant feedback as changes happen throughout the day. I like the early AM run time as I wake up to the results of the entire test suite, and can plan my morning accordingly.&lt;&#x2F;p&gt;
&lt;p&gt;Below I&#x27;ll walk through the different sections of the GHA yml file, below you can see the entire workflow file.&lt;&#x2F;p&gt;
&lt;p&gt;The first sections include the &lt;strong&gt;name&lt;&#x2F;strong&gt;, &lt;strong&gt;on&lt;&#x2F;strong&gt;, and &lt;strong&gt;permissions&lt;&#x2F;strong&gt; blocks.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Daily Playwright API Checks
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;schedule&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;cron&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;0 6 * * *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;workflow_dispatch&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;inputs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;base_url&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;URL, to run tests against&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;required&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;permissions&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;contents&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;write
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pages&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;write
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id-token&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;write
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;strong&gt;on&lt;&#x2F;strong&gt; section includes a ton of options that can be used in initiating the job. The &lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;actions&#x2F;using-workflows&#x2F;events-that-trigger-workflows&quot;&gt;docs&lt;&#x2F;a&gt; show off 35 different ways to trigger a GHA as I&#x27;m writing this, I&#x27;m sure there will be more added as GHA is still a relatively new tool, as it&#x27;s only bee in the wild for 5 years.&lt;&#x2F;p&gt;
&lt;p&gt;In my on section I have a &lt;strong&gt;schedule&lt;&#x2F;strong&gt; section which will run the job via &lt;a href=&quot;https:&#x2F;&#x2F;crontab.guru&#x2F;#0_6_*_*_*&quot;&gt;cron&lt;&#x2F;a&gt;, every morning at 6:00 AM. I also have a &lt;strong&gt;workflow_dispatch&lt;&#x2F;strong&gt; section with some inputs. This allows me to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;actions&#x2F;workflows&#x2F;daily-full.yml&quot;&gt;run my job&lt;&#x2F;a&gt; adhoc and pass in a different base_url if needed.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;11&#x2F;image-5.png&quot; alt=&quot;Image 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The permission section is necessary as I want to publish my artifacts to GitHub Pages so I can view my latest run&#x27;s trace file for better debugging.&lt;&#x2F;p&gt;
&lt;p&gt;The next section focuses on &lt;strong&gt;jobs.&lt;&#x2F;strong&gt; The first entry is the name of the job that will be run, where I also setup environment variables for the entire suite if needed. The specific environment settings were for the GHA pages step. I also have a hard timeout-minutes of 10. This will ensure that if the job is for some reason running longer than 10 minutes to time it out. I am also running this on ubuntu-latest Operating System. The final section is an &lt;code&gt;env&lt;&#x2F;code&gt; variable where I am setting BASE_URL to the input &lt;code&gt;base_url&lt;&#x2F;code&gt; for workflow_dispatch runs, where I specify a different URL to run against.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;playwright-automation-checks&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;github-pages
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ steps.deployment.outputs.page_url }}
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;timeout-minutes&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;env&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;BASE_URL&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ github.event.inputs.base_url }}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The rest of the GHA file is all about the &lt;code&gt;[steps](https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;actions&#x2F;learn-github-actions&#x2F;understanding-github-actions#jobs)&lt;&#x2F;code&gt; that are run as apart of the job.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A job is a set of &lt;em&gt;steps&lt;&#x2F;em&gt; in a workflow that is executed on the same runner. Each step is either a shell script that will be executed, or an &lt;em&gt;action&lt;&#x2F;em&gt; that will be run. Steps are executed in order and are dependent on each other. Since each step is executed on the same runner, you can share data from one step to another. For example, you can have a step that builds your application followed by a step that tests the application that was built.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;What&#x27;s nice about steps is they are run in order, and are dependent on each other, so you can think of the steps as a list of what you want to accomplish.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;checkout-code-and-setup-environment&quot;&gt;Checkout Code and Setup Environment&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Checking Out Code using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;actions&#x2F;checkout&quot;&gt;actions&#x2F;checkout@v3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Installing Node using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;actions&#x2F;setup-node&quot;&gt;actions&#x2F;setup-node@v3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Installing Node Dependencies and Caching OR Restoring Them From Cache using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;actions&#x2F;cache&quot;&gt;actions&#x2F;cache@v3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Installing Playwright and Caching OR Restoring From Cache using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;actions&#x2F;cache&quot;&gt;actions&#x2F;cache@v3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each of the setups in this section use a pre-made action that cane be found on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;marketplace?type=actions&quot;&gt;GitHub Marketplace&lt;&#x2F;a&gt;. Each of these have their own readme on how to use them in their respective repos linked above.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;checkout@v3
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;fetch-depth&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;setup-node@v3
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;node-version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;16
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Cache node_modules
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;cache@v3
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;node-modules-cache
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;        node_modules
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;modules-${{ hashFiles(&amp;#39;package-lock.json&amp;#39;) }}
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm ci --ignore-scripts
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;steps.node-modules-cache.outputs.cache-hit != &amp;#39;true&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Get installed Playwright version
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-version
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;echo &amp;quot;PLAYWRIGHT_VERSION=$(node -e &amp;quot;console.log(require(&amp;#39;.&#x2F;package-lock.json&amp;#39;).dependencies[&amp;#39;@playwright&#x2F;test&amp;#39;].version)&amp;quot;)&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Cache playwright binaries
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;cache@v3
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-cache
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;        ~&#x2F;.cache&#x2F;ms-playwright
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright install --with-deps
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;steps.playwright-cache.outputs.cache-hit != &amp;#39;true&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright install-deps
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;steps.playwright-cache.outputs.cache-hit != &amp;#39;true&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;One quick note if you use Webkit&#x2F;Safari for testing, you may run into issues, I would recommend not caching playwright if this is the case.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;running-tests&quot;&gt;Running Tests&lt;&#x2F;h3&gt;
&lt;p&gt;For the next set of steps I am only including a &lt;code&gt;name&lt;&#x2F;code&gt; and &lt;code&gt;run&lt;&#x2F;code&gt; with the associated command that I want to run. The GHA runner has node installed and has playwright and the project dependencies installed already so we are good to just run the same commands you would from your local machine.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Run linter&lt;&#x2F;li&gt;
&lt;li&gt;Run prettier&lt;&#x2F;li&gt;
&lt;li&gt;Run unit tests&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;When setting the BASE_URL environment, I am actually first checking if the &lt;code&gt;env.BASE_URL == null&lt;&#x2F;code&gt; then set it with a value. If there is any other value, this set doesn&#x27;t get executed.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Set BASE_URL if null&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This section I am setting up &lt;code&gt;env&lt;&#x2F;code&gt; variables that re needed for this step, along with a multi-line command. The first is just echoing the github event name (more for debugging purposes, it&#x27;s not needed for the job to suceed) and the 2nd is the &lt;code&gt;npm run test&lt;&#x2F;code&gt; command which will run our tests with the following command (this is found in the package.json file in the &lt;code&gt;scripts&lt;&#x2F;code&gt; section.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;11&#x2F;image-6.png&quot; alt=&quot;Image 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Setup Environment Variables for &lt;a href=&quot;https:&#x2F;&#x2F;currents.dev&#x2F;playwright?utm_source=pw_solutions_cicd&quot;&gt;Currents.Dev&lt;&#x2F;a&gt; (I&#x27;m cooking up an article for this service!)&lt;&#x2F;li&gt;
&lt;li&gt;Run Playwright tests&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run lint
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run lint
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run prettier
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run prettier
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run UnitTests
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run ut
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Set BASE_URL if not passed in
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;env.BASE_URL == null
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    echo &amp;quot;BASE_URL=https:&#x2F;&#x2F;automationintesting.online&#x2F;&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run Playwright tests
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;env&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;CURRENTS_PROJECT_ID&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ secrets.CURRENTS_PROJECT_ID }}
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;CURRENTS_RECORD_KEY&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ secrets.CURRENTS_RECORD_KEY }}
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;CURRENTS_CI_BUILD_ID&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;reporter-${{ github.repository }}-${{ github.run_id }}-${{ github.run_attempt }}
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;URL&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ env.BASE_URL}}
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    echo &amp;quot;The github event is: ${{ github.event_name }}&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    npm run test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;publishing-results&quot;&gt;Publishing Results&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Setup GH Pages using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;actions&#x2F;configure-pages&quot;&gt;actions&#x2F;configure-pages@v3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Upload GHA Artifact &lt;code&gt;playwright-report&lt;&#x2F;code&gt; using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;actions&#x2F;upload-artifact&quot;&gt;actions&#x2F;upload-artifact@v3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Upload Pages Artifact using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;actions&#x2F;upload-pages-artifact&quot;&gt;actions&#x2F;upload-pages-artifact@v1&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Deploy to Pages using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;actions&#x2F;deploy-pages&quot;&gt;action&#x2F;deploy-pages@v2&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each of these sections are using the &lt;code&gt;use&lt;&#x2F;code&gt; functionality that leverage pre-built steps. 3 of the 4 steps above are all about configuring, uploading, and deploying the Playwright Report to GitHub Pages, while the 2nd setp Upload GHA Artifact will zip and upload the &lt;code&gt;playwright-report&lt;&#x2F;code&gt; and attach it to the GHA run. An example of this can be found on this &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;actions&#x2F;runs&#x2F;6923995256&quot;&gt;test run&lt;&#x2F;a&gt;. The bottom section has Artifacts that you are able to download assuming they haven&#x27;t expired. This will include the html test report along with trace files and any assets (videos, images) that were captured as a part of the test run.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;11&#x2F;image-7.png&quot; alt=&quot;Image 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# The following steps are for deploying the report to GitHub Pages
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Setup Pages
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;configure-pages@v3
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;upload-artifact@v3
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;report-artifact
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;retention-days&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Upload Pages Artifact
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;upload-pages-artifact@v1
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Deploy to GitHub Pages
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;deployment
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;deploy-pages@v2
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;the-full-gha-workflow-file&quot;&gt;The Full GHA workflow file&lt;&#x2F;h3&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&#x2F;.github&#x2F;workflows&#x2F;daily-full.yml
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Daily Playwright API Checks
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;schedule&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;cron&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;0 6 * * *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;workflow_dispatch&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;inputs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;base_url&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;URL, to run tests against&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;required&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;permissions&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;contents&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;write
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pages&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;write
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id-token&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;write
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;playwright-automation-checks&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;github-pages
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ steps.deployment.outputs.page_url }}
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;timeout-minutes&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;env&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;BASE_URL&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ github.event.inputs.base_url }}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;checkout@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;fetch-depth&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;setup-node@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;node-version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;16
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Cache node_modules
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;cache@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;node-modules-cache
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            node_modules
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;modules-${{ hashFiles(&amp;#39;package-lock.json&amp;#39;) }}
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm ci --ignore-scripts
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;steps.node-modules-cache.outputs.cache-hit != &amp;#39;true&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Get installed Playwright version
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-version
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;echo &amp;quot;PLAYWRIGHT_VERSION=$(node -e &amp;quot;console.log(require(&amp;#39;.&#x2F;package-lock.json&amp;#39;).dependencies[&amp;#39;@playwright&#x2F;test&amp;#39;].version)&amp;quot;)&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Cache playwright binaries
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;cache@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-cache
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            ~&#x2F;.cache&#x2F;ms-playwright
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright install --with-deps
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;steps.playwright-cache.outputs.cache-hit != &amp;#39;true&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright install-deps
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;steps.playwright-cache.outputs.cache-hit != &amp;#39;true&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run lint
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run lint
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run prettier
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run prettier
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run UnitTests
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run ut
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Set BASE_URL if not passed in
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;env.BASE_URL == null
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;BASE_URL=https:&#x2F;&#x2F;automationintesting.online&#x2F;&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run Playwright tests
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;env&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;CURRENTS_PROJECT_ID&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ secrets.CURRENTS_PROJECT_ID }}
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;CURRENTS_RECORD_KEY&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ secrets.CURRENTS_RECORD_KEY }}
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;CURRENTS_CI_BUILD_ID&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;reporter-${{ github.repository }}-${{ github.run_id }}-${{ github.run_attempt }}
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;URL&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ env.BASE_URL}}
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;The github event is: ${{ github.event_name }}&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          npm run test
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# The following steps are for deploying the report to GitHub Pages
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Setup Pages
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;configure-pages@v3
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;upload-artifact@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;report-artifact
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;retention-days&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Upload Pages Artifact
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;upload-pages-artifact@v1
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Deploy to GitHub Pages
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;deployment
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;deploy-pages@v2
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;changed-files-only-on-pull-requests-runs&quot;&gt;Changed Files Only on Pull Requests Runs&lt;&#x2F;h2&gt;
&lt;p&gt;Below is my GHA workflow that I run on pull requests that occure against the main branch. I&#x27;ll walk through the differences below.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&#x2F; .github&#x2F;workflows&#x2F;on-pr.files-changed.yml
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Changed Files Playwright API Checks
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pull_request&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;main
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;permissions&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;contents&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;write
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pages&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;write
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id-token&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;write
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;playwright-automation-checks&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;github-pages
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ steps.deployment.outputs.page_url }}
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;timeout-minutes&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;env&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;BASE_URL&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ github.event.inputs.base_url }}
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;CHANGED&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;checkout@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;fetch-depth&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;setup-node@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;node-version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;16
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Cache node_modules
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;cache@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;node-modules-cache
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            node_modules
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;modules-${{ hashFiles(&amp;#39;package-lock.json&amp;#39;) }}
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm ci --ignore-scripts
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;steps.node-modules-cache.outputs.cache-hit != &amp;#39;true&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Get installed Playwright version
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-version
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;echo &amp;quot;PLAYWRIGHT_VERSION=$(node -e &amp;quot;console.log(require(&amp;#39;.&#x2F;package-lock.json&amp;#39;).dependencies[&amp;#39;@playwright&#x2F;test&amp;#39;].version)&amp;quot;)&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Cache playwright binaries
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;cache@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-cache
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            ~&#x2F;.cache&#x2F;ms-playwright
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright install --with-deps
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;steps.playwright-cache.outputs.cache-hit != &amp;#39;true&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright install-deps
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;steps.playwright-cache.outputs.cache-hit != &amp;#39;true&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run lint
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run lint
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run prettier
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run prettier
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run UnitTests
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run ut
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Set BASE_URL if not passed in
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;env.BASE_URL == null
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;BASE_URL=https:&#x2F;&#x2F;automationintesting.online&#x2F;&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Create Test List if pull_request
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;github.event.pull_request
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;Creating a list of tests that have changed&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} | grep &amp;quot;.spec.ts&amp;quot; | tr &amp;#39;\n&amp;#39; &amp;#39; &amp;#39;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;CHANGED=$FILES&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run Playwright tests
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;CHANGED = ${{ env.CHANGED }}&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;The github event is: ${{ github.event_name }}&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          URL=${{ env.BASE_URL}} npx playwright test --reporter=github --workers=1 ${{ env.CHANGED }}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;upload-artifact@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;report-artifact
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-report&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;retention-days&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The biggest difference is we won&#x27;t plan on running all the playwright tests whenever there is a pull request, but only attempt to run files that were changed or added as a part of the pull request. Below are the big differences.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Create a List of Tests That Have Been Added or Changed&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This section utilizes a bash script in the &lt;code&gt;run&lt;&#x2F;code&gt; section to set variable FILES= to the difference between git shas where the filename ends in &lt;code&gt;.spec.ts&lt;&#x2F;code&gt; using &lt;code&gt;grep&lt;&#x2F;code&gt; and then replacing &lt;code&gt;\n&lt;&#x2F;code&gt; which is a new line (enter) with just a &lt;code&gt;&#x27; &#x27;&lt;&#x2F;code&gt; space.  I then export the FILES variable to &lt;code&gt;CHANGED&lt;&#x2F;code&gt; env variable.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Run the Playwright Tests That Were Changed&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Now that we have a list of files that have changed we can then pass those in at the end of the &lt;code&gt;npx playwright test&lt;&#x2F;code&gt; script.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Create Test List if pull_request
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;github.event.pull_request
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    echo &amp;quot;Creating a list of tests that have changed&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} | grep &amp;quot;.spec.ts&amp;quot; | tr &amp;#39;\n&amp;#39; &amp;#39; &amp;#39;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    echo &amp;quot;CHANGED=$FILES&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run Playwright Tests
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    echo &amp;quot;CHANGED = ${{ env.CHANGED }}&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    echo &amp;quot;The github event is: ${{ github.event_name }}&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    URL=${{ env.BASE_URL}} npx playwright test --reporter=github --workers=1 ${{ env.CHANGED }}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is an example of what the output for the &lt;code&gt;Run Playwright Tests&lt;&#x2F;code&gt; section looks like within GHA (the blue are the commands that were executed).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;11&#x2F;image-8.png&quot; alt=&quot;Image 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can see that the different test file names were saved properly into the env variable. The pro to this is you get faster feedback when you are updating your tests. The con is if you make a change to a non-spec file the &lt;code&gt;env.CHANGED&lt;&#x2F;code&gt; variable will be empty and your whole test suite will run (which may be ok in the end).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;&#x2F;h2&gt;
&lt;p&gt;GitHub Actions is a great tool that is already integrated with GitHub in order to run your Playwright Tests, and the cost of GHA minutes is relatively inexpensive for most folks needs. This is one of the first things I spend effort on when building out my automated checks to ensure I get constant feedback either after pull requests or at least daily.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks for reading! If you found this helpful, reach out and let me know on &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; or consider &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;buying me a cup of coffee&lt;&#x2F;a&gt;. If you want more content delivered to you in your inbox subscribe below, and be sure to leave a ❤️ to show some love.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-16-adding-ci-cd-through-github-actions&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-11-27T13:30:36.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>三大自动化测试工具 playwright&#x2F;selenium&#x2F;cypress 的终极对比</title>
        <published>2023-11-21T00:00:00+00:00</published>
        <updated>2023-11-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/deciding-among-the-titans-of-test-automation-a-playwright-vs-selenium-vs-cypress-showdown-e5f26ef9e08f/"/>
        <id>https://playwright.itest.info/blog/deciding-among-the-titans-of-test-automation-a-playwright-vs-selenium-vs-cypress-showdown-e5f26ef9e08f/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/deciding-among-the-titans-of-test-automation-a-playwright-vs-selenium-vs-cypress-showdown-e5f26ef9e08f/">&lt;p&gt;作者：Franco Kleinerman&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;deciding-among-the-titans-of-test-automation-a-playwright-vs-selenium-vs-cypress-showdown-e5f26ef9e08f&#x2F;.&#x2F;cy_vs_se.png&quot; alt=&quot;图片3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在不断发展的软件开发领域，自动化测试已成为确保应用程序质量和可靠性的关键组成部分。但是，面对众多可用的自动化测试工具，为您的项目选择合适的工具可能是一项艰巨的任务。&lt;&#x2F;p&gt;
&lt;p&gt;本文将介绍 Playwright、Selenium 和 Cypress 这三个强大且广泛使用的自动化测试工具。在这份全面指南中，我们将探讨这三个工具，每种工具都有其独特的功能、能力和使用场景。无论您是经验丰富的自动化工程师还是该领域的新手，本文旨在揭开选择过程的神秘面纱，帮助您做出明智的选择。&lt;&#x2F;p&gt;
&lt;p&gt;我们将深入探讨行业最新的选手 Playwright 的优势和劣势，它承诺为 Web 测试带来全新的自动化能力。我们还将探讨备受信赖的 Selenium，这是一个用于在多个浏览器和平台上测试 Web 应用程序的可靠工具。最后但同样重要的是，我们将研究 Cypress，它以简单、快速和无缝集成而闻名，是许多开发人员和 QA 工程师的首选。&lt;&#x2F;p&gt;
&lt;p&gt;我们将首先回顾每个工具的关键点和独特功能，为您提供一个坚实的基础，以了解它们的不同之处。之后，我们将深入探讨每个工具的细节和实际使用体验。但真正有趣的部分是当我们把它们放在一起比较的时候。我们将生成一个详细的表格，分解 Playwright、Selenium 和 Cypress 的主要功能、优势和劣势，让您能够评估它们是否适合您的特定需求。为了使决策过程更加简单明了，我们将根据自动化测试中最重要的因素为每个工具打分。在这个旅程结束时，您将掌握必要的知识和见解，为您的自动化测试工作做出明智的选择。&lt;&#x2F;p&gt;
&lt;p&gt;让我们从市场上存在时间最长的工具开始&lt;&#x2F;p&gt;
&lt;h2 id=&quot;selenium&quot;&gt;Selenium！&lt;&#x2F;h2&gt;
&lt;p&gt;我们将从最古老的工具 Selenium 开始。作为自动化测试世界中的一个家喻户晓的名字，Selenium 长期以来一直是 Web 测试的首选。它因其强大的功能、多样性以及庞大的用户和贡献者社区而广受认可。但是，像任何工具一样，Selenium 也有自己的优缺点，我们将在本节中探讨。无论您是在考虑使用 Selenium 进行自动化测试，还是只是想了解更多关于这个经验丰富的巨头的信息，请继续阅读以便了解它的闪光点和面临的挑战。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Selenium 的优点：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;广泛的浏览器支持：Selenium 支持多种浏览器，包括 Chrome、Firefox、Safari、Edge 等，使其成为跨浏览器测试的多功能选择。&lt;&#x2F;li&gt;
&lt;li&gt;语言支持：Selenium 为多种编程语言提供了支持，如 Java、Python、C#和 JavaScript，使其对广大开发人员具有可访问性。&lt;&#x2F;li&gt;
&lt;li&gt;庞大的社区：Selenium 拥有一个庞大而活跃的社区，因此有丰富的文档、资源和大量用户生成的内容，可帮助解决问题和学习。&lt;&#x2F;li&gt;
&lt;li&gt;跨平台兼容性：Selenium 与多个操作系统兼容，包括 Windows、macOS 和 Linux。&lt;&#x2F;li&gt;
&lt;li&gt;强大的生态系统：Selenium 提供各种工具和框架，如用于并行测试的 Selenium Grid、用于自动化的 Selenium WebDriver 和用于录制和回放功能的 Selenium IDE。&lt;&#x2F;li&gt;
&lt;li&gt;成熟稳定：作为最古老的自动化工具之一，Selenium 已经证明了其稳定性和可靠性，赢得了许多企业的信任。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Selenium 的缺点：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;复杂的设置：对于初学者来说，首次设置 Selenium 可能比较难，特别是在为不同浏览器配置驱动程序(driver)时。&lt;&#x2F;li&gt;
&lt;li&gt;不稳定性：Selenium 测试可能容易出现不稳定性，由于页面加载时间或动态元素等因素，测试可能会间歇性失败。&lt;&#x2F;li&gt;
&lt;li&gt;执行速度慢：与其他现代工具相比，Selenium 测试有时运行速度较慢，这可能会影响测试套件的效率。&lt;&#x2F;li&gt;
&lt;li&gt;没有内置的测试框架：Selenium 不包含内置的测试 runner 或断言库，这意味着您需要依赖其他框架或工具来处理这些方面。&lt;&#x2F;li&gt;
&lt;li&gt;有限的移动支持：虽然 Selenium 可以自动化移动 Web 浏览器，但与专门的移动测试工具相比，其移动应用测试能力较弱。&lt;&#x2F;li&gt;
&lt;li&gt;学习曲线陡峭：Selenium 的灵活性是以更陡峭的学习曲线为代价的，特别是对于那些刚接触自动化测试的人来说。&lt;&#x2F;li&gt;
&lt;li&gt;没有原生支持 API 测试，我们可能需要编写自定义框架来与做 API 接口测试。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Selenium 作为强大可靠的自动化测试工具的声誉是实至名归的。其广泛的浏览器支持、多样化的语言绑定和成熟的生态系统使其成为许多人的理想选择。然而，通往成功的 Selenium 自动化之路并非一帆风顺。初始化项目的挑战、偶尔的不稳定性和学习曲线是需要考虑的方面。在我们继续探索 Playwright、Selenium 和 Cypress 的过程中，我们鼓励您仔细权衡这些优缺点，为您的自动化测试项目做出明智的选择。现在，让我们继续我们的旅程，深入探讨下一个竞争者。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwright&quot;&gt;Playwright！&lt;&#x2F;h2&gt;
&lt;p&gt;在我们探索自动化测试巨头的旅程中，接下来是这个三人组中最年轻的竞争者 Playwright。Playwright 凭借其独特的能力和测试自动化方法在测试社区中引起了不小的轰动。它承诺简化 Web 测试的复杂性并增强自动化能力。但是，像任何工具一样，Playwright 也有其自身的优势和挑战。在本节中，我们将深入探讨 Playwright 的与众不同之处以及它可能遇到的障碍。无论您是在考虑将 Playwright 用于您的项目，还是只是对测试自动化的最新进展感到好奇，请继续阅读，以便了解这个工具的特别之处。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Playwright 的优点：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;跨浏览器支持：Playwright 支持多个 Web 浏览器，包括 Chromium、Firefox 和 WebKit，实现全面的跨浏览器测试。&lt;&#x2F;li&gt;
&lt;li&gt;统一 API：Playwright 为自动化 Web、移动和桌面应用程序提供统一的 API，使其成为各种测试场景的多功能选择。&lt;&#x2F;li&gt;
&lt;li&gt;自动化复杂场景：它擅长自动化复杂场景，如处理 iframes、shadow DOM 和难以识别的元素，使其适合现代 Web 应用程序。&lt;&#x2F;li&gt;
&lt;li&gt;并行测试执行 — Playwright 原生支持这一功能，而在 Selenium 中也可以实现，但需要使用自定义框架，如 pytest。&lt;&#x2F;li&gt;
&lt;li&gt;语言支持：Playwright 也为各种编程语言提供支持，如 Java、Python、C#和 JavaScript，使其成为许多开发人员的绝佳选择。&lt;&#x2F;li&gt;
&lt;li&gt;原生 API 接口测试能力。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Playwright 的缺点：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;学习曲线：Playwright 用的技术比较新， 可能会带来陡峭学习曲线，特别是对于习惯于传统自动化工具的人来说。&lt;&#x2F;li&gt;
&lt;li&gt;较小的社区：与 Selenium 相比，Playwright 的社区较小，这可能意味着可用的资源和社区生成的内容较少。&lt;&#x2F;li&gt;
&lt;li&gt;较新的工具：作为一个较新的工具，一些企业可能更不愿意将 Playwright 用于关键项目。&lt;&#x2F;li&gt;
&lt;li&gt;兼容性挑战：一些用户报告了与特定网站和应用程序的兼容性问题。由于 Playwright 的快速发展，更新频繁，这有时可能导致与现有代码库的兼容性挑战。&lt;&#x2F;li&gt;
&lt;li&gt;过渡期：如果您的团队习惯于 Selenium 或其他传统自动化工具，过渡到 Playwright 可能需要重新培训和适应期，这对一些组织来说可能被视为一个障碍。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Playwright 在测试自动化领域的出现无疑引起了人们的注目，这要归功于其现代化的测试方法和强大功能。其跨浏览器支持、复杂场景自动化能力是强有力的卖点。然而，像任何新兴技术一样，它也面临着一些挑战，比如与 Selenium 相比社区较小。当我们转向最后一个竞争者 Cypress 时，我们鼓励您仔细考虑 Playwright 的优缺点。选择正确的工具取决于您项目的独特需求和团队的专业知识。那么，让我们继续我们的旅程，探索最后一个自动化测试巨头，Cypress。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;cypress&quot;&gt;Cypress！&lt;&#x2F;h2&gt;
&lt;p&gt;在自动化测试巨头之战中，我们的最后一个竞争者是 Cypress，这是一个以简单、快速和对开发人员友好的测试方法而闻名的工具。当我们结束对 Playwright、Selenium 和现在的 Cypress 的探索时，我们深入研究了 Cypress 的突出特点和可能面临的挑战。如果您正在寻找一个重视易用界面和快速执行的测试工具，或者只是想了解 Cypress 如何与其竞争对手相比的优劣，本节是我们旅程的最后一站。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Cypress 的优点：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;用户友好：Cypress 以其对开发人员友好的界面而闻名，允许测试人员和开发人员轻松编写和运行测试。&lt;&#x2F;li&gt;
&lt;li&gt;实时重载：Cypress 提供实时重载功能，使测试开发和调试更加高效。&lt;&#x2F;li&gt;
&lt;li&gt;自动等待：Cypress 自动等待元素可用，减少了测试脚本中显式等待的需求。&lt;&#x2F;li&gt;
&lt;li&gt;时间旅行调试：Cypress 提供独特的时间旅行调试功能，允许您逐步检查和调试测试执行。&lt;&#x2F;li&gt;
&lt;li&gt;快速执行：Cypress 设计注重速度，与其他一些自动化工具相比，测试执行时间更快。&lt;&#x2F;li&gt;
&lt;li&gt;内置测试运 runner：它包含内置的测试 runner 和断言库，简化了测试创建和管理。&lt;&#x2F;li&gt;
&lt;li&gt;优秀的文档：Cypress 拥有结构良好的文档和乐于助人的社区来进行用户支持。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Cypress 的缺点：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;有限的跨浏览器支持：Cypress 主要支持 Chrome，对其他浏览器的支持有限。如果跨浏览器测试是关键要求，这可能是一个缺点。&lt;&#x2F;li&gt;
&lt;li&gt;不支持移动应用测试：Cypress 专注于 Web 应用程序测试，缺乏测试移动应用的强大功能。&lt;&#x2F;li&gt;
&lt;li&gt;复杂场景的学习曲线：虽然 Cypress 对基本用例来说很友好，但更复杂的测试场景可能需要更深入地理解其架构。&lt;&#x2F;li&gt;
&lt;li&gt;仅支持 JavaScript：Cypress 主要基于 JavaScript，这对于偏好其他编程语言的团队来说可能不是理想选择。&lt;&#x2F;li&gt;
&lt;li&gt;有限的生态系统：虽然 Cypress 生态系统正在增长，但可能无法提供像 Selenium 这样更成熟工具的相同规模的插件、扩展和集成。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Cypress 通过为开发人员提供直观愉快的测试体验，在自动化测试世界中赢得了声誉。它的实时重载、自动等待和内置时间旅行调试对许多人来说是游戏规则的改变者。然而，没有工具是完美无缺的。Cypress 也面临着诸如有限的跨浏览器支持和缺乏移动应用测试能力等挑战。当我们结束对这三个自动化测试巨头的讨论时，我们鼓励您将 Cypress 的优势和劣势与 Playwright 和 Selenium 一起考虑。您最终做出的选择应该反映你负责项目的独特需求和团队的专业知识。现在，让我们结束我们的旅程，给出一个大致的结论。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhi-jie-bi-jiao-zhe-xie-gong-ju&quot;&gt;直接比较这些工具&lt;&#x2F;h2&gt;
&lt;p&gt;我们将添加星星（从 ⭐ 到 ⭐⭐⭐⭐⭐）来评定每个自动化工具在各个方面的表现。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwright.itest.info&#x2F;blog&#x2F;deciding-among-the-titans-of-test-automation-a-playwright-vs-selenium-vs-cypress-showdown-e5f26ef9e08f&#x2F;.&#x2F;playwright_vs_se.png&quot; alt=&quot;图片5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这张表格对比了 Playwright、Selenium 和 Cypress 在不同方面的表现，包括：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;跨浏览器支持&lt;&#x2F;li&gt;
&lt;li&gt;跨平台支持&lt;&#x2F;li&gt;
&lt;li&gt;语言支持&lt;&#x2F;li&gt;
&lt;li&gt;移动测试&lt;&#x2F;li&gt;
&lt;li&gt;平行测试执行&lt;&#x2F;li&gt;
&lt;li&gt;学习曲线&lt;&#x2F;li&gt;
&lt;li&gt;社区支持&lt;&#x2F;li&gt;
&lt;li&gt;执行速度&lt;&#x2F;li&gt;
&lt;li&gt;API 测试&lt;&#x2F;li&gt;
&lt;li&gt;稳定性&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源：https:&#x2F;&#x2F;medium.com&#x2F;leniolabs&#x2F;deciding-among-the-titans-of-test-automation-a-playwright-vs-selenium-vs-cypress-showdown-e5f26ef9e08f&lt;&#x2F;p&gt;
&lt;p&gt;发布时间：2023-11-21T17:15:23.190Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在 Playwright Test 中为 page.goto() 添加查询参数?</title>
        <published>2023-11-20T00:00:00+00:00</published>
        <updated>2023-11-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-do-you-append-query-parameters-to-page-goto-using-playwright-test/"/>
        <id>https://playwright.itest.info/blog/how-do-you-append-query-parameters-to-page-goto-using-playwright-test/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-do-you-append-query-parameters-to-page-goto-using-playwright-test/">&lt;p&gt;虽然我们不常需要在特定请求中添加和验证查询参数,但本文的方法在测试使用 URL 参数的 &lt;a href=&quot;https:&#x2F;&#x2F;blog.hubspot.com&#x2F;marketing&#x2F;what-are-utm-tracking-codes-ht&quot;&gt;UTM&lt;&#x2F;a&gt; 或其他类型的跟踪链接时会非常有用。&lt;&#x2F;p&gt;
&lt;p&gt;在本文中,我将测试 &lt;a href=&quot;https:&#x2F;&#x2F;practicesoftwaretesting.com&#x2F;&quot;&gt;https:&#x2F;&#x2F;practicesoftwaretesting.com&lt;&#x2F;a&gt; 这个站点。非常感谢 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;roydekleijn&#x2F;&quot;&gt;Roy de Kleijn&lt;&#x2F;a&gt; 提供这个优秀的测试资源!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-practicesoftwaretesting.com&quot;&gt;GitHub - playwrightsolutions&#x2F;playwright-practicesoftwaretesting.com&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我们先来编写一个简单的测试用例,访问页面并填写提交表单。最终目标可能是通过 UTM 代码跟踪某些操作,比如结账过程或支付行为。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;contact&#x2F;contact.nofixtured.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;首页测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nav-contact&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;first-name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;last-name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Mctester&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;asf@asdf.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;subject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;selectOption&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;payments&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;repeat&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;40&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;contact-submit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.alert-success&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveText&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;感谢您的留言!我们将尽快与您联系。&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;注意:上面的测试没有包含任何 URL 参数,也没有检查它们。有趣的是,当我访问&lt;a href=&quot;https:&#x2F;&#x2F;practicesoftwaretesting.com&#x2F;#&#x2F;&quot;&gt;https:&#x2F;&#x2F;practicesoftwaretesting.com&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;时,会被重定向到&lt;a href=&quot;https:&#x2F;&#x2F;practicesoftwaretesting.com&#x2F;#&#x2F;&quot;&gt;https:&#x2F;&#x2F;practicesoftwaretesting.com&#x2F;#&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在添加 URL 参数时需要考虑这一点,因为参数会被添加到我们访问的网站上,但如果发生重定向,URL 参数就不会被保留。&lt;&#x2F;p&gt;
&lt;p&gt;如果你只有一个测试用例,可以直接将 URL 参数传入 &lt;code&gt;page.goto(&quot;&#x2F;#&#x2F;?UTM_SOURCE=playwright&quot;)&lt;&#x2F;code&gt;。但在我们的示例中,我们想让多个测试用例里的 url 都带上这个参数,比较好的方式是将实现一个扩展基本页面的&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-fixtures#creating-a-fixture&quot;&gt;固定装置(fixture)&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;乙醇的注释 👀: 学过前端的同学一看就懂了，这其实就跟拦截器很像&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;fixtures&#x2F;modifiedGoto.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;bind&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;modifiedGoto&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;?UTM_SOURCE=playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;modifiedGoto&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;goto &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= goto&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我不会详细解释&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-fixtures#creating-a-fixture&quot;&gt;如何创建 fixture&lt;&#x2F;a&gt;,但会讲解 fixture 的逻辑。&lt;&#x2F;p&gt;
&lt;p&gt;首先,我们创建一个新变量 &lt;code&gt;goto&lt;&#x2F;code&gt;,它被设置为 &lt;code&gt;page.goto.bind(page)&lt;&#x2F;code&gt;。这里使用了 &lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Reference&#x2F;Global_objects&#x2F;Function&#x2F;bind&quot;&gt;bind() 函数&lt;&#x2F;a&gt;,用于创建一个新函数,其 &lt;code&gt;this&lt;&#x2F;code&gt; 值设置为 &lt;code&gt;page&lt;&#x2F;code&gt; 对象。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;乙醇的注释 👀: bind()是经典的 javascript 中比较容易迷糊的问题，大家可以让 ai 多举几个例子来理解&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;接下来的代码添加了一个名为 &lt;code&gt;modifiedGoto()&lt;&#x2F;code&gt; 的新函数,它将替代 &lt;code&gt;goto()&lt;&#x2F;code&gt;。在这个函数中,我们将 &lt;code&gt;?UTM_SOURCE=playwright&lt;&#x2F;code&gt; 加到提供的 URL 字符串上并返回。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;modifiedGoto&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;?UTM_SOURCE=playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;下一行 &lt;code&gt;page.goto = modifiedGoto;&lt;&#x2F;code&gt; 用 &lt;code&gt;modifiedGoto&lt;&#x2F;code&gt; 方法替换了原始的 &lt;code&gt;page.goto&lt;&#x2F;code&gt; 方法。&lt;&#x2F;p&gt;
&lt;p&gt;然后我们调用 &lt;code&gt;await use(page);&lt;&#x2F;code&gt;,这是一个回调函数,用于&quot;在运行的测试中使用固定装置值&quot;。&lt;&#x2F;p&gt;
&lt;p&gt;最后,我们调用 &lt;code&gt;page.goto = goto;&lt;&#x2F;code&gt;,在 &lt;code&gt;use&lt;&#x2F;code&gt; 函数调用后恢复原始的 &lt;code&gt;page.goto&lt;&#x2F;code&gt; 方法。&lt;&#x2F;p&gt;
&lt;p&gt;总的来说,我们现在有了一个功能性的 fixture,可以导入到任何测试用例中,并自动添加我们在 fixture 中指定的 URL 参数。让我们通过创建一个新的 &lt;code&gt;contact.spec.ts&lt;&#x2F;code&gt; 来测试它,该用例将使用 &lt;code&gt;@fixtures&#x2F;modifiedGoto&lt;&#x2F;code&gt;。注意,我们在访问页面后添加了一个断言,以验证 UTM_SOURCE 是否存在于 URL 中!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;contact&#x2F;contact.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@fixtures&#x2F;modifiedGoto&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;首页测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;#&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;?UTM_SOURCE=playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nav-contact&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;first-name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;last-name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Mctester&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;asf@asdf.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;subject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;selectOption&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;payments&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;repeat&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;40&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;contact-submit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.alert-success&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveText&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;感谢您的留言!我们将尽快与您联系。&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这篇文章的灵感来自 &lt;a href=&quot;https:&#x2F;&#x2F;aka.ms&#x2F;playwright&#x2F;discord&quot;&gt;Playwright Discord&lt;&#x2F;a&gt; 上 &lt;a href=&quot;https:&#x2F;&#x2F;discord.com&#x2F;channels&#x2F;807756831384403968&#x2F;1160874346483564644&quot;&gt;Tomaj&lt;&#x2F;a&gt; 的帖子。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;11&#x2F;image.png&quot; alt=&quot;Image 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;他提供了他的解决方案,让我了解到 &lt;code&gt;bind&lt;&#x2F;code&gt; 方法的&quot;全新世界&quot;!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读!如果您觉得这篇文章有帮助,请在 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; 上联系我,或考虑&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;为我买杯咖啡&lt;&#x2F;a&gt;。如果您想在收件箱中收到更多内容,请在下方订阅,别忘了留下一个 ❤️ 来表示您的喜爱。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-do-you-append-query-parameters-to-page-goto-using-playwright-test&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-11-20&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright中如何使用fixture来监控前端的javascript错误</title>
        <published>2023-11-20T00:00:00+00:00</published>
        <updated>2023-11-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/track-frontend-javascript-exceptions-with-playwright/"/>
        <id>https://playwright.itest.info/blog/track-frontend-javascript-exceptions-with-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/track-frontend-javascript-exceptions-with-playwright/">&lt;p&gt;在今天充满 JavaScript 的世界中，ui 自动化测试和实时监控是非常具有挑战性的。现代应用程序中有大量的异步 JavaScript 代码运行，怎么样让测试稳定运行不挂掉一个真正的难题。&lt;&#x2F;p&gt;
&lt;p&gt;这就是为什么许多团队依赖于测试关键功能，并将“测试其余部分”视为一种可有可无的做法。这是一种典型的成本-效益决策。这种方法的缺点是，如果我们不对所有功能进行 ui 自动化测试，很容易忽略生产环境的问题和回归。&lt;&#x2F;p&gt;
&lt;p&gt;除了编写更多的测试之外，我们还能做些什么来解决测试覆盖率不足的问题呢？&lt;&#x2F;p&gt;
&lt;p&gt;一种有价值的方法是实施一个安全策略，让 ui 自动化测试监听显而易见的应用程序问题。让我们以 JavaScript 异常为例。&lt;&#x2F;p&gt;
&lt;p&gt;我们的关键功能用例全部测试通过并不意味着一切都如预期般工作。通过在 ui 自动化测试应用程序时监控 JavaScript 异常，增加捕获问题的机会，这样就不需要直接测试所有可用功能了。&lt;&#x2F;p&gt;
&lt;p&gt;在本文中，我将向我们展示如何设置 Playwright，并利用其 fixture 功能，以便在 JavaScript 控制台显示红色警报时使测试失败。&lt;&#x2F;p&gt;
&lt;p&gt;准备好了吗？&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;如果我们更喜欢观看视频而不是阅读博文，请访问&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=LKMwS_u_x8Y&quot;&gt;YouTube 上的视频链接&lt;&#x2F;a&gt;。&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wen-ti-qian-duan-de-js-dai-ma-hui-bao-cuo&quot;&gt;问题：前端的 js 代码会报错&lt;&#x2F;h2&gt;
&lt;p&gt;让我们看一个例子：我一段时间前把 Checkly 博客上的延迟加载功能玩坏了。为了防止再次发生这种情况，我编写了一个在 Checkly 中运行的 Playwright ui 自动化测试。GitHub 操作使用 &lt;a href=&quot;https:&#x2F;&#x2F;www.checklyhq.com&#x2F;docs&#x2F;cli&#x2F;command-line-reference&#x2F;#npx-checkly-test&quot;&gt;npx checkly test&lt;&#x2F;a&gt; 测试每个预览部署，并且如果一切顺利，我的测试用例将转化为生产监控，使用 &lt;a href=&quot;https:&#x2F;&#x2F;www.checklyhq.com&#x2F;docs&#x2F;cli&#x2F;command-line-reference&#x2F;#npx-checkly-deploy&quot;&gt;npx checkly deploy&lt;&#x2F;a&gt;来运行并监控。&lt;&#x2F;p&gt;
&lt;p&gt;以下是完整的 Playwright 测试代码。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Checkly 博客延迟加载&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:3000&#x2F;blog&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 定位所有博客文章
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;articles &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;a[title=&amp;quot;Visit this post&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 计算最初包含的文章数量
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;articleCount &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;articles&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 将最后一篇文章滚动到视图中，以触发延迟加载
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;articles&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;last&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;scrollIntoViewIfNeeded&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 等待更多文章加载和渲染
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newCount &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;articles&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newCount&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;articleCount&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toPass&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我原以为这种方法会安全，但事实并非如此（剧透：我错了）。&lt;&#x2F;p&gt;
&lt;p&gt;该测试导航到博客并测试了实现的延迟加载功能，但没有监控网站的其他问题。功能性的延迟加载是否意味着页面上的所有内容都正确工作？并非如此。&lt;&#x2F;p&gt;
&lt;p&gt;即使在向下滚动页面后页面上有更多的博客文章，也会忽略高度关键的用户问题，包括各种的 JavaScript 异常。该测试仅覆盖一个功能，忽略了其余部分。这并不理想！&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;images.prismic.io&#x2F;checklyhq&#x2F;4c0c430b-0b69-4ebb-9721-082baff0a8e8_javascript-exception-monitoring.jpg?auto=compress,format&quot; alt=&quot;图片 1: 旁边的成功 Playwright 测试显示出现应用程序的情况。&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;监听抛出的 JavaScript 异常是防止 UI 奔溃的一种保护措施。&lt;&#x2F;p&gt;
&lt;p&gt;如前所述，我们可能没有时间或能力对每个功能进行 ui 自动化，但是&lt;strong&gt;我们的 ui 自动化测试应该至少关注显而易见的问题&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;那么，如何监听抛出的 JS 异常呢？&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jian-ting-playwright-ce-shi-zhong-de-javascript-yi-chang&quot;&gt;监听 Playwright 测试中的 JavaScript 异常&lt;&#x2F;h2&gt;
&lt;p&gt;在 Playwright 中监听页面事件非常简单。page 对象带有一个方便的 &lt;code&gt;on&lt;&#x2F;code&gt; 函数，允许我们监听多种页面事件类型（&lt;code&gt;&quot;load&quot;&lt;&#x2F;code&gt;、&lt;code&gt;&quot;pageerror&quot;&lt;&#x2F;code&gt;、&lt;code&gt;&quot;crash&quot;&lt;&#x2F;code&gt; 等）。对于 JavaScript 异常跟踪，我们需要的是 &lt;code&gt;&quot;pageerror&quot;&lt;&#x2F;code&gt; 事件。&lt;&#x2F;p&gt;
&lt;p&gt;我们要考虑测试中抛出的异常，搞一个事件侦听器并将所有抛出的异常收集到一个数组中。一旦 ui 自动化测试功能通过，断言我们的错误数组不包含任何条目。如果有，我们的测试将失败。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Checkly 博客延迟加载&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 设置一个新的数组以收集抛出的错误
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Array&amp;lt;Error&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在测试会话期间监听异常
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pageerror&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 所有我们的测试代码...
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 断言没有发生任何错误
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveLength&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;em&gt;请注意，在测试用例与页面交互之前必须初始化事件侦听器。理想情况下，我们的 &lt;code&gt;page.on&lt;&#x2F;code&gt; 代码应该首先出现在测试用例中。&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;虽然上面的代码片段非常适合在单个测试用例中实现 js 的异常跟踪，但在大型测试项目中就不太好维护了。我们不希望在每个测试用例中重复相同的事件处理逻辑。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ru-guo-javascript-pao-chu-yi-chang-rang-ui-zi-dong-hua-ce-shi-zi-dong-shi-bai&quot;&gt;如果 JavaScript 抛出异常，让 ui 自动化测试自动失败&lt;&#x2F;h2&gt;
&lt;p&gt;我们可以用 &lt;code&gt;beforeEach&lt;&#x2F;code&gt; 或 &lt;code&gt;beforeAll&lt;&#x2F;code&gt; 钩子在测试之前和之后运行代码，但是一个被低估的 Playwright 特性，这个特性值得更多关注！&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-fixtures&quot;&gt;Playwright fixtures&lt;&#x2F;a&gt; 允许我们以结构化方式组织代码。它们还允许我们在测试之前和之后运行代码，并且甚至可以提供配置修改和测试数据生成。对于许多其他用例，fixtures 非常有用。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fixture-dai-ma&quot;&gt;fixture 代码&lt;&#x2F;h3&gt;
&lt;p&gt;在 Playwright 中创建 fixture 以捕获 JavaScript 异常并自动使测试失败。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 将其放在通用 fixture 部分
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-fixtures#fixtures
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Array&amp;lt;Error&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在启动 Playwright 测试用例之前，注册事件侦听器
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;errorListener &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pageerror&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用 fixture 注册事件侦听器
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用 &amp;#39;errorListener&amp;#39; 定制的 &amp;#39;launchContext&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customFixture &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page }&amp;gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;testInfo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 创建一个新页面实例
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 注册错误侦听器
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;errorListener&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用该页面进行测试
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 关闭页面并终止浏览器实例
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 添加异常捕获到我们的 Playwright 测试
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;customFixture&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Checkly 博客延迟加载&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:3000&#x2F;blog&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 所有我们的测试代码...
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 断言没有发生任何错误
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveLength&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;xiang-mu-ji-de-fixture-she-zhi&quot;&gt;项目级的 fixture 设置&lt;&#x2F;h3&gt;
&lt;p&gt;接下来，我们应该为将来的扩展性和可维护性做好准备。将上述 fixture 提取到一个独立的文件中，并将其与其他 fixture 和自定义逻辑一起集成。&lt;&#x2F;p&gt;
&lt;p&gt;为了保持代码的整洁和有序，我们应该考虑把一个大型 fixture 放在自己的文件夹中，并在单独的模块中引用。这种方法允许我们添加额外的测试代码和检查脚本，而无需让代码库炸裂。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; fixtures.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;report&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;errorListener &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pageerror&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; custom-fixture.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customFixture &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;fixtures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;report&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;customFixture&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Checkly 博客延迟加载&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:3000&#x2F;blog&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 所有我们的测试代码...
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 断言没有发生任何错误
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveLength&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; report.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Array&amp;lt;Error&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;结论 — 使用扩展的 Playwright 设置进行更多测试&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;即使是简单的 Playwright ui 自动化测试，也可以通过添加一些基本的 JS 错误跟踪来提高其功能。通过了解如何设置和组织代码，我们可以确保未来的更改不会影响基础测试集的稳定性。&lt;&#x2F;p&gt;
&lt;p&gt;虽然这种方法不会取代更全面的测试战略，但它确实提供了一个低成本的方法来提高测试用例的可靠性。通过识别在测试中自动捕获的常见问题，我们可以通过捕获简单的 JS 错误和异常来提升 ui 自动化测试的价值。&lt;&#x2F;p&gt;
&lt;p&gt;所以，让我们回顾一下我们可以做些什么：在 Playwright 中使用 fixture 跟踪 JavaScript 异常，让我们的 ui 自动化测试更智能，更具鲁棒性。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customFixture &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;fixtures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;report&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;customFixture&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Checkly 博客延迟加载&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:3000&#x2F;blog&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 所有我们的测试代码...
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 断言没有发生任何错误
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveLength&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.checklyhq.com&#x2F;blog&#x2F;track-frontend-javascript-exceptions-with-playwright&#x2F;&quot;&gt;阅读原文&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;
&lt;strong&gt;发布时间：&lt;&#x2F;strong&gt; 2023-11-20&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright可以做API测试吗？当然可以✌️</title>
        <published>2023-11-16T00:00:00+00:00</published>
        <updated>2023-11-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/can-we-use-playwright-for-api-automation-project-yes/"/>
        <id>https://playwright.itest.info/blog/can-we-use-playwright-for-api-automation-project-yes/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/can-we-use-playwright-for-api-automation-project-yes/">&lt;p&gt;大家好。很长时间没见了，今天我要和大家聊聊我最近一直在使用的一个框架：Playwright。几乎每个从事测试领域的人都听过 Playwright 的名字。但在大多数测试自动化项目中，Playwright 被用于 UI 测试。经过团队的一些研究，我们决定为什么不将其用于 API 测试自动化项目呢，并开始了研究。到目前为止，我们有一个不错的项目成功地运行了 API 测试。让我告诉你如何从零开始一个 API 测试自动化项目。我们可以通过几个示例来结束这个话题。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-zhuang&quot;&gt;安装&lt;&#x2F;h2&gt;
&lt;p&gt;在安装 Playwright 之前，请确保你的环境中安装了 &lt;strong&gt;node.js&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 安装 playwright
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; install @playwright&#x2F;test
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 创建一个空项目目录
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;span&gt; api-test-project
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;cd&lt;&#x2F;span&gt;&lt;span&gt; api-test-project
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; init&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -y
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;让我们以一个预订接口为例。我假设我们将在这个结构中测试 GET、POST 和 DELETE 请求。在创建项目结构时，有一点需要注意。我们需要避免在构建结构时重复代码。我们应该尽量编写尽可能干净和可读的代码。因此，我选择将我的服务和测试分开存放。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;项目结构大致如下：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;booking-api-test&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt; ├── tests&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt; │ ├── get-booking.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt; │ ├── post-booking.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt; │ ├── delete-booking.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt; │ └── ...
&lt;&#x2F;span&gt;&lt;span&gt; ├── services&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt; │ ├── api-service.ts
&lt;&#x2F;span&gt;&lt;span&gt; │ ├── auth-service.ts
&lt;&#x2F;span&gt;&lt;span&gt; │ └── ...
&lt;&#x2F;span&gt;&lt;span&gt; ├── package.json
&lt;&#x2F;span&gt;&lt;span&gt; ├── tsconfig.json
&lt;&#x2F;span&gt;&lt;span&gt; └── .env
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;现在让我们编写服务和测试代码。我将假设这个结构中有身份验证。对于这种身份验证，我假设 Headers 中有一个 accessToken 值。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;bu-zou-2-chuang-jian-fu-wu-wen-jian&quot;&gt;步骤 2：创建服务文件&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;api-service.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;APIService {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getBooking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;**&#x2F;bookings&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createBooking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;accessToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;bookingData&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;**&#x2F;bookings&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;      method: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      headers: {
&lt;&#x2F;span&gt;&lt;span&gt;        Authorization: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Bearer ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Content-Type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;application&#x2F;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;      body: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingData&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;deleteBooking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;accessToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;**&#x2F;bookings&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;      method: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;DELETE&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      headers: {
&lt;&#x2F;span&gt;&lt;span&gt;        Authorization: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Bearer ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; auth-service.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;AuthService {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getAccessToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 获取访问令牌所需的 API 调用
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 例如，你可以使用 OAuth 2.0 进行身份验证
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;bu-zou-3-chuang-jian-api-ce-shi-wen-jian&quot;&gt;步骤 3：创建 API 测试文件&lt;&#x2F;h2&gt;
&lt;p&gt;现在我们可以创建测试文件。作为示例，让我们创建 &lt;strong&gt;get-booking.spec.ts&lt;&#x2F;strong&gt;、&lt;strong&gt;post-booking.spec.ts&lt;&#x2F;strong&gt; 和 &lt;strong&gt;delete-booking.spec.ts&lt;&#x2F;strong&gt; 文件。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;get-booking.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;APIService&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;AuthService &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET Booking Test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;authService &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;AuthService(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessToken &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;authService&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getAccessToken&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiService &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;APIService(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiService&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getBooking&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessToken&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 测试断言
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingData&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveProperty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;## &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;spec&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;APIService&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;AuthService &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST Booking Test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;authService &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;AuthService(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessToken &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;authService&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getAccessToken&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 预订数据
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 根据你的接口，你可以传递所需的数据
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiService &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;APIService(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiService&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createBooking&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessToken&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingData&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 测试断言
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;201&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createdBooking &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createdBooking&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveProperty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; delete-booking.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;APIService&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;AuthService &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;DELETE Booking Test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;authService &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;AuthService(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessToken &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;authService&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getAccessToken&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiService &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;APIService(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiService&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;deleteBooking&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessToken&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 测试断言
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;204&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个结构实际上向你展示了如何非常简单地获取 accessToken，然后如何使用它。我试图以一种简单的方式解释它。也可以采取其他路径。&lt;&#x2F;p&gt;
&lt;p&gt;创建项目结构可能会根据你和你正在处理的项目有所不同。例如，你可以全局处理这个问题，而不必在每个测试中调用一个方法来访问 accessToken。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;注意：Playwright 像其他框架一样，包含 beforeAll()、beforeEach() 和类似的方法。阅读和遵循文档会对你有帮助。&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bu-zou-4-ci-cd-ji-cheng&quot;&gt;步骤 4：CI&#x2F;CD 集成&lt;&#x2F;h2&gt;
&lt;p&gt;下面是我为 GitHub Actions 准备的一个非常简单的 yaml 文件。这与其他方法一样，可以根据你的项目需求进行调整。我会给你一个简单的示例以供参考，剩下的就靠你了 :)&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;API Test CI&#x2F;CD
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;main
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;api_test&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Checkout Repository
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;checkout@v2
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;       - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Setup Node.js
&lt;&#x2F;span&gt;&lt;span&gt;         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;setup-node@v2
&lt;&#x2F;span&gt;&lt;span&gt;         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;           &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;node-version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;       - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Install Dependencies
&lt;&#x2F;span&gt;&lt;span&gt;         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm install
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;       - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run API Tests
&lt;&#x2F;span&gt;&lt;span&gt;         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个 GitHub Actions 示例将在每次推送时运行 API 测试。你可以通过在 &lt;strong&gt;.env&lt;&#x2F;strong&gt; 文件中存储敏感的密码来提高安全性。&lt;&#x2F;p&gt;
&lt;p&gt;希望这对你有帮助。在本文中，我只是想谈谈如何使用它以及作为初学者你可以如何使用它。当然，你会有自己独特的方法和结构来建立项目。我建议你阅读 Playwright 文档。同时，TypeScript 也为我们提供了灵活性。你也可以使用不同的库。我可能会在下一篇文章中通过提供更多细节来扩展我的观点。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;numanhanduran.medium.com&#x2F;can-we-use-playwright-for-api-automation-project-yes-9857865dedea&quot;&gt;URL Source&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-11-06&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>使用playwright和java从0到1构建自动化测试框架</title>
        <published>2023-11-14T00:00:00+00:00</published>
        <updated>2023-11-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/from-setup-to-success-creating-scalable-tests-with-playwright-java-and-page-object-model-423e27ef795e/"/>
        <id>https://playwright.itest.info/blog/from-setup-to-success-creating-scalable-tests-with-playwright-java-and-page-object-model-423e27ef795e/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/from-setup-to-success-creating-scalable-tests-with-playwright-java-and-page-object-model-423e27ef795e/">&lt;h2 id=&quot;yin-yan&quot;&gt;引言&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;拥抱现代网页自动化:Playwright 与 Java 以及页面对象模型&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wang-ye-zi-dong-hua-ge-xin-playwright-de-jue-qi&quot;&gt;网页自动化革新:Playwright 的崛起&lt;&#x2F;h2&gt;
&lt;p&gt;在 web 开发中,自动化测试已成为不可或缺的一部分。Playwright 的出现标志着这一领域的重大进步。作为一个跨浏览器自动化库,Playwright 使开发人员和测试人员能够为网络应用程序编写可靠且强大的测试。它能够在真实环境下与多个浏览器(包括 Chrome、Firefox 和 Safari)交互,这一特性使其从前辈中脱颖而出。这种能力确保了在不同浏览环境中的全面覆盖和兼容性,这对当今多样化的 web 生态系统中至关重要。&lt;&#x2F;p&gt;
&lt;p&gt;你也可以查看&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@mohsenny&#x2F;deciding-between-cypress-and-playwright-a-comprehensive-guide-4d883d1be147&quot;&gt;这篇文章对 Cypress 和 Playwright 进行了全面比较&lt;&#x2F;a&gt;,因为这两个自动化框架目前都被广泛使用,处于领先地位。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;java-yu-playwright-qiang-da-de-zu-he&quot;&gt;Java 与 Playwright:强大的组合&lt;&#x2F;h2&gt;
&lt;p&gt;Java 以其稳定性、可扩展性和强大的生态系统而闻名,是实现基于 Playwright 的测试自动化的绝佳选择。Java 丰富的库、强大的社区支持和跨平台特性使其成为许多组织的首选语言。当与 Playwright 的功能相结合时,Java 提供了一个强大的框架,用于创建复杂而高效的自动化测试套件。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yin-ru-ye-mian-dui-xiang-mo-xing-pom-yi-shi-xian-ke-wei-hu-de-ce-shi-zi-dong-hua&quot;&gt;引入页面对象模型(POM)以实现可维护的测试自动化&lt;&#x2F;h2&gt;
&lt;p&gt;页面对象模型(POM)是一种设计模式,已成为现代测试自动化策略的基石。POM 提倡为每个网页创建单独的对象,封装页面的结构和行为。这种分离带来了诸多好处:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;增强可维护性:&lt;&#x2F;strong&gt; 网络应用程序 UI 的变化可以通过更新页面对象来管理,最大限度地减少测试脚本的变动。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;提高可读性:&lt;&#x2F;strong&gt; 测试脚本变得更易读懂,因为它们使用反映用户操作的方法,而不是直接与 UI 元素交互。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;可复用性:&lt;&#x2F;strong&gt; 页面对象可以在不同的测试中重复使用,减少代码重复。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;改善协作:&lt;&#x2F;strong&gt; 清晰的结构使团队更容易理解和贡献测试代码,增强协作。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;tan-suo-playwright-de-lu-zhi-hui-fang-gong-ju&quot;&gt;探索 Playwright 的录制回放工具&lt;&#x2F;h2&gt;
&lt;p&gt;在设置好 Playwright 后,你应该探索的第一个功能之一是 Playwright 的代码生成工具,通常被称为&quot;codegen&quot;。Codegen 是一个革命性的功能,它通过记录你与网络应用程序的交互并生成相应的 Playwright 代码来协助创建测试脚本。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;codegen-de-you-shi&quot;&gt;Codegen 的优势:&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;快速脚本创建:&lt;&#x2F;strong&gt; 它显著加快了编写测试脚本的过程,特别是对于复杂的交互。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;学习工具:&lt;&#x2F;strong&gt; 对于 Playwright 新手来说,它是一个出色的学习辅助工具,展示了 Playwright 脚本的结构。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;准确性:&lt;&#x2F;strong&gt; 有助于捕获精确的选择器,减少手动编写选择器时可能出现的错误。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;shi-yong-codegen&quot;&gt;使用 Codegen:&lt;&#x2F;h2&gt;
&lt;p&gt;要使用 codegen,可以在命令行中以 codegen 模式运行 Playwright。这将打开一个浏览器,你可以在其中手动执行你希望自动化的操作。Playwright 将记录这些操作并实时生成相应的代码。以下是启动 codegen 的简单示例:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright codegen example.com
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个命令会启动一个浏览器窗口,你可以在其中与&quot;example.com&quot;进行交互。当你浏览和与网站互动时,Playwright 会为这些操作生成代码:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*wbhbuGRovWEGyW-434juJg.png&quot; alt=&quot;图片 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yu-pom-ji-cheng&quot;&gt;与 POM 集成:&lt;&#x2F;h2&gt;
&lt;p&gt;尽管 codegen 是一个强大的工具,但将生成的代码集成到页面对象模型结构中以保持可维护性是至关重要的。使用生成的代码作为基础,并对其进行完善以适应你的页面类和测试结构。&lt;&#x2F;p&gt;
&lt;p&gt;在接下来的章节中,我们将深入探讨如何使用 Java 设置 Playwright 项目,利用页面对象模型创建一个健壮、可维护和可扩展的自动化框架。&lt;&#x2F;p&gt;
&lt;p&gt;要查看这些概念的实际应用并获取本文中 Google 搜索示例的完整代码,请随时查看这个综合性仓库:&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mohsenny&#x2F;PlaywrightWithJava&quot;&gt;PlaywrightWithJava&lt;&#x2F;a&gt;。它作为一个即用型模板,用于使用页面对象模型实现 Java 的 Playwright 测试。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;di-yi-zhang-playwright-he-java-ru-men&quot;&gt;第一章: Playwright 和 Java 入门&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;奠定基础:设置 Java、Maven 和 Playwright&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在本章中,我们将指导你完成使用 Java 和 Maven 设置 Playwright 项目的初始步骤。这个设置是创建高效且可扩展的 web 自动化框架的第一步。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-chu-shi-hua-java-he-maven-huan-jing&quot;&gt;1. 初始化 Java 和 Maven 环境:&lt;&#x2F;h3&gt;
&lt;p&gt;在深入 Playwright 之前,确保你的系统上已安装 Java 和 Maven。Java 是你将使用的编程语言,而 Maven 是一个强大的构建工具,简化了依赖管理和项目构建。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;安装 Java: 你需要在系统上安装 Java JDK。你可以从 &lt;a href=&quot;https:&#x2F;&#x2F;www.oracle.com&#x2F;java&#x2F;technologies&#x2F;javase-jdk11-downloads.html&quot;&gt;Oracle 网站&lt;&#x2F;a&gt;下载,或使用 OpenJDK。&lt;&#x2F;li&gt;
&lt;li&gt;安装 Maven: 从 &lt;a href=&quot;https:&#x2F;&#x2F;maven.apache.org&#x2F;download.cgi&quot;&gt;Apache Maven 项目&lt;&#x2F;a&gt;下载并安装 Maven。按照他们网站上提供的安装说明在你的系统上设置它。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;2-an-zhuang-java-ban-playwright&quot;&gt;2. 安装 Java 版 Playwright:&lt;&#x2F;h3&gt;
&lt;p&gt;一旦 Java 和 Maven 设置完成,下一步是将 Playwright 添加到你的项目中。你可以通过在 Maven 项目中添加 Playwright 作为依赖项来实现这一点。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;创建新的 Maven 项目:&lt;&#x2F;strong&gt; 如果你正在开始一个新项目,在你喜欢的 IDE 中或从命令行创建一个 Maven 项目。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;添加 Playwright 依赖:&lt;&#x2F;strong&gt; 在你的 &lt;code&gt;pom.xml&lt;&#x2F;code&gt; 中,添加以下依赖项以包含 Playwright:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;xml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-xml &quot;&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;dependencies&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&amp;lt;!-- Playwright --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;dependency&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;groupId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;com.microsoft.playwright&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;groupId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;artifactId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;playwright&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;artifactId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;[latest_version]&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;scope&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;test&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;scope&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;dependency&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&amp;lt;!-- 测试运行器和断言 --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;dependency&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;groupId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;org.junit.jupiter&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;groupId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;artifactId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;junit-jupiter&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;artifactId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;RELEASE&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;scope&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;test&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;scope&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;dependency&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;dependency&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;groupId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;junit&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;groupId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;artifactId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;junit&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;artifactId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;4.13.2&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;scope&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;test&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;scope&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;dependency&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;dependency&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;groupId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;org.junit&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;groupId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;artifactId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;junit-bom&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;artifactId&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;5.10.1&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;pom&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;scope&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;import&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;scope&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;dependency&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&amp;lt;!-- 根据需要添加其他依赖 --&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;dependencies&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;将 &lt;code&gt;[latest_version]&lt;&#x2F;code&gt; 替换为 Java 版 Playwright 的最新版本,你可以在 &lt;a href=&quot;https:&#x2F;&#x2F;mvnrepository.com&#x2F;artifact&#x2F;com.microsoft.playwright&#x2F;playwright&quot;&gt;Maven 仓库&lt;&#x2F;a&gt;上找到。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;3-yan-zheng-pei-zhi&quot;&gt;3. 验证配置:&lt;&#x2F;h3&gt;
&lt;p&gt;在设置完项目并添加 Playwright 依赖后,至关重要的是验证一切是否正常运行。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;创建一个简单的测试:&lt;&#x2F;strong&gt; 编写一个小脚本来打开浏览器并导航到一个网页。这里是一个例子:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;package &lt;&#x2F;span&gt;&lt;span&gt;com.example;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;microsoft&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;color:#8cdaff;&quot;&gt;TestExample &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public static &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;[] &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright&lt;&#x2F;span&gt;&lt;span&gt; playwright &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;create&lt;&#x2F;span&gt;&lt;span&gt;()) {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Browser&lt;&#x2F;span&gt;&lt;span&gt; browser &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; playwright.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;chromium&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Page&lt;&#x2F;span&gt;&lt;span&gt; page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; browser.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;            page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navigate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;example.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;System&lt;&#x2F;span&gt;&lt;span&gt;.out.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;println&lt;&#x2F;span&gt;&lt;span&gt;(page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;运行你的测试:&lt;&#x2F;strong&gt; 使用 Maven 执行这个脚本,以确保 Playwright 可以启动浏览器并执行操作。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;随着 Java、Maven 和 Playwright 的成功设置,你现在已准备好深入 web 自动化测试的世界。接下来的章节将涵盖你的项目结构以及如何有效地实现页面对象模型以进行可维护和可扩展的测试。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;di-er-zhang-ke-wei-hu-xing-de-xiang-mu-jie-gou&quot;&gt;第二章: 可维护性的项目结构&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;构建你的测试框架:使用 Java 和 Maven 的最佳项目布局&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;创建一个组织良好的项目结构对于维护一个可扩展和高效的自动化框架至关重要。本章概述了使用 Java 和 Maven 的 Playwright 项目的最佳实践,强调可维护性和易用性。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-tui-jian-de-mu-lu-jie-gou&quot;&gt;1. 推荐的目录结构:&lt;&#x2F;h3&gt;
&lt;p&gt;结构化的方法有助于有效管理代码,尤其是随着项目的增长。以下是推荐的项目结构:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;YourProject
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; src
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── main
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   ├── java
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   │   └── com
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   │       ├── pages
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   │       └── utils
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   └── resources
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │       └── translations
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── test
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       ├── java&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       │   └── com
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       │       └── tests
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       └── resources
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;           ├── config
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;           └── utils
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;└──&lt;&#x2F;span&gt;&lt;span&gt; pom.xml
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src&#x2F;main&#x2F;java&lt;&#x2F;code&gt;: 包含测试框架的核心,包括页面对象和实用工具类。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;src&#x2F;test&#x2F;java&lt;&#x2F;code&gt;: 存放测试脚本,以反映应用程序结构的方式组织。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;src&#x2F;test&#x2F;resources&lt;&#x2F;code&gt;: 存储外部资源,如选择器和测试数据,将它们与测试逻辑分开。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;2-mei-ge-zu-jian-de-shuo-ming&quot;&gt;2. 每个组件的说明:&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;页面类&lt;&#x2F;strong&gt; (&lt;code&gt;src&#x2F;main&#x2F;java&#x2F;...&#x2F;pages&#x2F;&lt;&#x2F;code&gt;): 应用程序中的每个网页都应该有一个对应的页面类。这个类封装了网页的所有功能,遵循页面对象模型。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;实用工具类&lt;&#x2F;strong&gt; (&lt;code&gt;src&#x2F;main&#x2F;java&#x2F;...&#x2F;utils&#x2F;&lt;&#x2F;code&gt;): 这些类可以包括各种实用工具,如配置读取器、常见任务的辅助方法等。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;测试类&lt;&#x2F;strong&gt; (&lt;code&gt;src&#x2F;test&#x2F;java&#x2F;...&#x2F;tests&#x2F;&lt;&#x2F;code&gt;): 在这里,你编写实际的测试用例,使用页面类。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;测试数据文件&lt;&#x2F;strong&gt; (&lt;code&gt;src&#x2F;test&#x2F;resources&#x2F;testdata&#x2F;&lt;&#x2F;code&gt;): 将你的测试数据(如用户名和密码)保存在外部文件中,以便于管理并避免硬编码。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Maven 配置&lt;&#x2F;strong&gt; (&lt;code&gt;pom.xml&lt;&#x2F;code&gt;): 这个文件管理项目依赖、插件和其他配置。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;3-ye-mian-lei-de-li-zi&quot;&gt;3. 页面类的例子:&lt;&#x2F;h3&gt;
&lt;p&gt;以下是一个简单的页面类示例,代表登录页面:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;package &lt;&#x2F;span&gt;&lt;span&gt;com.yourcompany.yourproject.pages;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;microsoft&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Page&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;color:#8cdaff;&quot;&gt;LoginPage &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private final &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;page;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 定位器
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private final &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;USERNAME_INPUT &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private final &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;PASSWORD_INPUT &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private final &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;LOGIN_BUTTON &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;LoginPage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; page;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;USERNAME_INPUT&lt;&#x2F;span&gt;&lt;span&gt;, username);
&lt;&#x2F;span&gt;&lt;span&gt;        page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;PASSWORD_INPUT&lt;&#x2F;span&gt;&lt;span&gt;, password);
&lt;&#x2F;span&gt;&lt;span&gt;        page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;LOGIN_BUTTON&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;一个结构良好的项目布局对于测试框架的长期可维护性和可扩展性至关重要。通过将代码组织成逻辑部分并将选择器和测试数据外部化,你为测试自动化工作创建了一个强大的基础。在下一章中,我们将深入探讨如何创建有效且可维护的页面类。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;di-san-zhang-chuang-jian-ye-mian-lei&quot;&gt;第三章: 创建页面类&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;构建强大的页面对象:Playwright 中页面类构建指南&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;使用页面对象模型(POM)的可维护测试自动化框架的关键组成部分之一是创建页面类。本章重点介绍如何使用 Playwright 和 Java 有效构建这些类。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-ye-mian-lei-zai-pom-zhong-de-jiao-se&quot;&gt;1. 页面类在 POM 中的角色:&lt;&#x2F;h3&gt;
&lt;p&gt;页面类作为网页 UI 元素的接口。每个页面类对应应用程序中的一个页面,封装了可以在该页面上执行的操作。这种方法提高了可维护性，优化了可读性,并减少了代码重复。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-gou-jian-ye-mian-lei&quot;&gt;2. 构建页面类:&lt;&#x2F;h2&gt;
&lt;p&gt;以下是创建页面类的分步指南:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;识别 UI 元素:&lt;&#x2F;strong&gt; 确定网页上测试将与之交互的所有元素,如文本框、按钮和链接。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;定义选择器:&lt;&#x2F;strong&gt; 在页面类中存储这些元素的选择器。最佳做法是将这些选择器外部化,但为简单起见,我们将直接在类中定义它们。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;实现操作:&lt;&#x2F;strong&gt; 为页面上可以执行的每个操作创建方法,如输入文本、点击按钮等。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;3-shi-li-chuang-jian-searchpage-lei&quot;&gt;3. 示例:创建 SearchPage 类:&lt;&#x2F;h3&gt;
&lt;p&gt;让我们为 Google 搜索页面创建一个 &lt;code&gt;SearchPage&lt;&#x2F;code&gt; 类:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;package &lt;&#x2F;span&gt;&lt;span&gt;org.pages;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;microsoft&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Locator&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;microsoft&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Page&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;microsoft&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;AriaRole&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;org&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;util&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;TranslationReader&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;color:#8cdaff;&quot;&gt;SearchPage &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private final &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;page;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* 为避免硬编码文本,我们从翻译文件中读取它们
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;       以便在定义选择器时使用。
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;       你可以在这里查看 TranslationReader.java 的内容:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;       https:&#x2F;&#x2F;shorturl.at&#x2F;vRST3 *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;TranslationReader &lt;&#x2F;span&gt;&lt;span&gt;reader &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;TranslationReader&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;SearchPage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; page;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 选择器:返回页面某些元素的选择器
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Locator &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getCookieSelector&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;AriaRole&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;BUTTON&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GetByRoleOptions&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;setName&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;                reader.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getTranslation&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cookie.accept&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        ));
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Locator &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getSearchField&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;AriaRole&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;COMBOBOX&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GetByRoleOptions&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;setName&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;                reader.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getTranslation&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;search&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        ));
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* ...更多选择器 *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 方法:在页面上执行特定操作
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;clickCookieAcceptButton&lt;&#x2F;span&gt;&lt;span&gt;(){
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getCookieSelector&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;performSearch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;searchKeyword&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getMainSearchField&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(searchKeyword);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getSearchButton&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* ...更多方法 *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这个例子中,&lt;code&gt;SearchPage&lt;&#x2F;code&gt; 类代表我们登陆 Google 时的第一个页面。它包括与页面交互的方法,如使用给定关键词搜索和导航到不同的结果标签。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-she-ji-ye-mian-lei-de-zui-jia-shi-jian&quot;&gt;4. 设计页面类的最佳实践:&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;每个页面一个类:&lt;&#x2F;strong&gt; 为应用程序的每个页面创建一个单独的类。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;描述性方法名:&lt;&#x2F;strong&gt; 使用清晰描述操作的名称,如 &lt;code&gt;performSearch()&lt;&#x2F;code&gt; 或 &lt;code&gt;navigateToImageResults()&lt;&#x2F;code&gt;。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;模块化:&lt;&#x2F;strong&gt; 设计你的方法使其小巧、专注且可以复用。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;页面类是使用 Playwright 和 Java 构建可扩展和可维护的测试自动化框架的基础。通过封装与网页的交互,你创建了一个强大且可复用的结构,简化了测试脚本并使其更能适应 UI 的变化。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;di-si-zhang-bian-xie-ce-shi-lei&quot;&gt;第四章:编写测试类&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;实现有效的测试用例:在测试中集成页面对象&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在设置好页面类之后,下一步是编写测试类。这些脚本将使用页面类中定义的方法与网络应用程序进行交互。本章介绍如何使用 Playwright 和 Java 创建这些测试用例。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-bian-xie-ce-shi-lei-de-ji-chu&quot;&gt;1. 编写测试类的基础:&lt;&#x2F;h3&gt;
&lt;p&gt;测试类应该清晰、简洁,并专注于测试逻辑,而不是与网络应用程序交互的细节。这些测试将使用页面类提供的方法。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-gou-jian-ce-shi-lei&quot;&gt;2. 构建测试类:&lt;&#x2F;h3&gt;
&lt;p&gt;一个典型的测试类包括:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;setup 方法:&lt;&#x2F;strong&gt; 在每个测试之前初始化 Playwright 浏览器和其他先决条件。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;测试方法:&lt;&#x2F;strong&gt; 单独的测试用例,每个代表不同的场景。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;清理方法:&lt;&#x2F;strong&gt; 在每个测试之后关闭浏览器并执行任何清理。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;3-li-yong-playwrightextension-shi-xian-gao-xiao-de-ce-shi-she-zhi-he-qing-li&quot;&gt;3. 利用 &lt;code&gt;PlaywrightExtension&lt;&#x2F;code&gt; 实现高效的测试设置和清理&lt;&#x2F;h3&gt;
&lt;p&gt;在自动化测试领域,效率和可复用性至关重要。这就是 &lt;code&gt;PlaywrightExtension&lt;&#x2F;code&gt; 发挥作用的地方,它是一个自定义的 JUnit 扩展,旨在简化测试的 setup 和 teardown 过程。&lt;code&gt;PlaywrightExtension&lt;&#x2F;code&gt; 显著减少了样板代码,确保每个测试类都遵循 DRY(不要重复自己)原则。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;什么是 &lt;strong&gt;PlaywrightExtension&lt;&#x2F;strong&gt;?&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;PlaywrightExtension&lt;&#x2F;code&gt; 是一个自定义的 JUnit 扩展,用于自动化每个测试的 Playwright 资源的初始化和清理。它管理浏览器实例、页面和上下文的生命周期,确保每个测试都从一个干净的状态开始。这种方法消除了在每个测试类中重复设置和清理代码的需要,使你的测试更清晰、更易于维护。&lt;&#x2F;p&gt;
&lt;p&gt;要实现 &lt;code&gt;PlaywrightExtension&lt;&#x2F;code&gt;,将其定义为 JUnit 5 扩展,并用 &lt;code&gt;@ExtendWith(PlaywrightExtension.class)&lt;&#x2F;code&gt; 注解你的测试类。这告诉 JUnit 使用 &lt;code&gt;PlaywrightExtension&lt;&#x2F;code&gt; 中定义的方法来管理测试环境。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;org&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;junit&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;jupiter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;extension&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ExtendWith&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;microsoft&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ExtendWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PlaywrightExtension&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;color:#8cdaff;&quot;&gt;YourTest &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 测试方法在这里
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;PlaywrightExtension&lt;&#x2F;code&gt; 类本身看起来像这样:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;package &lt;&#x2F;span&gt;&lt;span&gt;resources.config;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;java&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nio&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Paths&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;microsoft&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;org&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;junit&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;jupiter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;extension&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;color:#8cdaff;&quot;&gt;PlaywrightExtension &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;implements &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;font-style:italic;color:#8cdaff;&quot;&gt;BeforeEachCallback&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;font-style:italic;color:#8cdaff;&quot;&gt;AfterEachCallback &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Browser &lt;&#x2F;span&gt;&lt;span&gt;browser;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;BrowserContext &lt;&#x2F;span&gt;&lt;span&gt;context;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;page;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Override
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ExtensionContext &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;throws &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Exception &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 覆盖并全局定义 beforeEach
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Override
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;afterEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ExtensionContext &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;throws &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Exception &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 覆盖并全局定义 afterEach
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getPage&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; page;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Browser &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getBrowser&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; browser;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这样我们可以实现以下目标:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;减少样板代码:&lt;&#x2F;strong&gt; 自动处理常见的设置和清理任务。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;一致性:&lt;&#x2F;strong&gt; 为每个测试用例确保一致的测试环境。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;资源管理:&lt;&#x2F;strong&gt; 高效管理和处理 Playwright 资源,防止资源泄漏。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;增强可读性:&lt;&#x2F;strong&gt; 简化测试类,使其更易读和维护。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;在现有的测试类中,你可以用 &lt;code&gt;PlaywrightExtension&lt;&#x2F;code&gt; 替换原本用于设置和清理的 &lt;code&gt;@BeforeEach&lt;&#x2F;code&gt; 和 &lt;code&gt;@AfterEach&lt;&#x2F;code&gt;。以下是修订后的测试类示例:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;package &lt;&#x2F;span&gt;&lt;span&gt;org;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;microsoft&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;org&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;junit&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;jupiter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;org&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;junit&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;jupiter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;extension&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ExtendWith&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;org&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pages&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;SearchPage&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ExtendWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PlaywrightExtension&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;color:#8cdaff;&quot;&gt;SearchTests &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Browser &lt;&#x2F;span&gt;&lt;span&gt;browser;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;SearchPage &lt;&#x2F;span&gt;&lt;span&gt;searchPage;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;page;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;BeforeEach
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createContextAndPage&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这里你可以从 PlaywrightExtension 访问页面
&lt;&#x2F;span&gt;&lt;span&gt;        page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PlaywrightExtension&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navigate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.google.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Test
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;shouldSearch&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用 SearchPage 的测试实现
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;AfterAll
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public static &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;tearDown&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这里你可以从 PlaywrightExtension 访问浏览器
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PlaywrightExtension&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBrowser&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这个例子中,&lt;code&gt;PlaywrightExtension&lt;&#x2F;code&gt; 处理 &lt;code&gt;Page&lt;&#x2F;code&gt; 和 &lt;code&gt;Browser&lt;&#x2F;code&gt; 实例的创建和关闭,从而简化了测试结构。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-shi-li-chuang-jian-sou-suo-ce-shi-lei&quot;&gt;4. 示例:创建搜索测试类:&lt;&#x2F;h3&gt;
&lt;p&gt;让我们创建一个 &lt;code&gt;SearchTest&lt;&#x2F;code&gt; 类,使用 &lt;code&gt;SearchPage&lt;&#x2F;code&gt; 类测试 Google 的搜索功能:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;package &lt;&#x2F;span&gt;&lt;span&gt;org;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;microsoft&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;org&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;junit&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;jupiter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;org&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;junit&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;jupiter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Test&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;org&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;junit&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;jupiter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;extension&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ExtendWith&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;org&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pages&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;SearchPage&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;resources&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PlaywrightExtension&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;microsoft&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;assertions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PlaywrightAssertions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;assertThat&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ExtendWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PlaywrightExtension&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;text-decoration:underline;color:#8cdaff;&quot;&gt;SearchTests &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Browser &lt;&#x2F;span&gt;&lt;span&gt;browser;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;SearchPage &lt;&#x2F;span&gt;&lt;span&gt;searchPage;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;page;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;BeforeEach
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createContextAndPage&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PlaywrightExtension&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getPage&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        searchPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;SearchPage&lt;&#x2F;span&gt;&lt;span&gt;(page);
&lt;&#x2F;span&gt;&lt;span&gt;        page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navigate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.google.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Test
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;shouldSearch&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        searchPage.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;clickCookieAcceptButton&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        searchPage.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;performSearch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Potato&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;assertThat&lt;&#x2F;span&gt;&lt;span&gt;(searchPage.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getSearchField&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;containsText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Potato&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        searchPage.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navigateToImageResults&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 更多断言...
&lt;&#x2F;span&gt;&lt;span&gt;        searchPage.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navigateToAllResults&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        searchPage.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navigateToVideoResults&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 更多断言...
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;AfterAll
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public static &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;tearDown&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        page.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PlaywrightExtension&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBrowser&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这个例子中,&lt;code&gt;shouldSearch&lt;&#x2F;code&gt; 是一个使用 &lt;code&gt;SearchPage&lt;&#x2F;code&gt; 执行 Google 搜索的测试方法。它展示了如何使用页面对象使测试逻辑变得更加可读和简洁。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-shi-xian-qing-xi-ke-du-de-ce-shi-dai-ma-de-ji-qiao&quot;&gt;5. 实现清晰可读的测试代码的技巧&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;描述性测试名称:&lt;&#x2F;strong&gt; 为测试方法使用有意义的名称,描述测试验证的内容。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;模块化测试:&lt;&#x2F;strong&gt; 编写小型、专注的测试,每次验证应用程序的一个功能。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;可复用的 setup 和 teardown:&lt;&#x2F;strong&gt; 使用 &lt;code&gt;@BeforeEach&lt;&#x2F;code&gt; 和 &lt;code&gt;@AfterEach&lt;&#x2F;code&gt; 注解处理常见的设置和清理任务。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;断言:&lt;&#x2F;strong&gt; 包含断言以验证测试的结果,确保它们检查正确的条件。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;测试类是将页面类的功能整合在一起以与应用程序交互的地方。通过遵循这些指南,你可以创建不仅有效捕获 bug,而且易于阅读和维护的测试。&lt;&#x2F;p&gt;
&lt;p&gt;最后,为了展示到目前为止项目结构的样子:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:490&#x2F;1*XVyB7XWls68tWItwqhJuhA.png&quot; alt=&quot;图片 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Google 搜索测试的项目结构&lt;&#x2F;p&gt;
&lt;h2 id=&quot;di-wu-zhang-wai-bu-hua-xuan-ze-qi-he-ce-shi-shu-ju&quot;&gt;第五章:外部化选择器和测试数据&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;增强测试灵活性和维护性:管理选择器和测试数据的策略&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在维护一个强大且适应性强的测试自动化框架中,高效管理选择器和测试数据至关重要。本章探讨如何在 Playwright 项目中外部化选择器和测试数据,提高灵活性并减少频繁更改代码的需求。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-wai-bu-hua-xuan-ze-qi-de-zhong-yao-xing&quot;&gt;1. 外部化选择器的重要性:&lt;&#x2F;h3&gt;
&lt;p&gt;在测试脚本或页面类中硬编码选择器可能导致维护噩梦,尤其是当 UI 发生变化时。通过外部化这些选择器,你可以轻松更新它们,而无需更改核心测试逻辑。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-shi-yong-json-jin-xing-xuan-ze-qi-guan-li&quot;&gt;2. 使用 JSON 进行选择器管理:&lt;&#x2F;h3&gt;
&lt;p&gt;管理选择器的一种有效方法是将它们存储在 JSON 文件中。这允许你更改选择器而无需触及 Java 代码。以下是一个例子:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;乙醇的注释 👀：这个方法我既不喜欢也不推荐。对于 java 代码来说，直接在 pom 里修改定位器是有代码提示的，比独立出来写 json 文件要好一点&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JSON 选择器文件&lt;&#x2F;strong&gt; (&lt;code&gt;loginSelectors.json&lt;&#x2F;code&gt;):&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;usernameInput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;passwordInput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;loginButton&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;在页面类中读取选择器:&lt;&#x2F;strong&gt; 修改你的页面类以从这些 JSON 文件读取选择器。你可以使用 Java 的内置库或第三方库如 Jackson 或 Gson 来解析 JSON 文件。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;3-wai-bu-hua-ce-shi-shu-ju&quot;&gt;3. 外部化测试数据:&lt;&#x2F;h3&gt;
&lt;p&gt;与选择器类似,测试数据如用户名、密码或任何输入数据也应该外部化。这种做法不仅使你的测试更易读,还简化了数据管理并增强了安全性。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JSON 测试数据文件&lt;&#x2F;strong&gt; (&lt;code&gt;loginTestData.json&lt;&#x2F;code&gt;):&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;validUser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;user1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pass123&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;invalidUser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;user2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;wrongpass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;在测试中使用测试数据:&lt;&#x2F;strong&gt; 在测试类中加载这些数据,并用它来驱动你的测试。这种方法对于数据驱动测试特别有用。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;4-xuan-ze-qi-he-ce-shi-shu-ju-guan-li-de-zui-jia-shi-jian&quot;&gt;4. 选择器和测试数据管理的最佳实践:&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;保持组织:&lt;&#x2F;strong&gt; 以有组织的方式存储你的选择器和测试数据,最好在项目内的专用目录中。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;版本控制:&lt;&#x2F;strong&gt; 将这些 JSON 文件包含在你的版本控制系统中,以跟踪更改并维护更新历史。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;安全考虑:&lt;&#x2F;strong&gt; 对敏感数据要谨慎。对于高度敏感的数据(如真实密码),使用环境变量或安全保管库。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;外部化选择器和测试数据增强了测试自动化框架的可维护性和可扩展性。它允许更容易地更新和修改,使你的测试对应用程序 UI 和数据集的变化更具弹性。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-lun&quot;&gt;结论&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;拥抱自动化测试的未来:关键要点和下一步&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果你想深入了解并查看完整设置,我准备的 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mohsenny&#x2F;PlaywrightWithJava&quot;&gt;PlaywrightWithJava&lt;&#x2F;a&gt; 仓库提供了一个全面的示例,展示了本文讨论的概念。&lt;&#x2F;p&gt;
&lt;p&gt;在结束我们关于使用页面对象模型设置 Playwright 与 Java 的指南时,重要的是回顾关键要点 并进行讨论下一步行动。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-guan-jian-dian-hui-gu&quot;&gt;1. 关键点回顾:&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Playwright 和 Java:&lt;&#x2F;strong&gt; 我们探索了 Playwright 与 Java 的强大组合,突出了这对组合在自动化测试中的优势。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;项目结构:&lt;&#x2F;strong&gt; 讨论了最佳的项目布局,强调了可维护性和组织性。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;页面对象模型(POM):&lt;&#x2F;strong&gt; 概述了 POM 的实现,展示了它在创建可扩展和可维护的测试脚本中的重要性。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;页面类:&lt;&#x2F;strong&gt; 我们深入研究了页面类的创建,这是 POM 的核心组件,并提供了说明其结构和功能的示例。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;测试类:&lt;&#x2F;strong&gt; 涵盖了编写有效测试类的结构和最佳实践,确保测试的清晰性和高效。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;外部化选择器和数据:&lt;&#x2F;strong&gt; 强调了外部化选择器和测试数据的重要性,突出其在增强测试灵活性和可维护性方面的作用。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;2-gu-li-tan-suo-he-gua-ying&quot;&gt;2. 鼓励探索和适应:&lt;&#x2F;h3&gt;
&lt;p&gt;web 自动化的世界广阔且不断发展。虽然本指南提供了坚实的基础,但继续探索和适应至关重要。请持续关注 Playwright、Java 和测试自动化的最新发展。尝试不同的方法和工具,找出最适合你特定需求的方式。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-she-qu-he-chi-xu-xue-xi&quot;&gt;3. 社区和持续学习:&lt;&#x2F;h2&gt;
&lt;p&gt;与社区互动是无价的。参与论坛、参加网络研讨会,并为开源项目做贡献。这种互动促进学习,可以让你了解行业的最佳实践。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;4-xia-yi-bu&quot;&gt;4. 下一步:&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;集成高级功能&lt;&#x2F;strong&gt;: 探索 Playwright 和 Java 的高级功能,如处理动态内容、使用 iframe 等。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;CI&#x2F;CD 集成:&lt;&#x2F;strong&gt; 考虑将你的测试集成到 CI&#x2F;CD 流程中,实现自动化测试执行。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;性能和安全测试:&lt;&#x2F;strong&gt; 扩展你的测试范围,包括性能和安全方面。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;5-zui-hou-si-kao&quot;&gt;5. 最后思考:&lt;&#x2F;h2&gt;
&lt;p&gt;自动化测试是一个持续学习和改进的旅程。本指南中讨论的设置和实践只是开始。拥抱自动化测试带来的挑战和机遇,你将为 web 自动化的质量和成功做出重大贡献。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@mohsenny&#x2F;from-setup-to-success-creating-scalable-tests-with-playwright-java-and-page-object-model-423e27ef795e&quot;&gt;URL Source&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-11-14&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>The Definitive Guide to API Test Automation With Playwright: Part 15 - Adding Test Tags to Get Targeted Feedback</title>
        <published>2023-11-13T00:00:00+00:00</published>
        <updated>2023-11-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-15-adding-test-tags-to-get-targeted-feedback/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-15-adding-test-tags-to-get-targeted-feedback/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-15-adding-test-tags-to-get-targeted-feedback/">&lt;p&gt;In this guide I&#x27;ll cover how tags work, link to other articles where I discuss some tips on how to use tags, and show some code examples of the tags I like to use.&lt;&#x2F;p&gt;
&lt;p&gt;If you are just now joining us for the first time feel free to check out the &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;introduction post&lt;&#x2F;a&gt; and the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&quot;&gt;playwright-api-test-demo repository&lt;&#x2F;a&gt; which in which all code examples are included.&lt;&#x2F;p&gt;
&lt;p&gt;The main tags I typically add to a a project include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;endpoints: &lt;code&gt;@auth&lt;&#x2F;code&gt;, &lt;code&gt;@booking&lt;&#x2F;code&gt;, &lt;code&gt;@branding&lt;&#x2F;code&gt;, &lt;code&gt;@message&lt;&#x2F;code&gt;, &lt;code&gt;@report&lt;&#x2F;code&gt;, &lt;code&gt;@room&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;happy path tests: &lt;code&gt;@happy&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;flaky tests:  &lt;code&gt;@unsatisfactory&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Of course you can add any type of tag you&#x27;d but these are typically the tags I&#x27;ve found useful for api automation suites. The official docs for tags can be found below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-annotations#tag-tests&quot;&gt;Annotations&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here is an example of a spec with tags implemented. Note I have add the &lt;code&gt;@auth&lt;&#x2F;code&gt; tag in the describe block which is at the highest level. This will ensure that all specs within the describe block are run when running with the command &lt;code&gt;npx playwright tests --grep @auth&lt;&#x2F;code&gt;. As you see in the tests below I&#x27;ve also included the &lt;code&gt;@happy&lt;&#x2F;code&gt; tag in only 1 of the specs below. My thought around this decision is to ensure the happy path from the &lt;code&gt;auth&#x2F;login&lt;&#x2F;code&gt; endpoint I really only want to make sure that I can log in successfully as it is a critical path when I run my happy path tests. The other checks though useful are less critical.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;auth&#x2F;login.post.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: POST &#x2F;auth&#x2F;login
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@fixtures&#x2F;fixtures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Env &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;env&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&#x2F;login POST requests @auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with valid credentials @happy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with invalid username and password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with valid username and invalid password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with invalid username and valid password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with no username and valid password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with empty body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with no body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with valid credentials then validate with token @happy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As a part of this exercise I went ahead and added a flakey test to my repository. Now I know there is lots of debate on what you should do with your flakey tests, but if you find some sort of value in them whatsoever sometimes it doesn&#x27;t make sense to completely remove them from your repo, but rather put them in a bucket of tests that don&#x27;t run as often. In this scenario and in my workplace, we decided on the tag &lt;code&gt;@unsatisfactory&lt;&#x2F;code&gt;. To me this tells me the test doesn&#x27;t meet the standard expectations I have to live as a part of the repository and run on a frequent basis, but it allows me to be able to run say once a week or some other cadence, where I can dive into the results of the test and assess if the test(s) flaked out or actually uncovered an issue.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;test.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;flakey test @unsatisfactory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randomBoolean &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;Math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;random&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randomBoolean&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To run all tests except for the &lt;code&gt;@unsatisfactory&lt;&#x2F;code&gt; tag we&#x27;ll run our suite using the command &lt;code&gt;npx playwright test --grep-invert @unsatisfactory&lt;&#x2F;code&gt; which will run all the tests EXCEPT the @unsatisfactory tag. This can also be combined with the &lt;code&gt;--grep&lt;&#x2F;code&gt; command which we&#x27;ll look at next.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-cli&quot;&gt;command line documentation&lt;&#x2F;a&gt; provides us with insights on how to run the playwright tests from the command line. Both commands below will work&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-g @ tag&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--grep @tag&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It&#x27;s also worth noting that you can also pass in multiple tags such as if I wanted to run all the tests with the &lt;code&gt;@happy&lt;&#x2F;code&gt; tag and&#x2F;or &lt;code&gt;@room&lt;&#x2F;code&gt; tag.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;--grep &quot;@happy|@room&quot;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This article covers additional details and strategies that can be used with tags&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;run-a-subset-of-tests-with-grep-and-grep-invert-in-package-json&#x2F;&quot;&gt;How do I run a subset of tests with Playwright Test Runner using grep functionality?&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;running-tagged-tests-from-ui-mode&quot;&gt;Running Tagged Tests From UI Mode&lt;&#x2F;h2&gt;
&lt;p&gt;You are also able to run tagged tests from Playwright&#x27;s UI mode. To enter ui mode run the command &lt;code&gt;npx playwright test --ui&lt;&#x2F;code&gt;. This will bring up a UI in which you can access all your tests along with a plethora of developer tools to help build and debug tests. In the below example I added &lt;code&gt;@happy&lt;&#x2F;code&gt; within the search box and pressed the&lt;code&gt;Enter&lt;&#x2F;code&gt; key, this will filter the tests down and then run the tests.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;10&#x2F;image-4.png&quot; alt=&quot;Image 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;updating-package-json-with-npm-scripts&quot;&gt;Updating package.json With npm Scripts&lt;&#x2F;h2&gt;
&lt;p&gt;As you can see below I&#x27;ve added some new npm run scripts most of which include the &lt;code&gt;--grep-invert @unsatisfactory&lt;&#x2F;code&gt; which we covered before and will prevent any tests that have this tagged to not run. This is useful for tagging tests that may be flaky or items you don&#x27;t want running on every run, but you do want to keep the spec for documentation or to run and get feedback on an ad-hoc basis.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; package.json
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92649;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92649;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test --grep-invert @unsatisfactory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test:generate:schema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GENERATE_SCHEMA_TRACKING_DATA=true npx playwright test --grep-invert @unsatisfactory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test:staging&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test_env=staging npx playwright test --grep-invert @unsatisfactory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test:local&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test_env=local npx playwright test --grep-invert @unsatisfactory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test:happy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test --grep @happy --grep-invert @unsatisfactory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test:unsatisfactory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test --grep @unsatisfactory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test --ui&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92649;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92649;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;updating-playwright-config-with-proper-teardown&quot;&gt;Updating Playwright Config with proper teardown&lt;&#x2F;h2&gt;
&lt;p&gt;A few guides back we added a setup  projects within our repository in order to create valid auth sessions and to calculate coverage and a teardown project to run after all tests were complete in order to report on any warnings within our automation suite. When implementing this, there was an error, which wasn&#x27;t evident until now we try and introduce an &lt;code&gt;@unsatisfactory&lt;&#x2F;code&gt; tag using the &lt;code&gt;--grep-invert&lt;&#x2F;code&gt; command to run all tests except these. Our first implementation I was missing &lt;code&gt;teardown&lt;&#x2F;code&gt; within the setup project. Below you can see I&#x27;ve now included that, and no longer have a decency listed under teardown. The playwright docs can be found &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-global-setup-teardown#teardown-example&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; playwright.config.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  projects: [
&lt;&#x2F;span&gt;&lt;span&gt;    { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, testMatch: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;coverage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;, teardown: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;teardown&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api-checks&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      dependencies: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;teardown&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      testMatch: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;completion&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;teardown&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;introducing-bruno-for-exploring-the-api&quot;&gt;Introducing Bruno for Exploring the API&lt;&#x2F;h2&gt;
&lt;p&gt;During this round of work I took some time and removed the Thunder client files and introduced the &lt;code&gt;bruno&lt;&#x2F;code&gt; folder for managing examples of API calls for exploratory testing. &lt;a href=&quot;https:&#x2F;&#x2F;docs.usebruno.com&#x2F;&quot;&gt;Bruno&lt;&#x2F;a&gt; is an open source IDE for exploring and testing APIs. You can read the &lt;a href=&quot;https:&#x2F;&#x2F;docs.usebruno.com&#x2F;manifesto.html&quot;&gt;Bruno Manifesto&lt;&#x2F;a&gt; to get a better idea of why I am swapping.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;10&#x2F;image-3.png&quot; alt=&quot;Image 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;What&#x27;s really nice about this is the entire collection and environment files are all stored within the &lt;code&gt;.&#x2F;bruno&#x2F;&lt;&#x2F;code&gt; directory on the root level. Each request is easily readable and available whenever you clone the repository.&lt;&#x2F;p&gt;
&lt;p&gt;All the changes discussed in this article can be found in the pull request below. There were a lot of updates where I touched each spec file and added tags appropriately.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pull&#x2F;18&#x2F;files&quot;&gt;adding tags to suite by BMayhew&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;&#x2F;h2&gt;
&lt;p&gt;Using tags within your tests allows you to manage running different tests based on your needs. I&#x27;ve found the flexibility with this approach has been able to solve any problem I&#x27;ve faced when building out automation suites.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks for reading! If you found this helpful, reach out and let me know on &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; or consider &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;buying me a cup of coffee&lt;&#x2F;a&gt;. If you want more content delivered to you in your inbox subscribe below, and be sure to leave a ❤️ to show some love.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-15-adding-test-tags-to-get-targeted-feedback&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-11-13T13:30:17.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何使用 Faker 快速创建真实数据</title>
        <published>2023-11-06T00:00:00+00:00</published>
        <updated>2023-11-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-to-quickly-get-realistic-data-with-faker-for-playwright-tests/"/>
        <id>https://playwright.itest.info/blog/how-to-quickly-get-realistic-data-with-faker-for-playwright-tests/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-to-quickly-get-realistic-data-with-faker-for-playwright-tests/">&lt;p&gt;在测试注册流程或填写表单时,输入一个简单的&quot;Jane Doe&quot;很容易。但当你还需要输入电子邮件、地址,并在不同的语言环境中完成这些操作时,事情就变得复杂了。&lt;&#x2F;p&gt;
&lt;p&gt;当然,你可以自己创建测试数据集,但为什么不直接用代码生成呢?&lt;&#x2F;p&gt;
&lt;p&gt;这正是 &lt;a href=&quot;https:&#x2F;&#x2F;fakerjs.dev&#x2F;&quot;&gt;Faker&lt;&#x2F;a&gt; 的用途:它可以生成从姓名、地址到公司名称的任何信息。它能为你的测试创造处逼真的假数据。&lt;&#x2F;p&gt;
&lt;p&gt;Faker 是一个 Node.js 库,所以让我们用 &lt;code&gt;npm install --save-dev @faker-js&#x2F;faker&lt;&#x2F;code&gt; 来安装它&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@faker-js&#x2F;faker&#x2F;locale&#x2F;en&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randomName &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;person&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fullName&lt;&#x2F;span&gt;&lt;span&gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Rowan Nikolaus
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randomEmail &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;internet&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Kassandra.Haley@erich.biz
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;生成假数据的函数一般是按领域分组的:个人、公司、地点、日期等。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yu-playwright-de-ji-cheng&quot;&gt;与 Playwright 的集成&lt;&#x2F;h2&gt;
&lt;p&gt;在 Playwright 测试中使用 Faker 非常简单:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@faker-js&#x2F;faker&#x2F;locale&#x2F;en&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试应用程序&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;应该使用用户名和密码创建账户&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;internet&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;userName&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;internet&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;internet&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;exampleEmail&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 访问网页并创建账户
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.example.com&#x2F;register&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { exact: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;confirm password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Register&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 现在,我们尝试使用这些凭据登录
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.example.com&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Register&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 我们应该成功登录到仪表板页面
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dashboard&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;seed-she-zhi&quot;&gt;seed 设置&lt;&#x2F;h2&gt;
&lt;p&gt;每次测试运行时,你都会获得新的数据。但有时,你可能希望数据不那么随机。例如,如果你进行截图对比测试,每次值都发生变化会导致测试失败。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;xkcd.com&#x2F;221&#x2F;&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;imgs.xkcd.com&#x2F;comics&#x2F;random_number.png&quot; alt=&quot;图片 1: xkcd 图像:一个返回 4 的 getRandomNumber 函数。评论说&amp;quot;通过公平骰子选择。保证随机&amp;quot;&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;梗图：不那么随机 👆&lt;&#x2F;p&gt;
&lt;p&gt;为了保持测试的确定性,你需要&lt;a href=&quot;https:&#x2F;&#x2F;fakerjs.dev&#x2F;api&#x2F;faker.html#seed&quot;&gt;为 Faker 的随机生成器设置 seed&lt;&#x2F;a&gt;,并在每次测试之间重置它。&lt;&#x2F;p&gt;
&lt;p&gt;这里有一个文件,包含一个设置了 seed 的测试(总是相同的值)和一个随机测试:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@faker-js&#x2F;faker&#x2F;locale&#x2F;en&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这将在每次测试后重新为我们的 faker 实例设置种子
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;afterEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;seed&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用设置种子的 Faker 生成熊&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 用一个静态数字为我们的 faker 实例设置种子
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;seed&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;123&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;animal &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;animal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;bear&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;animal&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;animal&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toMatchSnapshot&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;随机生成熊&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;animal &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;animal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;bear&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;animal&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;让我们检查结果,这里有 3 次运行:每个浏览器一次。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; npx &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span&gt; test
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;使用&lt;&#x2F;span&gt;&lt;span&gt; 1 个工作进程运行 6 个测试
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;✓&lt;&#x2F;span&gt;&lt;span&gt;  1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;chromium&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt; › seed.spec.ts:9:1 › 使用设置种子的 Faker 生成熊 (75ms)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;亚洲黑熊
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;✓&lt;&#x2F;span&gt;&lt;span&gt;  2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;chromium&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt; › seed.spec.ts:18:1 › 随机生成熊 (65ms)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;大熊猫
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;✓&lt;&#x2F;span&gt;&lt;span&gt;  3 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;firefox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt; › seed.spec.ts:9:1 › 使用设置种子的 Faker 生成熊 (62ms)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;亚洲黑熊
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;✓&lt;&#x2F;span&gt;&lt;span&gt;  4 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;firefox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt; › seed.spec.ts:18:1 › 随机生成熊 (29ms)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;美洲黑熊
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;✓&lt;&#x2F;span&gt;&lt;span&gt;  5 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;webkit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt; › seed.spec.ts:9:1 › 使用设置种子的 Faker 生成熊 (58ms)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;亚洲黑熊
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;✓&lt;&#x2F;span&gt;&lt;span&gt;  6 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;webkit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt; › seed.spec.ts:18:1 › 随机生成熊 (27ms)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;眼镜熊
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt; 个测试通过 (2.5s)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如你所见,&lt;code&gt;使用设置种子的 Faker 生成熊&lt;&#x2F;code&gt;这个用例是截图对比的测试用例。它顺利通过,并且在之后的每次运行中都会通过。控制台输出始终是&lt;code&gt;亚洲黑熊&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;随机生成熊&lt;&#x2F;code&gt;的控制台输出在每次测试运行时的结果都不一样。这是因为每次测试后,我们都重置了 Faker 的种子。&lt;&#x2F;p&gt;
&lt;p&gt;你可以在 Faker 的文档中的&lt;a href=&quot;https:&#x2F;&#x2F;fakerjs.dev&#x2F;guide&#x2F;usage.html#reproducible-results&quot;&gt;可重现结果&lt;&#x2F;a&gt;部分查看更多详情。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h2&gt;
&lt;p&gt;Faker 是一个方便的库,用于生成假的但是看起来又很真实的数据。我们已经了解了它的基本用法,以及如何将其添加到我们的测试中,而且还知道了可以通过设置 seed 来控制随机性。事实证明,Faker 与 Playwright Test 一起使用非常简单!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;嗨!我是 Jean-François,我是一名前端技术主管。我帮助团队在提高敏捷性和技术卓越性。我对软件工程和测试感兴趣。
你可以在 &lt;a href=&quot;https:&#x2F;&#x2F;jfgreffier.com&#x2F;&quot;&gt;https:&#x2F;&#x2F;jfgreffier.com&lt;&#x2F;a&gt; 找到我&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-to-quickly-get-realistic-data-with-faker-for-playwright-tests&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-11-06T13:30:29.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright真实项目里哪些断言使用频率比较高</title>
        <published>2023-11-03T00:00:00+00:00</published>
        <updated>2023-11-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/statistics-of-assertions-in-playwright-test-suite/"/>
        <id>https://playwright.itest.info/blog/statistics-of-assertions-in-playwright-test-suite/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/statistics-of-assertions-in-playwright-test-suite/">&lt;h2 id=&quot;guan-yu-ce-shi-xiang-mu-de-jie-shao&quot;&gt;关于测试项目的介绍:&lt;&#x2F;h2&gt;
&lt;p&gt;我的测试项目是一个现代的客户端-服务器应用程序。后端（API）单独测试。前端（基于 React）充满了业务逻辑，拥有多模式界面：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:1000&#x2F;1*qAP8yJixSvKffSdElE3bBQ.png&quot; alt=&quot;图片 3: UI 设计图&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;⬆️UI 设计图；&lt;a href=&quot;https:&#x2F;&#x2F;www.esa.int&#x2F;ESA_Multimedia&#x2F;Images&#x2F;2006&#x2F;12&#x2F;Location_of_buried_basins_detected_by_MARSIS&quot;&gt;背景图片来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;自动化测试覆盖了超过 80%的功能，是回归测试的主要组成部分。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwright-ce-shi-tao-jian-bao-gua&quot;&gt;Playwright 测试套件包括:&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;103 个测试文件；&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;955 个测试；&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;601&lt;&#x2F;strong&gt; 个 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-playwrightassertions#playwright-assertions-expect-generic&quot;&gt;expect&lt;&#x2F;a&gt; &lt;strong&gt;方法&lt;&#x2F;strong&gt;（在 97 个测试文件中）；&lt;&#x2F;li&gt;
&lt;li&gt;其中有 50 个负向检查（断言在匹配器之前有 &lt;code&gt;.not&lt;&#x2F;code&gt;）~ 8.5%的 &lt;code&gt;expect&lt;&#x2F;code&gt; 是负向的；&lt;&#x2F;li&gt;
&lt;li&gt;页面对象模型中没有断言，但有 &lt;strong&gt;185 个等待定位器的 &lt;code&gt;awaits&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;；&lt;&#x2F;li&gt;
&lt;li&gt;在 &lt;code&gt;beforeAll&lt;&#x2F;code&gt; 或 &lt;code&gt;afterAll&lt;&#x2F;code&gt; 钩子中没有断言（见&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@adequatica&#x2F;principles-of-writing-automated-tests-a2b72218264c#6da4&quot;&gt;自动化测试编写原则第 2 条&lt;&#x2F;a&gt;）；&lt;&#x2F;li&gt;
&lt;li&gt;所有断言都是可执行的（测试中没有 if 语句，见&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@adequatica&#x2F;principles-of-writing-automated-tests-a2b72218264c#8fbe&quot;&gt;自动化测试编写原则第 7 条&lt;&#x2F;a&gt;）；&lt;&#x2F;li&gt;
&lt;li&gt;没有&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions#soft-assertions&quot;&gt;软断言&lt;&#x2F;a&gt;；&lt;&#x2F;li&gt;
&lt;li&gt;所有测试在 CI 中以 4 个线程运行，耗时 14 分钟。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;我需要一些澄清：我在页面对象中使用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locator#locator-wait-for&quot;&gt;waitFor()&lt;&#x2F;a&gt; 方法等待定位器的特定状态，这&lt;em&gt;可以被视为一种断言&lt;&#x2F;em&gt;，因为如果定位器不满足条件，测试将失败。这就是为什么有些测试（测试文件）可能没有 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-playwrightassertions&quot;&gt;expect&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-playwrightassertions&quot;&gt;断言&lt;&#x2F;a&gt;，因为它们仅包含具有 &lt;code&gt;waitFor()&lt;&#x2F;code&gt; 的页面对象，如下所示：&lt;&#x2F;p&gt;
&lt;p&gt;页面对象示例：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;Dungeon {
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;gemstone&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Locator;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;gemstone &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.mineral_crystal&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt; }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForGem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;visibleState&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;boolean&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;gemstone&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitFor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt; }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;测试示例：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Should have a gem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dungeon &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;Dungeon(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dungeon&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForGem&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这就是为什么我将 &lt;code&gt;waitFor()&lt;&#x2F;code&gt; 添加到断言的 top 列表中，与&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-genericassertions&quot;&gt;通用断言&lt;&#x2F;a&gt;一起。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;top-duan-yan-lie-biao&quot;&gt;top 断言列表:&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;.toBe&lt;&#x2F;code&gt;— 328&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;waitFor()&lt;&#x2F;code&gt; — 185&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.toStrictEqual&lt;&#x2F;code&gt; — 67&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.toContain&lt;&#x2F;code&gt; — 54&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.toEqual&lt;&#x2F;code&gt; — 39&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.toHaveURL&lt;&#x2F;code&gt; — 34&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.toHaveAttribute&lt;&#x2F;code&gt; — 22&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.toBeChecked&lt;&#x2F;code&gt; — 15&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.toHaveClass&lt;&#x2F;code&gt; — 14&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.toBeGreaterThanOrEqual&lt;&#x2F;code&gt; — 10&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.toBeNull&lt;&#x2F;code&gt; — 6&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.toMatch&lt;&#x2F;code&gt; — 4&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.toBeTruthy&lt;&#x2F;code&gt; — 4&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.toBeGreaterThan&lt;&#x2F;code&gt; — 2&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.toBeFalsy&lt;&#x2F;code&gt; — 2&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:1000&#x2F;1*9rDEfHvlV90Q2hyajQsnDQ.png&quot; alt=&quot;图片 4: 断言顶部列表&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆top 断言列表: &lt;code&gt;expect&lt;&#x2F;code&gt; + &lt;code&gt;waitFor()&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;结果非常令人期待：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;60%以上&lt;&#x2F;strong&gt; — 检查数据是否有某个值；&lt;&#x2F;li&gt;
&lt;li&gt;大约四分之一 — 等待某些东西（定位器&#x2F;文本）；&lt;&#x2F;li&gt;
&lt;li&gt;剩下的 10–15% — URL&#x2F;标题匹配，罕见的 CSS 属性检查等。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这与我之前参与的项目非常相似。&lt;&#x2F;p&gt;
&lt;p&gt;阅读更多关于 Playwright 断言的信息：&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions&quot;&gt;断言&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;adequatica.medium.com&#x2F;statistics-of-assertions-in-playwright-test-suite-9e464866982d&quot;&gt;URL Source&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布日期: 2023-11-03&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何让Playwright 视觉回归测试稳定运行不出错</title>
        <published>2023-10-31T00:00:00+00:00</published>
        <updated>2023-10-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/fix-flaky-playwright-visual-regression-tests/"/>
        <id>https://playwright.itest.info/blog/fix-flaky-playwright-visual-regression-tests/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/fix-flaky-playwright-visual-regression-tests/">&lt;p&gt;我们在流水线中使用视觉回归测试，以确保我们即将发布的代码没有意外更改任何视觉内容。我们通过使用 Playwright 的截图功能捕获页面并上传到 &lt;a href=&quot;https:&#x2F;&#x2F;percy.io&#x2F;&quot;&gt;Percy&lt;&#x2F;a&gt; 来实现这一目标。我们选择 Percy 而不是 Playwright 内置的 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-snapshots&quot;&gt;截图比较方案&lt;&#x2F;a&gt;，因为我们喜欢一个简单直观的用户界面来查看差异。如果 主干代码 和我们提交的更改之间有差异，我们会得到一个这样的视图，如果满意的话可以进行审核和批准：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.houseful.blog&#x2F;gifs&#x2F;visual-test-percy-diff.gif&quot; alt=&quot;Image 1: Visual regression test in Percy&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这已被证明是我们测试工具中的一个非常有价值的工具。然而，视觉测试通常很难保持稳定。这是因为运行在客户端的代码往往并不简单——我们要应对异步触发的脚本、行为不一致的第三方脚本、加载状态、动画、模态框等各种复杂情况。这种复杂性可能导致不稳定和“容易失败”的测试运行结果。&lt;&#x2F;p&gt;
&lt;p&gt;为了让测试用例可以合理的应对我们的更改有，它们需要稳定。如果测试用例老是误报错误，那么开发人员可能会忽略它们，并盲目的把流程转到下一步，这将使它们在流水线中运行的目的失去意义。&lt;&#x2F;p&gt;
&lt;p&gt;在 &lt;a href=&quot;https:&#x2F;&#x2F;www.altosoftware.co.uk&#x2F;&quot;&gt;Alto&lt;&#x2F;a&gt;，我们目前捕获大约 200 张截图，并且这个列表会随着时间的推移而增长。虽然大多数截图是稳定的，但我们发现有一些是非常容易失败的。我们最近花时间解决这些测试问题，使它们稳定，并重建对我们视觉测试的信任。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mock-mock-mock-mo-ni-mo-ni-mo-ni&quot;&gt;mock, mock, mock(模拟、模拟、模拟)&lt;&#x2F;h2&gt;
&lt;p&gt;这是一个很好的起点——不要依赖数据库&#x2F;api 服务提供的数据进行视觉测试。你的应用程序需要在视觉上保持一致，才能使截图比较产生意义，所以我们需要一致的数据集。&lt;&#x2F;p&gt;
&lt;p&gt;我们使用 &lt;a href=&quot;https:&#x2F;&#x2F;mswjs.io&#x2F;&quot;&gt;Mock Service Worker&lt;&#x2F;a&gt; 来模拟：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;api 响应&lt;&#x2F;li&gt;
&lt;li&gt;用户配置文件&lt;&#x2F;li&gt;
&lt;li&gt;feature toggle(功能的开关)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;我们选择 Mock Service Worker 是因为我们希望在整个技术栈中使用相同的 mock 技术，不过 Playwright 也有内置的 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;mock&quot;&gt;API mock&lt;&#x2F;a&gt;，也可以用于同样的目的。&lt;&#x2F;p&gt;
&lt;p&gt;除了数据模拟，考虑你依赖的浏览器功能。例如，对于日期，我们使用 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright&#x2F;issues&#x2F;6347#issuecomment-1085850728&quot;&gt;这个工具&lt;&#x2F;a&gt; 来模拟它。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;li-yong-deng-dai&quot;&gt;利用等待&lt;&#x2F;h2&gt;
&lt;p&gt;在进行截图之前，请使用 Playwright 的 &lt;code&gt;waitFor&lt;&#x2F;code&gt;。&lt;code&gt;waitFor&lt;&#x2F;code&gt; 的默认状态是 &lt;code&gt;visible&lt;&#x2F;code&gt;，所以在这个例子中，我们知道标题在进行截图之前是可见的：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;heading&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;My Heading&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitFor&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;screenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;myapp&#x2F;foo-bar.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  fullPage: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果在拍摄截图之前需要运行某些异步操作，你可以等待其视觉状态的变化。在下面的例子中，我们希望确保加载的转菊花从视图中隐藏，并且我们的图像在截图之前是可见的。这确保页面在捕获之前处于正确的状态：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 等待菊花隐藏
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;loading-spinner&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitFor&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  state: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;hidden&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 等待图像可见
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByAltText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;My Image&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitFor&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;screenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;myapp&#x2F;foo-bar.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  fullPage: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;bu-huo-ni-xu-yao-de-nei-rong&quot;&gt;捕获你需要的内容&lt;&#x2F;h2&gt;
&lt;p&gt;我们注意到在较小的设备尺寸上，当内容显示在模态框中时会出现不稳定情况。它会在不同的地方打开。以下面的例子为例：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.houseful.blog&#x2F;img&#x2F;reeVg1wBML-383.jpeg&quot; alt=&quot;Image 2: A modal appearing over a page&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这会引起用例错误导致误报，因为模态框中的所有元素都处于稍微不同的位置。我们问自己，我们在这里试图测试什么？我们已经在点击模态框之前捕获了页面，所以有价值的是模态框的内容。我们的方法是只截图模态框：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.houseful.blog&#x2F;img&#x2F;zWNN3UMh8s-312.jpeg&quot; alt=&quot;Image 3: The contents of a modal&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;My element&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;screenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;myapp&#x2F;foo-bar.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这种方法一直很稳定，并且仍然捕获了足够的信息，使我们对频繁的代码修改保持有信心，因为我们的测试用例是可靠的。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yin-cang-zu-zhi-di-san-fang&quot;&gt;隐藏&#x2F;阻止第三方&lt;&#x2F;h2&gt;
&lt;p&gt;第三方脚本可能是不可预测且难以测试的。它们在不同的时间点触发，延迟页面加载和交互。回到我们的“捕获你需要的内容”的方法——如果我们有一个嵌入的第三方地图，它在一个 &lt;code&gt;iframe&lt;&#x2F;code&gt; 中加载——我们不能信心满满的等待其内容加载，因为第三方代码运行在自己的 document 元素中，并且也容易受到选择器、样式、功能等任何变化的影响。这也不是我们的应用程序代码。在这种情况下，为了让我们的测试更加稳定，我们可以选择屏蔽它：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;screenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;myapp&#x2F;foo-bar.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  fullPage: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  mask: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#map&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.houseful.blog&#x2F;img&#x2F;cZxyNokaS--414.jpeg&quot; alt=&quot;Image 4: A modal appearing over a page&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我们仍然可以看到它占据的空间，但我们不会截图这部分的具体内容。&lt;&#x2F;p&gt;
&lt;p&gt;另一种方法是阻止第三方脚本的触发。如果你有 google 或者百度分析、google tag 等类似的异步脚本——任何不属于视觉测试的东西，直接屏蔽掉 👍！&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在访问页面之前阻止谷歌地图
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;googletagmanager&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;abort&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;yourapplication.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;jin-yong-dong-hua&quot;&gt;禁用动画&lt;&#x2F;h2&gt;
&lt;p&gt;动画如过渡可能是不可预测的测试对象，幸运的是我们可以传递 &lt;code&gt;animations: &#x27;disabled&#x27;&lt;&#x2F;code&gt; 给我们的截图，“有限的动画会快速前进到完成状态，因此它们会触发 transitionend 事件。无限动画会取消到初始状态，然后在截图后重新播放。”（&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-elementhandle#element-handle-screenshot-option-animations&quot;&gt;来源&lt;&#x2F;a&gt;）。很简单！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;chrome-quan-ye-mian-jie-tu-cuo-wu&quot;&gt;Chrome 全页面截图错误&lt;&#x2F;h2&gt;
&lt;p&gt;我们注意到在某些全页面截图中出现了一些奇怪的行为，图像会被裁剪：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.houseful.blog&#x2F;img&#x2F;ObdrN_-RVl-365.jpeg&quot; alt=&quot;Image 5: A modal appearing over a page&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我们得出结论，这很可能是由于 &lt;a href=&quot;https:&#x2F;&#x2F;bugs.chromium.org&#x2F;p&#x2F;chromium&#x2F;issues&#x2F;detail?id=760596&quot;&gt;这个 Chromium 的 bug&lt;&#x2F;a&gt;，可惜的是它被标记为“不会修复”。我们采用了在截图之前滚动页面长度的方法（如&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright&#x2F;issues&#x2F;620#issuecomment-578022596&quot;&gt;这里&lt;&#x2F;a&gt; 所建议）来处理截图裁剪问题：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 工具方法
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;scrollFullPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await new &lt;&#x2F;span&gt;&lt;span&gt;Promise(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;totalHeight &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;distance &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;timer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setInterval&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;scrollHeight &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;document.body.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;scrollHeight&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        window.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;scrollBy&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;distance&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;totalHeight &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;distance&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;totalHeight &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;scrollHeight&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;clearInterval&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;timer&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;      }, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 首先滚动页面
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;scrollFullpage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 然后截图
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;screenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;myapp&#x2F;foo-bar.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  fullPage: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这种方法虽然感觉仍然没有必要，但已经产生了一致的结果。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jian-li-wen-ding-xing-wen-hua&quot;&gt;建立稳定性文化&lt;&#x2F;h2&gt;
&lt;p&gt;一旦我们对测试的稳定性感到满意，我们需要保持这种状态，所以我们实施了零易碎政策。这意味着，如果发现一个容易失败的视觉测试，它需要立即修复或用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-test#test-fixme-1&quot;&gt;fixme&lt;&#x2F;a&gt; 标记，直到工程师可以去修复它。这帮助我们维护了一套稳定的测试用例，并确保我们的工程师始终信任回归测试的结果。我们采用这种策略，因为对我们来说，不测试反而比天天运行容易失败的用例要好，测试用例不稳定，还不如不运行。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-lun&quot;&gt;结论&lt;&#x2F;h2&gt;
&lt;p&gt;这些只是我们保持视觉测试稳定运行的几个小方法。视觉回归很难做到绝对正确，有很多异常情况，并且很容易变得不稳定。然而，一旦你的测试套件表现的很好，你的开发就有信心对代码进行足够多的修改，并准确的知道用户最终会看到什么。 这就是我们拥有自动化测试的整个目的。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.houseful.blog&#x2F;posts&#x2F;2023&#x2F;fix-flaky-playwright-visual-regression-tests&#x2F;&quot;&gt;URL 来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Hex是什么鬼, 我该怎么用playwright去检查控件的颜色</title>
        <published>2023-10-30T00:00:00+00:00</published>
        <updated>2023-10-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/what-the-hex-or-how-i-check-colors-with-playwright/"/>
        <id>https://playwright.itest.info/blog/what-the-hex-or-how-i-check-colors-with-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/what-the-hex-or-how-i-check-colors-with-playwright/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;10&#x2F;2023-10-29_11-28-26-1.jpg&quot; alt=&quot;图片2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;别担心，这不是一个你必须在红色和蓝色之间选择的《黑客帝国》场景。我是一个视觉型的人，用图表、图片或者更好的面部表情来解释事情对我来说更容易。由于互联网还没有准备好展示我的面部表情，让我们用图片来说明。&lt;&#x2F;p&gt;
&lt;p&gt;在运行我的登录检查时，Playwright 做得非常好。它使用我们的数据测试 ID 找到“登录”按钮并点击它。然而，我正在开发的应用程序允许终端用户自定义颜色，这意味着管理员可以更改按钮和背景的颜色。这不是我每次发布都想测试的事情，所以我必须测试一下 Playwright 的能力。我能否用 Playwright 轻松检查按钮的颜色？还是最终不得不使用 JavaScript 去搞定？&lt;&#x2F;p&gt;
&lt;p&gt;事实证明，您可以检查按钮的颜色。只需使用 &lt;code&gt;.toHaveCSS()&lt;&#x2F;code&gt; &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locatorassertions#locator-assertions-to-have-css&quot;&gt;(官方文档链接)&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;当然，我不会只给你上面的链接然后祝你好运。让我们一起为按钮写一个断言。&lt;&#x2F;p&gt;
&lt;p&gt;一切都从检查你想要检查颜色的元素开始。在我们的例子中，这是“登录”按钮，这是它在开发者工具中的样子：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;10&#x2F;2023-10-29_12-01-45.jpg&quot; alt=&quot;图片3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;当您点击元素时，会发现很多 CSS 属性。这里有一个有用的技巧：使用右侧样式标签中的搜索框并输入“background”或“color”以过滤出您需要的特定属性。&lt;&#x2F;p&gt;
&lt;p&gt;如您所见，在此示例中，背景色不仅是蓝色，而是“#031df4”。这是该特定颜色的&lt;strong&gt;十六进制&lt;&#x2F;strong&gt;代码。&lt;br &#x2F;&gt;
&lt;em&gt;❤️ 如果你现在有点迷茫，你可以简单地搜索“十六进制颜色代码解释”，然后回来更好地理解。&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;根据文档，我们有我们需要的一切：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;locator（登录按钮）&lt;&#x2F;li&gt;
&lt;li&gt;CSS 属性名称（&lt;code&gt;background-color&lt;&#x2F;code&gt;）&lt;&#x2F;li&gt;
&lt;li&gt;CSS 属性值（&lt;code&gt;#031df4&lt;&#x2F;code&gt;）。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;让我们编写检查并运行它：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;10&#x2F;2023-10-29_13-29-33.jpg&quot; alt=&quot;图片4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;将十六进制颜色代码传递给 &lt;code&gt;.toHaveCSS()&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;它失败了，因为 &lt;code&gt;.toHaveCSS()&lt;&#x2F;code&gt; 期望你传递特定格式的 RGB 颜色作为&lt;code&gt;background-color&lt;&#x2F;code&gt;。&lt;br &#x2F;&gt;
&lt;em&gt;❤️ 如果你现在有点迷茫，你可以简单地搜索“RGB 颜色代码解释”，并且也许“RGB vs hex”以更好地理解。&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;你需要将十六进制颜色代码转换为 RGB 颜色代码。快速搜索搜索后，我找到了一个不需要使用库的简单解决方案：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;convertHexToRGB&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;hex&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果输入中包含 &amp;#39;#&amp;#39;，则将其删除
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;hex &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;hex&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;replace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;^&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 将十六进制值解析为单独的 R、G 和 B 值
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;red &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parseInt&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;hex&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;substring&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;green &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parseInt&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;hex&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;substring&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;blue &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parseInt&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;hex&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;substring&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 以对象形式返回 RGB 值
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    red: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;red&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    green: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;green&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    blue: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;blue&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;将十六进制转换为 RGB&lt;&#x2F;p&gt;
&lt;p&gt;我将其保存到我的 &lt;code&gt;.&#x2F;lib&lt;&#x2F;code&gt; 文件夹中，然后导入到规范文件中：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;10&#x2F;2023-10-29_13-25-50.jpg&quot; alt=&quot;图片5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;将十六进制转换为 RGB -&amp;gt; console.log(RGB 颜色) -&amp;gt; 在 &lt;code&gt;.toHaveCSS()&lt;&#x2F;code&gt; 检查中使用 RGB 颜色&lt;&#x2F;p&gt;
&lt;p&gt;测试通过了！现在，我有一个检查，以确保按钮的颜色正是我期望的。我还添加了对禁用按钮的检查。这样，如果禁用按钮的代码发生变化，我将是第一个知道的人。（注意：在我的应用程序中，禁用按钮没有使用&lt;code&gt;background-color&lt;&#x2F;code&gt;属性，而是使用&lt;code&gt;color&lt;&#x2F;code&gt;属性，因此始终要勤勉并首先手动检查属性。）&lt;&#x2F;p&gt;
&lt;p&gt;如果您在一个规范文件中有多个 &lt;code&gt;.toHaveCSS()&lt;&#x2F;code&gt; 断言，最好将它们抽象到您的“.&#x2F;lib”文件夹（或您保留固定装置的任何地方）中，以提高代码的可读性。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;export async function checkColor(element, cssProperty, rgbColors) {
&lt;&#x2F;span&gt;&lt;span&gt;  await expect(element).toHaveCSS(cssProperty, `rgb(${rgbColors.red}, ${rgbColors.green}, ${rgbColors.blue})`);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;将断言移到 .&#x2F;lib 文件夹中&lt;&#x2F;p&gt;
&lt;p&gt;最后，这是一个颜色检查如何在使用页面对象模式、抽象的 &lt;code&gt;convertHexToRGB()&lt;&#x2F;code&gt; 和 &lt;code&gt;checkColor()&lt;&#x2F;code&gt; 的规范文件中显示的示例：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;10&#x2F;2023-10-29_13-37-29.jpg&quot; alt=&quot;图片6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;10&#x2F;checkingcolor.png&quot; alt=&quot;图片7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;来源链接&lt;&#x2F;strong&gt;: &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;what-the-hex-or-how-i-check-colors-with-playwright&#x2F;&quot;&gt;What the Hex, or How I Check Colors With Playwright&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;发布时间&lt;&#x2F;strong&gt;: 2023-10-30&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>playwright如何修复 apiRequestContext.fetch报错:context disposed</title>
        <published>2023-10-23T00:00:00+00:00</published>
        <updated>2023-10-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-to-fix-apirequestcontext-fetch-request-context-disposed/"/>
        <id>https://playwright.itest.info/blog/how-to-fix-apirequestcontext-fetch-request-context-disposed/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-to-fix-apirequestcontext-fetch-request-context-disposed/">&lt;p&gt;在工作中进行自动化测试时,我发现在使用 &lt;code&gt;await page.route()&lt;&#x2F;code&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-route&quot;&gt;文档&lt;&#x2F;a&gt;)功能时,测试中经常出现以下两个错误:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;错误: apiRequestContext.fetch: Request context disposed
&lt;&#x2F;span&gt;&lt;span&gt;错误: apiRequestContext.fetch: Browser has been closed
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;还送了个错误
&lt;&#x2F;span&gt;&lt;span&gt;1 error was not a part of any test, see above for details
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这种情况通常发生在页面中有一个或一组网络请求,而测试在 API 请求完成前就结束了。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fu-xian-wen-ti&quot;&gt;复现问题&lt;&#x2F;h2&gt;
&lt;p&gt;我有一个例子,大约每 10 次运行就能复现一次。如果你想看到失败情况,可以下载代码库并运行以下命令:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;npx playwright test tests&#x2F;checkout&#x2F;checkoutWithRoute.spec.ts --repeat-each 20&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-practicesoftwaretesting.com&quot;&gt;代码在这里&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这个测试的代码如下。请注意,我没有添加任何断言,因为我用这个例子来验证一个可以解决上述错误的方案。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; checkoutWithRoute.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;HomePage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@pages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;UI与API路由&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;检查已释放的api上下文&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.practicesoftwaretesting.com&#x2F;products?**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;continue&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;HomePage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;测试报告中错误的示例 👇&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;10&#x2F;image-2.png&quot; alt=&quot;Image 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这种情况发生的原因是,即使测试已经运行完毕,网络请求仍未完成。一般情况下这种错误并不会发生,但如果你正在路由多个请求,或者应用程序正在获取大量数据,这种情况就非常有可能发生。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fang-zhi-huo-xiu-fu-zhe-xie-cuo-wu-de-ji-qiao&quot;&gt;防止或修复这些错误的技巧&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;jian-cha-ce-shi-wen-jian-zhong-de-await-yu-ju&quot;&gt;检查测试文件中的 await 语句&lt;&#x2F;h3&gt;
&lt;p&gt;首先,确保你已正确地&quot;await&quot;了所有浏览器或网络交互,例如:&lt;code&gt;await homePage.goto()&lt;&#x2F;code&gt;。如果你没有正确地等待这些操作,可能会在测试结束时出现这些错误,或者在不同的地方出现错误,这取决于哪些操作仍在进行。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-yong-waitforresponse&quot;&gt;使用 waitForResponse()&lt;&#x2F;h3&gt;
&lt;p&gt;接下来,在测试结束前使用&lt;code&gt;await page.waitForResponse()&lt;&#x2F;code&gt;(&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page#page-wait-for-response&quot;&gt;文档&lt;&#x2F;a&gt;)函数,以确保所有网络流量都已响应。看下面的例子,现在这个测试永远不会因为网络连接仍然打开而失败。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; checkoutWithRoute.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;HomePage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@pages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;UI与API路由&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;检查已释放的api上下文&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.practicesoftwaretesting.com&#x2F;products?**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;continue&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;HomePage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForResponse&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.practicesoftwaretesting.com&#x2F;products?**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;shi-lu-you-geng-jia-ju-ti&quot;&gt;使路由更加具体&lt;&#x2F;h3&gt;
&lt;p&gt;在我工作的代码库中,我们有一个报告页面,它获取所有支付信息,最终会路由超过 10 个匹配以下模式的 API 调用:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;await page.route(&quot;**&#x2F;payment-intents?**&quot;, ...&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;一个解决方案是使我的路由更加详细,不使用正则表达式匹配,比如这样。对于我测试中的断言,我实际上只需要加载前 25 个请求。限制通过 page.route()路由的请求数量应该可以帮助解决这类问题。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;await page.route(&quot;**&#x2F;payment-intents?offset=0&amp;amp;limit=25&quot;, ...&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h2&gt;
&lt;p&gt;总的来说,这些错误可能很难排查和解决,主要是因为我们测试的系统的网络时序问题。如果你有其他处理这些问题的技巧或窍门,请在 LinkedIn 上联系我,告诉我!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读!如果你觉得这篇文章有帮助,请在&lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt;上联系我,或者考虑&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;给我买杯咖啡&lt;&#x2F;a&gt;。如果你想在收件箱里收到更多内容,请在下方订阅,别忘了留下 ❤️ 表示支持。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-to-fix-apirequestcontext-fetch-request-context-disposed&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-10-23T12:30:01.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>playwright的各种规范</title>
        <published>2023-10-20T00:00:00+00:00</published>
        <updated>2023-10-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-standards/"/>
        <id>https://playwright.itest.info/blog/playwright-standards/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-standards/">&lt;p&gt;Houseful 业务中的多个团队使用 Playwright 进行前端测试自动化。在构建我们的 Playwright 框架时,我们的目标是让代码易于阅读、维护和调试。为了帮助我们实现这一目标,我们作为一个团队共同努力,在使用 Playwright 的各个代码库中创建了统一的测试规范。&lt;&#x2F;p&gt;
&lt;p&gt;在各个代码库中统一测试标准为我们在可读性、可用性和准入门槛方面提供了好处。例如:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;提高可重用性 - 函数 &#x2F; 定位器 &#x2F; 共享步骤 &#x2F; 其他测试代码可以轻松重用。对于在测试中工作的人来说,找到他们需要的函数和元素很简单。这减少了重复,并降低了代码审查开始后需要重新修改的可能性。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;简化 review - 代码可以更快地被 review。它减少了审查代码的心理负担,因为你可以轻松弄清楚测试代码在做什么。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;加快入职 - 命名约定帮助新人快速入职并感到舒适地为代码库做贡献。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;以下是我们在 Houseful 创建 Playwright 测试时遵循的测试规范和指南。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwright-zhi-nan&quot;&gt;Playwright 指南&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;duan-dao-duan-ce-shi-zhong-de-shu-ju-chuang-jian&quot;&gt;端到端测试中的数据创建&lt;&#x2F;h3&gt;
&lt;p&gt;运行端到端(e2e)测试成本很高。它们在资源方面成本高昂,可能需要大量精力来设置,并且对流程中任何地方的微小变化敏感,这可能使它们变得脆弱(容易报错)且难以长期维护。在选择这条路之前,请考虑其他选项。&lt;&#x2F;p&gt;
&lt;p&gt;如果你必须进行 e2e 测试:&lt;&#x2F;p&gt;
&lt;p&gt;好的做法 👍&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;每个测试都有自己的数据创建。即它创建完成检查所需的所有数据&lt;&#x2F;li&gt;
&lt;li&gt;每个测试都有一个后置步骤,即清理数据&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;不好的做法 ⚔️&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;依赖现有数据来执行测试&lt;&#x2F;li&gt;
&lt;li&gt;在测试后留下未清理的状态&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;ye-mian-dui-xiang-mo-xing-pom&quot;&gt;页面对象模型 (POM)&lt;&#x2F;h3&gt;
&lt;p&gt;每个页面都应该有一个相应的 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;pom&quot;&gt;POM 文件&lt;&#x2F;a&gt; 来帮助我们测试的可维护性和可扩展性。POM 文件应包含和给出 POM 相关的所有选择器和函数。&lt;&#x2F;p&gt;
&lt;p&gt;所有交互都应通过页面对象完成,即测试用例中不应包含选择器。&lt;&#x2F;p&gt;
&lt;p&gt;所有断言都应在你的测试中完成,即 POM 中不应包含断言。(&lt;strong&gt;乙醇的评论:👀 这点见仁见智，很多时候 pom 中可能会出现一些保护性的等待断言，比如等待某个元素出现，如果不出现整个测试就失败了&lt;&#x2F;strong&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;你可以在我们关于 &lt;a href=&quot;https:&#x2F;&#x2F;zoopla.blog&#x2F;posts&#x2F;2023&#x2F;test-framework-migration&#x2F;&quot;&gt;软件测试设计模式&lt;&#x2F;a&gt; 的博客中阅读更多关于我们如何利用 POM 的内容&lt;&#x2F;p&gt;
&lt;p&gt;好的做法 👍&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; POM 文件 &amp;#39;.&#x2F;pom&#x2F;foo&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 添加与页面相关的所有定位器和函数。
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 允许所有测试重用
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Locator&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;FooPage {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pageTitle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Locator;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;buttonFoo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Locator;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pageTitle &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=My Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;buttonFoo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=Foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;foo.spec.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 调用 POM 以使用定位器和函数
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 执行测试所需的所有断言
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;FooPage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;pom&#x2F;foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;FooPage;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;显示 foo bar&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;FooPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bar 是可见的&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;buttonFoo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;titlePage&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;不好的做法 ⚔️&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;foo.spec.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 直接在测试中包含定位器
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Locator&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;显示 foo bar&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bar 是可见的&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=Foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=My Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;ce-shi-jie-gou-arrange-act-assert&quot;&gt;测试结构 - Arrange, Act, Assert&lt;&#x2F;h3&gt;
&lt;p&gt;在构建测试时遵循 AAA (Arrange, Act, Assert) 模式。在大多数情况下,Arrange 步骤可以包含在 Before 块中。&lt;&#x2F;p&gt;
&lt;p&gt;考虑添加注释以提高可读性。&lt;&#x2F;p&gt;
&lt;p&gt;好的做法 👍&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 安排,创建一个 let 属性
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createProperty&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 执行,提出一个收费
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;raiseCharge&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 断言,确认收费已提出
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;charge&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;ToBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;raised&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;linter&quot;&gt;Linter&lt;&#x2F;h3&gt;
&lt;p&gt;安装并使用 linting 规则。我们使用 &lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;eslint-plugin-playwright&quot;&gt;eslint-playwright-plugin&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;推荐的配置将有助于执行本博文中描述的一些指南。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; install&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -D&lt;&#x2F;span&gt;&lt;span&gt; eslint-plugin-playwright
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;bi-mian-tiao-jian-yu-ju&quot;&gt;避免条件语句&lt;&#x2F;h3&gt;
&lt;p&gt;避免在测试文件中使用条件逻辑。测试应该是确定性的,这意味着我们应该目标是拥有明确预期结果的测试。带有条件语句的测试可能难以维护和阅读。此外,除非你绝对确定应用程序的状态在进行断言时已经稳定,否则这类测试可能会不稳定。&lt;&#x2F;p&gt;
&lt;p&gt;有条件语句可能是一个测试用例做了太多事情的迹象,可以被拆分。&lt;&#x2F;p&gt;
&lt;p&gt;不好的做法 ⚔️&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;FooPage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;pom&#x2F;foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;FooPage;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;条件测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;FooPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bar 是可见的&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isButtonVisible &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;buttonFoo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isButtonVisible&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果 buttonFoo 可见,点击它使 pageTitle 可见
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;buttonFoo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 然后检查 pageTitle 是否可见
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pageTitle&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 否则只检查 pageTitle 是否可见
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pageTitle&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;好的做法 👍&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 为每个场景有单独的规范。
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 包含需要达到该状态的设置步骤
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; fooPageVisible.spec.js
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bar 最初是可见的&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 设置你的测试,使 bar 最初可见
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 断言 bar 是可见的
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pageTitle&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; fooBarButton.spec.js
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;FooPage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;pom&#x2F;foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;FooPage;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;点击 Foo 按钮后 bar 是可见的&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 设置你的测试,使按钮最初可见
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 执行按钮操作
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;buttonFoo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 断言 bar 是可见的
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pageTitle&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;deng-dai&quot;&gt;等待&lt;&#x2F;h3&gt;
&lt;p&gt;不要使用任何任意的等待。这可能导致不稳定的测试,因为你很少能确定等待时间是否足够。它还可能不必要地增加测试运行时间。相反,尝试:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;使用 Playwright 的 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page#page-event-dom-content-loaded&quot;&gt;waitUntil: &#x27;domcontentloaded&#x27;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;等待特定的网络请求解析&lt;&#x2F;li&gt;
&lt;li&gt;等待页面状态稳定,例如元素在页面上可见&#x2F;不可见&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;不好的做法 ⚔️&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5000&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;好的做法 👍&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在等待帮助文件中定义
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; wait-helpers.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForAPIResponse &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= async &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;statusCode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number
&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForResponse&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;res&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;res&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;res&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;statusCode
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在你的测试文件中使用
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;tests&#x2F;foo.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Locator&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;waitForAPIResponse &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;helpers&#x2F;wait-helpers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;显示 foo bar&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bar 是可见的&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;buttonFoo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForNewAPIResponse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;Accounting&#x2F;GetRaisedCharges&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;titlePage&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;好的做法 👍&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;tests&#x2F;foo.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Locator&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;waitForAPIResponse &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;helpers&#x2F;wait-helpers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;显示 foo bar&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bar 是可见的&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooBarURL&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      waitUntil: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;domcontentloaded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;好的做法 👍&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;tests&#x2F;foo.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Locator&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;waitForAPIResponse &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;helpers&#x2F;wait-helpers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;导航到 foo bar&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;页面已加载&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;buttonFoo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;titlePage&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;xuan-ze-qi&quot;&gt;选择器&lt;&#x2F;h3&gt;
&lt;p&gt;避免使用与实现和页面结构绑定的选择器。&lt;&#x2F;p&gt;
&lt;p&gt;相反,我们根据 &lt;a href=&quot;https:&#x2F;&#x2F;testing-library.com&#x2F;docs&#x2F;queries&#x2F;about&#x2F;#priority&quot;&gt;testing-library 指导原则&lt;&#x2F;a&gt; 优先考虑以下内容&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;getByRole (这有助于可访问性,反映用户和辅助技术如何感知页面)&lt;&#x2F;li&gt;
&lt;li&gt;getByText&lt;&#x2F;li&gt;
&lt;li&gt;getByTestId (需要时添加)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;不好的做法 ⚔️&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.opt-u &amp;gt; div &amp;gt; .summary &amp;gt; div:nth-child(4) &amp;gt; div&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;好的做法 👍&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#foo-button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;OK&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;biao-qian&quot;&gt;标签&lt;&#x2F;h3&gt;
&lt;p&gt;在 Playwright 中利用标签(tags)来对测试进行分组并进行有针对性的运行。标签测试的一些方法包括:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;按测试类型 (例如 功能性、视觉,...)&lt;&#x2F;li&gt;
&lt;li&gt;按测试在流水线中运行的位置 (发布、回归,...)&lt;&#x2F;li&gt;
&lt;li&gt;按功能 (例如 日历、登录, …)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;你可以在我们关于 &lt;a href=&quot;https:&#x2F;&#x2F;zoopla.blog&#x2F;posts&#x2F;2022&#x2F;playwright-tag-annotations&#x2F;&quot;&gt;标签注释&lt;&#x2F;a&gt; 的博文中读到更多关于我们如何使用标签的内容。&lt;&#x2F;p&gt;
&lt;p&gt;好的做法 - 按测试类型 👍&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;显示 foo bar&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 设置在每种测试类型之前运行的步骤
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;foo&#x2F;bar&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;buttonFoo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;titlePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitFor&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 检查可访问性
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@accessibility&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;injectAxe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;checkA11y&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;undefined&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;a11yOpts&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 对页面运行断言
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@functional @smoke&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;buttonFoo&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;titlePage&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 桌面的视觉快照
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@visual desktop&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;captureScreenshot &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;captureScreenshot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;foo-desktop.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 移动设备的视觉快照
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;testMobile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@visual mobile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;captureScreenshot &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;captureScreenshot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;foo-mobile.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;乙醇的注释：👆 上面在测试标题里打标签的方式已经过时了，现在 playwright 提供了一种新的做法，更加的工程化和可视化一些，具体看&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-annotations#tag-tests&quot;&gt;这里&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;好的做法 - 按页面 &#x2F; 功能 &#x2F; 测试运行位置 👍&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;tests&#x2F;foo.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@foobar @smoke 导航到 foo bar&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;页面已加载&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;buttonFoo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;titlePage&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;bu-wen-ding-de-ce-shi&quot;&gt;不稳定的测试&lt;&#x2F;h3&gt;
&lt;p&gt;应优先解决不稳定的测试。如果你当时无法解决它,用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-test#test-fixme-1&quot;&gt;.fixme&lt;&#x2F;a&gt; 标签它们。这将跳过该测试。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;tests&#x2F;foo.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;显示 foo bar&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fixme&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bar 是可见的&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;buttonFoo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fooPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;titlePage&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;bing-xing-hua-he-ke-zhong-fu-xing&quot;&gt;并行化和可重复性&lt;&#x2F;h3&gt;
&lt;p&gt;构建测试以便在不干预的情况下重复运行。并与套件中的其他测试并行运行,即不干扰其他测试。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 通过启动同时运行的多个 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-parallel#worker-processes&quot;&gt;workders&lt;&#x2F;a&gt; 来实现开箱即用的并行测试。Playwright 可以根据可用资源扩展工作进程的数量。通过使用 &lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;actions&#x2F;using-github-hosted-runners&#x2F;about-larger-runners&quot;&gt;大型 github runners&lt;&#x2F;a&gt;,我们可以在 CI 流水线中并行运行更多测试。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ming-ming-yue-ding&quot;&gt;命名约定&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;bian-liang&quot;&gt;变量&lt;&#x2F;h4&gt;
&lt;p&gt;使用 &lt;em&gt;&lt;strong&gt;驼峰命名法&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; 声明。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bu-er-zhi&quot;&gt;布尔值&lt;&#x2F;h4&gt;
&lt;p&gt;以 &#x27;is&#x27;, &#x27;has&#x27;, &#x27;are&#x27;, &#x27;have&#x27; 开头。这有助于在浏览代码时识别这是一个布尔值。仍然使用 &lt;em&gt;&lt;strong&gt;驼峰命名法&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; 声明。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isTurnedOn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;ye-mian-dui-xiang-lei&quot;&gt;页面对象 &#x2F; 类&lt;&#x2F;h4&gt;
&lt;p&gt;使用 &lt;em&gt;&lt;strong&gt;帕斯卡命名法&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; 声明。&lt;&#x2F;p&gt;
&lt;p&gt;使用描述性命名,这可以帮助读者快速识别这是涵盖哪个页面或页面组件。根据需要使用你的产品中尽可能多的上下文来使名称有意义。&lt;&#x2F;p&gt;
&lt;p&gt;好的做法 👍&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;AddWorksOrderModal
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;不好的做法 ⚔️&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;newModal
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;ding-wei-qi&quot;&gt;定位器&lt;&#x2F;h4&gt;
&lt;p&gt;使用描述性命名,这可以帮助读者快速识别定位器所针对的元素。&lt;&#x2F;p&gt;
&lt;p&gt;例如,你可以使用包含 &lt;em&gt;&lt;strong&gt;&quot;动作 &#x2F; 元素名称&quot; + &quot;元素类型&quot;&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; 的命名结构。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;定义元素类型&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; - 这些是你的基本 HTML 元素类型,它们将在设计系统中定义和命名,或者作为一个团队,你可以在元素的一致命名上达成一致。例如:checkbox, tickbox, button, tooltip&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;定义动作 &#x2F; 名称&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; 考虑与此元素交互时将执行什么动作。或者元素的任何现有名称&#x2F;文本&lt;&#x2F;p&gt;
&lt;p&gt;好的做法 👍&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;这个元素是一个保存按钮,位于属性的上下文中
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span&gt;savePropertyButton: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Locator&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;好的做法 👍&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;这是一个报告日期的字段
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span&gt;reportedDateField: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Locator&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;han-shu-ming&quot;&gt;函数名&lt;&#x2F;h3&gt;
&lt;p&gt;函数名总是以 &lt;em&gt;&lt;strong&gt;&quot;动词&quot;&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; 开头,后跟函数正在交互的 &lt;em&gt;&lt;strong&gt;&quot;组件上下文&quot;&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt;,即它对哪个实体产生影响。&lt;&#x2F;p&gt;
&lt;p&gt;好的做法 👍&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getWorksOrder&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;printTransactions&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;deleteProperty&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.houseful.blog&#x2F;posts&#x2F;2023&#x2F;playwright-standards&#x2F;&quot;&gt;URL 来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright API 自动化测试权威指南:第14部分 - 通过扩展 Expect 创建自定义断言</title>
        <published>2023-10-16T00:00:00+00:00</published>
        <updated>2023-10-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-14-custom-assertions-extending-expect/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-14-custom-assertions-extending-expect/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-14-custom-assertions-extending-expect/">&lt;p&gt;本周我将回顾并重构一些测试,为我的代码库添加自定义断言。这是我早期考虑实现的功能,但当时为了保持简单而决定不做。随着 1.39 版本的发布,Playwright 团队推出了一些更简单的方法,通过一个固定文件来扩展你的测试用例和 expect 断言。详见下面的发布说明。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;release-notes#version-139&quot;&gt;发布说明 | Playwright 版本 1.39 !&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果你是第一次加入我们,可以查看&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;介绍文章&lt;&#x2F;a&gt;和 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&quot;&gt;playwright-api-test-demo 代码库&lt;&#x2F;a&gt;,其中包含了所有代码示例。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wei-shen-me-ni-ying-gai-guan-zhu-zhe-ge&quot;&gt;为什么你应该关注这个?&lt;&#x2F;h3&gt;
&lt;p&gt;最如果你现有的方法对你来说已经足够好,那么你不必在项目中实现自定义断言。但是如果你发现自己一遍又一遍地编写相同的冗长断言,那么自定义 expect 可能会派上用场。&lt;&#x2F;p&gt;
&lt;p&gt;例如,下面的断言可以转换为更简单、更易读的形式。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 我正在对报告数组中的每个预订进行断言
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;report&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;forEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;旧的方式
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.start)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;end&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.title).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 新的方式
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.start).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeValidDate&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;end&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeValidDate&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.title).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeString&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;随着最新的 1.39 版本发布,通过 fixture&lt;code&gt;扩展&lt;&#x2F;code&gt;expect 的能力以及&lt;code&gt;mergeExpects&lt;&#x2F;code&gt;和&lt;code&gt;mergeTests&lt;&#x2F;code&gt;的能力,这简化了在所有测试中导入 fixture 的过程! 在此之前,自定义断言是添加到&lt;code&gt;playwrightconfig.ts&lt;&#x2F;code&gt;文件中的。这可以在下面的文章中看到。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;blog&#x2F;creating-custom-expects-in-playwright-how-to-write-your-own-assertions&quot;&gt;在 Playwright 中创建自定义 expects:如何编写你自己的断言&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-xian-zi-ding-yi-expects&quot;&gt;实现自定义 Expects&lt;&#x2F;h3&gt;
&lt;p&gt;那么让我们开始通过 fixture 实现一些自定义 expects。我们将从&lt;code&gt;toBeValidDate()&lt;&#x2F;code&gt;开始。在之前的例子中,我们创建了一个辅助函数,我们调用它并验证如果日期可解析就返回&lt;code&gt;true&lt;&#x2F;code&gt;,今天我们将扩展 expect 文件以包含这个自定义 expect。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;fixtures&#x2F;toBeValidDate.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseExpect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseExpect&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeValidDate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp;&amp;amp; typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;passed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;toBeValidDate() 断言失败。&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你期望 &amp;#39;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;#39; 是一个有效的日期。&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个自定义 expect 的逻辑很直接,从 expect 接收数据,并验证使用&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Reference&#x2F;Global_Objects&#x2F;Date&#x2F;parse&quot;&gt;Date.parse(received)&lt;&#x2F;a&gt;方法判断字符串是否可以解析成正确的 date(并且不返回 NaN,这是一个&lt;a href=&quot;https:&#x2F;&#x2F;www.freecodecamp.org&#x2F;news&#x2F;falsy-values-in-javascript&#x2F;#:~:text=Description,)%2C%20and%20false%20of%20course.&quot;&gt;falsey 值&lt;&#x2F;a&gt;)。然后我们返回重写 expect 所需的详细信息。&lt;&#x2F;p&gt;
&lt;p&gt;注意,我在这个 fixture 中同时导出了&lt;code&gt;test&lt;&#x2F;code&gt;和&lt;code&gt;expect&lt;&#x2F;code&gt;,以便在测试中使用这个 fixture 时可以访问&lt;code&gt;test&lt;&#x2F;code&gt;。这是我做出的决定,你在测试中不必这样做。这确实允许我只使用一个 test&#x2F;expect 导入,而不是从 @playwright&#x2F;test 导入&lt;code&gt;test&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;auth&#x2F;login.post.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果我没有导出 test
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&#x2F;fixtures&#x2F;fixtures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;; (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;关于 fixtures 的更多信息见下文&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 因为我确实导出了 test,所以我可以这样做
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&#x2F;fixtures&#x2F;fixtures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&#x2F;login POST 请求&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST 没有主体&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;timestamp&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeValidDate&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.status).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Bad Request&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;auth&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我还添加了一些其他的自定义 expects,其中一些在这个项目中不会使用,但对其他人可能有用。下面的自定义断言在我工作中的代码库里被大量使用,因为我们有一个 API,这个接口根据输入的不同有很多潜在的值。在对具有多个返回数组的大型 GET items 请求进行断言时,我们可以为多个值创建更通用的断言。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;fixtures&#x2F;toBeOneOfValues.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseExpect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseExpect&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeOneOfValues&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;passed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;toBeOneOfValues() 断言失败。&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你期望 [${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}] 包含 &amp;#39;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;下面的自定义 expects 使得断言响应否是为正确类型变得超级简单!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;fixtures&#x2F;typesExpects.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseExpect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseExpect&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeOneOfTypes&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;||
&lt;&#x2F;span&gt;&lt;span&gt;      (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;passed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;toBeOneOfTypes() 断言失败。&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你期望 &amp;#39;${
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          }&amp;#39; 类型是 [${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}] 类型之一&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;警告: [${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}] 数组包含 &amp;#39;null&amp;#39; 类型,这在错误中不会被打印&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          }&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeNumber&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;passed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;toBeNumber() 断言失败。&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你期望 &amp;#39;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;#39; 是一个数字,但它是 ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeString&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;passed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;toBeString() 断言失败。&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你期望 &amp;#39;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;#39; 是一个字符串,但它是 ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeBoolean&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;boolean&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;passed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;toBeBoolean() 断言失败。&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你期望 &amp;#39;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;#39; 是一个布尔值,但它是 ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeObject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pass&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;passed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;toBeObject() 断言失败。&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你期望 &amp;#39;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;#39; 是一个对象,但它是 ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在下面的 spec 中,你可以看到在一个测试中使用的所有不同的自定义 expects。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;test.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lib&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fixtures&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fixtures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;; &#x2F;&#x2F; 导入自定义断言定&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92649;color:#f8f8f0;&quot;&gt;义&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;自定义断言&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用fixture&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;timestamp&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeValidDate&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dateStr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;2021-01-01&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dateStr&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeValidDate&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;number &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;123&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeNumber&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;boolean &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;boolean&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeBoolean&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;string &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeString&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.status).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeOneOfValues&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;401&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;403&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.status).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeOneOfTypes&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;mergeexpects-fixture&quot;&gt;MergeExpects fixture&lt;&#x2F;h3&gt;
&lt;p&gt;如果你在上面的例子中仔细观察,你可能注意到我只有一个导入 &lt;code&gt;import { test, expect } from &quot;@fixtures&#x2F;fixtures&quot;;&lt;&#x2F;code&gt; 用于我们添加的所有不同 fixture。在 1.39 版本中,playwright 团队引入了一种简单的方法来合并 &lt;code&gt;expect.extend&lt;&#x2F;code&gt; 和 &lt;code&gt;test.extend&lt;&#x2F;code&gt;,使你的导入更简洁和整洁! 发布说明可以在&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;release-notes#version-139&quot;&gt;这里&lt;&#x2F;a&gt;找到。&lt;&#x2F;p&gt;
&lt;p&gt;对于我们的例子,我创建了一个 &lt;code&gt;fixtures.ts&lt;&#x2F;code&gt; 文件,内容如下。我正在导入 &lt;code&gt;mergeExpects()&lt;&#x2F;code&gt;,这是最新版本的新增功能,以及所有其他 &lt;code&gt;expect.extend&lt;&#x2F;code&gt; fixture。然后我创建并导出一个新的 &lt;code&gt;expect 变量&lt;&#x2F;code&gt;,将其设置为 &lt;code&gt;mergeExpects(fixture1, fixture2, fixture3, etc)&lt;&#x2F;code&gt; 的响应。这将创建一个单一的 &lt;code&gt;fixture&lt;&#x2F;code&gt;,可以导入到所有使用这些自定义断言的测试中。&lt;&#x2F;p&gt;
&lt;p&gt;💡 如果你无法访问 &lt;code&gt;mergeExpects&lt;&#x2F;code&gt;,你需要在 package.json 文件中将 Playwright 更新到至少 1.39 版本。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;fixtures&#x2F;fixtures.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mergeExpects &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toBeOneOfValuesExpect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&#x2F;fixtures&#x2F;toBeOneOfValues&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toBeValidDate &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&#x2F;fixtures&#x2F;toBeValidDate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;typesExpects &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&#x2F;fixtures&#x2F;typesExpects&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mergeExpects&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toBeOneOfValuesExpect&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toBeValidDate&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;typesExpects
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;但在我们开始导入 fixture 之前,让我们更新我们的 &lt;code&gt;tsconfig.json&lt;&#x2F;code&gt; 并添加 &lt;code&gt;@fixtures&lt;&#x2F;code&gt; 相对路径,并用新的导入更新之前的用例和 &lt;code&gt;fixtures.ts&lt;&#x2F;code&gt; 文件。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tsconfig.json
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;compilerOptions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;baseUrl&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;esModuleInterop&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@datafactory&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&#x2F;datafactory&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&#x2F;helpers&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@fixtures&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&#x2F;fixtures&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;有了上面的更改,导入应该看起来像这样&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 新的
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@fixtures&#x2F;fixtures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 旧的
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&#x2F;fixtures&#x2F;fixtures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;你可以在下面的 pull request 中看到整个代码库中添加的所有更改。我没有对 &quot;@playwright&#x2F;test&quot; 进行查找和替换,尽管我本可以这样做。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pull&#x2F;17&quot;&gt;pull request&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wo-zai-vscode-zhong-yu-dao-de-qi-guai-cuo-wu&quot;&gt;我在 VSCode 中遇到的奇怪错误&lt;&#x2F;h3&gt;
&lt;p&gt;对于一个我在尝试使用一些自定义 expects 时持续遇到的错误,我仍然有点困惑。见下文。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;Property &amp;#39;toBeValidDate&amp;#39; does not exist on type &amp;#39;MakeMatchers&amp;lt;void, any,
&lt;&#x2F;span&gt;&lt;span&gt;{ toBeOneOfValues(this: State, received: any, array: any[]):
&lt;&#x2F;span&gt;&lt;span&gt;{ message: () =&amp;gt; string; pass: true; }
&lt;&#x2F;span&gt;&lt;span&gt;| { message: () =&amp;gt; string; pass: false; }; }
&lt;&#x2F;span&gt;&lt;span&gt;&amp;amp; { toBeValidDate(this: State, received: any):
&lt;&#x2F;span&gt;&lt;span&gt;{ ...; }
&lt;&#x2F;span&gt;&lt;span&gt;| { ...; }; }
&lt;&#x2F;span&gt;&lt;span&gt;&amp;amp; { ...; }&amp;gt;&amp;#39;.ts(2339) any
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我怀疑这可能是 Playwright 代码库的一个 bug,因为它只在存在 &lt;code&gt;expect(any)&lt;&#x2F;code&gt; 类型时显示这个错误,在下面的例子中,&lt;code&gt;body.timestamp&lt;&#x2F;code&gt; 是 &lt;code&gt;any&lt;&#x2F;code&gt; 类型,因为它直到异步响应调用后才被设置。如果我将 &lt;code&gt;body.timestamp&lt;&#x2F;code&gt; 改为时间戳的字符串,IDE 错误就会消失。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;10&#x2F;image.png&quot; alt=&quot;图片 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我花了相当多的时间试图找到一种测量 API 调用持续时间的方法。我最初尝试创建一个请求 fixture,但始终无法使其工作,因为我可以捕获持续时间,但无法将这个持续时间计算传递给 &lt;code&gt;request&lt;&#x2F;code&gt; 对象以在响应断言中使用,不过我可以在断言中通过 console.log 打印出来。所以我转而为测试添加了一个持续时间,可以在下面找到一种实现方法。这很冗长,我不太喜欢,但至少是一个前进的方向。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;auth&#x2F;login.post.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: POST &#x2F;auth&#x2F;login
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@fixtures&#x2F;fixtures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Env &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;env&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&#x2F;login POST 请求&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Env&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_NAME&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Env&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_PASSWORD&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用有效凭证的 POST&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 计算持续时间
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;start &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;now&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: {
&lt;&#x2F;span&gt;&lt;span&gt;        username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 计算持续时间
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;end &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;now&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;duration &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;end &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;start&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 断言持续时间
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;duration&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeLessThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;()[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;set-cookie&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;token=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h3&gt;
&lt;p&gt;1.39 版本的最新功能应该使管理 fixture 的 &lt;code&gt;imports&lt;&#x2F;code&gt; 变得更加容易,因为我们可以根据需要合并 fixture! 我相信这对通过 fixture 扩展 &lt;code&gt;test&lt;&#x2F;code&gt; 的影响会比扩展 &lt;code&gt;expect&lt;&#x2F;code&gt; 更大,因为这可能会使通过 fixture 管理页面对象变得更加容易。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;10&#x2F;image-1.png&quot; alt=&quot;图片 8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-14-custom-assertions-extending-expect&#x2F;&quot;&gt;URL 来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-10-16&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>The Definitive Guide to API Test Automation With Playwright: Part 13 - Validate API Response Against OpenAPI Spec Schema</title>
        <published>2023-10-09T00:00:00+00:00</published>
        <updated>2023-10-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-13-validate-api-response-against-openapi-spec-schema/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-13-validate-api-response-against-openapi-spec-schema/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-13-validate-api-response-against-openapi-spec-schema/">&lt;p&gt;Welcome back, this week we&#x27;ll look at how to validate the api response body from the schema against the OpenAPI spec schema. This is a more complex solution than we covered in the previous article below. In that article we walked through taking a snapshot of the response, converting it into a JSON schema and saving the results as a part of the repository. This article will take the downloaded OpenAPI spec files that we download when we calculate coverage, and use the spec to validate each field that is returned in the response body. I&#x27;ll also go ahead and mention a lot of the  ideas were originally from &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;author&#x2F;sergei&#x2F;&quot;&gt;Sergei Gapanovich&lt;&#x2F;a&gt;, and were implemented against our test repo at work.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-9-validating-json-schema&#x2F;&quot;&gt;The Definitive Guide to API Test Automation With Playwright: Part 9&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re just joining us now go check out the &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;introduction post&lt;&#x2F;a&gt; which links to all parts we&#x27;ve covered so far. For our examples we will use the playwright-api-test-demo repository linked below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&quot;&gt;GitHub - playwrightsolutions&#x2F;playwright-api-test-demo&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;breaking-down-validateagainstschema-function&quot;&gt;Breaking Down validateAgainstSchema() Function&lt;&#x2F;h2&gt;
&lt;p&gt;Before I show off the code, I wanted to walk through what type of checks we are doing through the function. I&#x27;ll attempt to explain it with the diagram below. Let&#x27;s start by observing the &lt;code&gt;body.bookings[0]&lt;&#x2F;code&gt; is an javascript object with &lt;code&gt;bookingDates&lt;&#x2F;code&gt; which includes a checkin string and a checkout string. The red line indicates the shape of the object. The blue line indicates the name &lt;code&gt;BookingDates&lt;&#x2F;code&gt; schema in the booking_spec3.json file. The green line indicates we need to look in the &lt;code&gt;booking&lt;&#x2F;code&gt; schema file, as we have multiple schema files with this project, for other proejcts that only have 1 OpenApi spec file the code would need to be tweaked. The orange lines indicate how the javascript object matches up with the OpenApi spec file &lt;code&gt;booking_spec3.json&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-16.png&quot; alt=&quot;Image 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;validateagainstschema-code&quot;&gt;validateAgainstSchema() code&lt;&#x2F;h2&gt;
&lt;p&gt;The code is for this function is below&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;helpers&#x2F;validateAgainstSchema.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;removeItemsFromArray &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;arrayFunctions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;capitalizeString &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;capitalizeString&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fail &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;assert&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addWarning &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;warnings&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;authSchemaExpectedResponseParamsCount &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;schemaData&#x2F;Auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingSchemaExpectedResponseParamsCount &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;schemaData&#x2F;Booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;brandingSchemaExpectedResponseParamsCount &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;schemaData&#x2F;Branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;messageSchemaExpectedResponseParamsCount &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;schemaData&#x2F;Message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;reportSchemaExpectedResponseParamsCount &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;schemaData&#x2F;Report&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomSchemaExpectedResponseParamsCount &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;schemaData&#x2F;Room&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;stringDateByDays &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * `Definition:` Validates an **object** against a specified **schema object**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; - The object to check schemaObject against.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaObject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; - The schema object name as documented in *_spec3.json.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; - The type of docs (e.g. `branding`, `message`, `booking`, etc).
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;notReturnedButInSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; - Any defined properties in schema but not returned by Falcon.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;extraParamsReturned&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; - Any undefined properties returned by Falcon; **_create a bug if there are any._**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateAgainstSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;schemaObject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;docs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;notReturnedButInSchema &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[],
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;extraParamsReturned &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; get keys from the object
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseObjectKeys &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Object&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;keys&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; get keys from the docs
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schema &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;schemaParameters&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaObject&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docsObjectKeys &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Object&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;keys&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schema&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;*
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    if used - workaround around a bug
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    this should not be ok when we have more params in a response than in docs
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    filter out extra params from the response params array if any
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;  *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;extraParamsReturned&lt;&#x2F;span&gt;&lt;span&gt;.length &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseObjectKeys &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;removeItemsFromArray&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseObjectKeys&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;extraParamsReturned
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; filter out hidden params from the schema params array if any
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;notReturnedButInSchema&lt;&#x2F;span&gt;&lt;span&gt;.length &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docsObjectKeys &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;removeItemsFromArray&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docsObjectKeys&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;notReturnedButInSchema
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; compare object keys (need to be sorted since order differs)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docsObjectKeys&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;sort&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;responseObjectKeys&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;sort&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; add a warning if schema object length has been changed based on doc types
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;recordedSchemaResponseParamsCount&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;recordedSchemaResponseParamsCount &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;authSchemaExpectedResponseParamsCount&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;recordedSchemaResponseParamsCount &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingSchemaExpectedResponseParamsCount&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;recordedSchemaResponseParamsCount &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;brandingSchemaExpectedResponseParamsCount&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;recordedSchemaResponseParamsCount &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;messageSchemaExpectedResponseParamsCount&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;report&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;recordedSchemaResponseParamsCount &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;reportSchemaExpectedResponseParamsCount&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;room&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;recordedSchemaResponseParamsCount &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomSchemaExpectedResponseParamsCount&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docsObjectKeys&lt;&#x2F;span&gt;&lt;span&gt;.length &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!==
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;recordedSchemaResponseParamsCount&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaObject&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;-
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;notReturnedButInSchema&lt;&#x2F;span&gt;&lt;span&gt;.length
&lt;&#x2F;span&gt;&lt;span&gt;  ) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;addWarning&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&amp;#39;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaObject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;#39; schema object in &amp;#39;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;#39; docs has been updated. Please, do the following: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;` &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;- Check if the change is expected &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;` &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;- Update &amp;quot;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;().title}&amp;quot; test with appropriate assertions &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;` &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;- Re-run the test from terminal with &amp;#39;GENERATE_SCHEMA_TRACKING_DATA=true&amp;#39;, commit and push generated files &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;schemaParameters&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;schema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;docs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiDocs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;readFileSync&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}_spec3.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;utf-8&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiDocs&lt;&#x2F;span&gt;&lt;span&gt;.components.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemas&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schema&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;properties&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;catch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;e&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fail&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The &amp;#39;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;#39; object you passed does not exist in &amp;#39;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;#39; documentation&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;updateDocsSchemasParamsCount&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;allDocs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;report&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;room&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;allDocs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;forEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;docs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiDocs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;readFileSync&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}_spec3.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;utf-8&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemas &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiDocs&lt;&#x2F;span&gt;&lt;span&gt;.components.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemas&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaObjects &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Object&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;keys&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemas&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&#x2F; updated on &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;export const ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}SchemaExpectedResponseParamsCount = {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaObjects&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;forEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;schema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}: ${
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;        Object.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;keys&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemas&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;properties&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;).length
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;      },&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;};&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;writeFileSync&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;lib&#x2F;helpers&#x2F;schemaData&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;capitalizeString&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;)}.ts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data
&lt;&#x2F;span&gt;&lt;span&gt;      );
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;catch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I won&#x27;t walk through all the code for this but the JSDoc for the main function walks through the inputs needed. One thing that is worth calling out here, is with this type of Schema Verification is that when new items are added to the response and schema, you won&#x27;t have any way of knowing about this programmatically. So we&#x27;ve added &lt;code&gt;updateDocsSchemasParamsCount()&lt;&#x2F;code&gt; function along with some static files that keep track of the different counts for the different json objects. When new objects are added&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; .&#x2F;lib&#x2F;helpers&#x2F;schemaData&#x2F;Booking.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; updated on 2023-09-23
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingSchemaExpectedResponseParamsCount &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  Error: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  Booking: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  BookingDates: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  CreatedBooking: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;updating-expectedresponsecount&quot;&gt;Updating ExpectedResponseCount&lt;&#x2F;h2&gt;
&lt;p&gt;To update the above numbers programmatically I&#x27;ve built the code so you can run the test with the environment variable &lt;code&gt;GENERATE_SCHEMA_TRACKING_DATA=true&lt;&#x2F;code&gt; to update tracking files. It will overwrite existing files but it&#x27;s you who have to commit and push them to our repo.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;warning-log&quot;&gt;Warning.log&lt;&#x2F;h2&gt;
&lt;p&gt;With this tracking we now have a way to notify if new items are added to a response body, and is a trigger for me to go and update our checks to assert against the new field. With that I have a &lt;code&gt;warning.log&lt;&#x2F;code&gt; from the &lt;code&gt;addWarning()&lt;&#x2F;code&gt; function that can get output to warn me of things, but not fail the test.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;&#x2F;&#x2F; warning.log
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;WARNING: &amp;#39;BookingDates&amp;#39; schema object in &amp;#39;booking&amp;#39; docs has been updated. Please, do the following:
&lt;&#x2F;span&gt;&lt;span&gt;- Check if the change is expected
&lt;&#x2F;span&gt;&lt;span&gt;- Update &amp;quot;GET booking summary with specific room id&amp;quot; test with appropriate assertions
&lt;&#x2F;span&gt;&lt;span&gt;- Re-run the test from terminal with &amp;#39;GENERATE_SCHEMA_TRACKING_DATA=true&amp;#39;, commit and push generated files
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;WARNING: This test should be refactored: &amp;#39;GET booking summary with specific room id&amp;#39; to use custom assertions
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;validateagainstschema-failure&quot;&gt;validateAgainstSchema() failure&lt;&#x2F;h2&gt;
&lt;p&gt;An example of a failure would be if the OpenApi booking_spec.json was updated to be &lt;code&gt;checkindate&lt;&#x2F;code&gt; and &lt;code&gt;checkoutdate&lt;&#x2F;code&gt; rather than just &lt;code&gt;checkin&lt;&#x2F;code&gt; and &lt;code&gt;checkout&lt;&#x2F;code&gt;, the failure would look like. As the response body of the API still had &lt;code&gt;checkin&lt;&#x2F;code&gt; and &lt;code&gt;checkout&lt;&#x2F;code&gt;, this would cause the test to fail. Note we aren&#x27;t doing any type checking here we are just validating that the shape of the response body matches the OpenApi spec file.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-18.png&quot; alt=&quot;Image 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As long as there are good assertions that validate the content of the message is a string in our other expects we should have the type checks included as a part of our assertions.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-implement-this-functionality&quot;&gt;Why implement this functionality?&lt;&#x2F;h2&gt;
&lt;p&gt;The main reason we did at our workplace is we are working with really large API response bodies, and the jsonSchema snapshot testing required a good bit of upkeep. For example we had some fields that would respond with null or a string, and if the original response body, had just a string, the snapshot would always look for a string. To record new snapshots we would have to get both null and string responses for the snapshot to pass for all scenarios.&lt;&#x2F;p&gt;
&lt;p&gt;We also found that we didn&#x27;t have a way to keep up with &quot;change-detection&quot;. If new fields were added to a response body, we wanted to add assertions around those new fields, it was not easy to know about them. Now as long as our Open Api spec file is updated this new code will add a warning for us.&lt;&#x2F;p&gt;
&lt;p&gt;The pull request with the code changes can be found below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pull&#x2F;14&#x2F;files&quot;&gt;Bm&#x2F;validate against schema by BMayhew&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks for reading! If you found this helpful, reach out and let me know on &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; or consider &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;buying me a cup of coffee&lt;&#x2F;a&gt;. If you want more content delivered to you in your inbox subscribe below, and be sure to leave a ❤️ to show some love.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-13-validate-api-response-against-openapi-spec-schema&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-10-09T12:30:25.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>The Definitive Guide to API Test Automation With Playwright: Part 12 - My Top 10 Great Practices </title>
        <published>2023-10-02T00:00:00+00:00</published>
        <updated>2023-10-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-12-best-practices/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-12-best-practices/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-12-best-practices/">&lt;p&gt;Ok so I will admit, I was tempted to title this&lt;code&gt;Best Practices&lt;&#x2F;code&gt; but decided to stick with Great practices because all the advice I plan to give really &quot;Depends on Your Context&quot;. I do however plan to give you some good advice along with rationale so you can tell when it becomes bad advice. I believe, if you don&#x27;t understanding why something should be done, then you&#x27;ve fallen into a trap of building things that aren&#x27;t helping you reach your goals, and you&#x27;ll keep doing it even when it&#x27;s no longer necessary or even becomes deleterious.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re just joining us now go check out the &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;introduction post&lt;&#x2F;a&gt; which links to all parts we&#x27;ve covered so far. For our examples we will use the playwright-api-test-demo repository linked below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&quot;&gt;GitHub - playwrightsolutions&#x2F;playwright-api-test-demo&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;great-practices&quot;&gt;Great Practices&lt;&#x2F;h2&gt;
&lt;p&gt;Reminding you of our context, we have been on a 11 Part journey where we have built a somewhat complex API Test Automation Regression Suite of checks against the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mwinteringham&#x2F;restful-booker-platform&quot;&gt;API&lt;&#x2F;a&gt; that drives &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-establish-a-goal-for-your-playwright-test-suite&quot;&gt;1. Establish a Goal for Your Playwright Test Suite&lt;&#x2F;h2&gt;
&lt;p&gt;Decide on what your goal will be for this automation suite. For example my goal with the the repo we&#x27;ve been building has been to catch &lt;code&gt;Regression&lt;&#x2F;code&gt; bugs, acting as a change detection tool. If the API changes in a breaking fashion (things about the api responses are changed or removed) the automation should catch this. In it&#x27;s current state it wouldn&#x27;t catch new non-breaking changes that are added to the APIs, for example a new peice of information on the response body. Once you have this goal go ahead and add it to your readme.md and share it with your team.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-write-independent-tests&quot;&gt;2. Write Independent Tests&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Each test should be able to run on it&#x27;s own in any order.&lt;&#x2F;li&gt;
&lt;li&gt;There should be no dependencies that are needed between tests (outside of a &lt;code&gt;setup&lt;&#x2F;code&gt; script that runs before the test suite).&lt;&#x2F;li&gt;
&lt;li&gt;Tests shouldn&#x27;t share data between themselves&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you build your tests this way you unlock the power of running multiples tests at once, and eventually sharing your tests for SPEED!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-14.png&quot; alt=&quot;Image 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-naming-your-test-files-well&quot;&gt;3. Naming Your Test Files Well&lt;&#x2F;h2&gt;
&lt;p&gt;When creating your tests, it&#x27;s always helpful to organize them in a way that is logical and easy to follow. As you can see below, each test is organized in it&#x27;s own folder &lt;code&gt;auth&lt;&#x2F;code&gt; for example are all the end points that are at the path &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;auth&#x2F;&quot;&gt;&lt;code&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;auth&#x2F;&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; with the following name the actual endpoint that is being hit, along with the verb in the test name. This makes it really easy to see which tests exist, and a great pattern to follow. If you decide to follow a different pattern, that is fine just find something that you can manage and do it consistently!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-10.png&quot; alt=&quot;Image 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;4-utilize-describe-and-test-titles-well&quot;&gt;4. Utilize Describe and Test Titles Well&lt;&#x2F;h2&gt;
&lt;p&gt;In the below example I have taken the &lt;code&gt;login.post.spec.ts&lt;&#x2F;code&gt; file and removed most of the logic keeping the titles. When the below test runs, the &lt;code&gt;describe&lt;&#x2F;code&gt; block is at the highest level, and when tests run will be the overall container for all of the other tests when it comes to reporting.  You can notice that in the describe block I put the full endpoint path, along with the Verb. And within each test title I start with the &lt;code&gt;POST&lt;&#x2F;code&gt; verb along with what the test is actually checking (either positive or negative). This will help give a pattern, and make it really easy to troubleshoot when failures occur, our you have multiple &lt;code&gt;VERB&lt;&#x2F;code&gt; types within the same describe block (this can be seen in other tests within the repository we are working from).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; .&#x2F;tests&#x2F;auth&#x2F;login.post.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: POST &#x2F;auth&#x2F;login
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isValidDate &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Env &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;env&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&#x2F;login POST requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with valid credentials&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with invalid username and password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with valid username and invalid password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with invalid username and valid password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with no username and valid password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with empty body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with no body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST with valid credentials then validate with token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see there is a nice tree, and I can easily see the tests, which tests that were run, and the information about each tests just from the title. Following this pattern also allows me to collapse each endpoint down in the VS Code Test Explorer.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-11.png&quot; alt=&quot;Image 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;5-utilizing-let-variables-in-describe-blocks&quot;&gt;5. Utilizing let Variables in Describe Blocks&lt;&#x2F;h2&gt;
&lt;p&gt;For these next few practices we will use the following &lt;code&gt;bookings.post.spec.ts&lt;&#x2F;code&gt; code example. In this test I am making a &lt;code&gt;POST&lt;&#x2F;code&gt; to the booking&#x2F; URL. In order to make this post I need a valid roomId, checkInTime, and checkOutTime. When I need specific data like this especially if I want to have multiple tests, I want to be able to create some data in a &lt;code&gt;beforeEach()&lt;&#x2F;code&gt; block of code. When I do this if I create a variable within the &lt;code&gt;beforeEach()&lt;&#x2F;code&gt;, such as the &lt;code&gt;const room =&lt;&#x2F;code&gt; line of code, because of the way Javascript scoping works, I am not able to use that variable outside of the &lt;code&gt;beforeEach()&lt;&#x2F;code&gt; block. So anytime I need a variable that I want to set in a &lt;code&gt;beforeEach()&lt;&#x2F;code&gt; and then use it in my test later (see &lt;code&gt;requestBody&lt;&#x2F;code&gt; and &lt;code&gt;roomId&lt;&#x2F;code&gt;) I create empty let variables at the highest level within the &lt;code&gt;describe()&lt;&#x2F;code&gt; block.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; .&#x2F;tests&#x2F;booking&#x2F;bookings.post.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: POST &#x2F;booking&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRandomBookingBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureOpenCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@datafactory&#x2F;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;stringDateByDays &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRoom &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@datafactory&#x2F;room&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validateJsonSchema &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;validateJsonSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validateAgainstSchema &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;validateAgainstSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F; POST requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;room &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createRoom&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;room&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;futureOpenCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkInString &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toISOString&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;split&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOutString &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createRandomBookingBody&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkInString&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOutString
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST new booking with full body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;201&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST_booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateAgainstSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, [
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;phone&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    ]);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateAgainstSchema&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;BookingDates&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;6-utilizing-beforeeach-for-test-data-creation&quot;&gt;6. Utilizing beforeEach() for Test Data Creation&lt;&#x2F;h2&gt;
&lt;p&gt;Looking back to the example above you can see a lot is happening in the &lt;code&gt;beforeEach()&lt;&#x2F;code&gt; section of the code. My goal of the test here is to do any data creation or manipulation that we need for the test to run in the &lt;code&gt;beforeEach()&lt;&#x2F;code&gt; block. In this case it meant:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Creating a room&lt;&#x2F;li&gt;
&lt;li&gt;Setting the roomId variable&lt;&#x2F;li&gt;
&lt;li&gt;Getting an available future checkin data with the datafactory&lt;&#x2F;li&gt;
&lt;li&gt;Creating a valid checkin string&lt;&#x2F;li&gt;
&lt;li&gt;Creating a valid checkout string 2 days from a helper function&lt;&#x2F;li&gt;
&lt;li&gt;Creating a requestBody object utilizing the datafactory&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If this endpoint required authentication I would have also created the appropriate authorization headers in the &lt;code&gt;beforeEach()&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Doing all of this makes the test very basic, all I had to do was make the &lt;code&gt;POST&lt;&#x2F;code&gt; request with the &lt;code&gt;requestBody&lt;&#x2F;code&gt; that was set, and then start making assertions!&lt;&#x2F;p&gt;
&lt;p&gt;💡&lt;&#x2F;p&gt;
&lt;p&gt;Every time a new test is written new data for that test should be created separate of any previously written test (ex. new account created for each test, new payment intent for each test and that&#x27;s where the &lt;code&gt;Data Factory&lt;&#x2F;code&gt; comes in handy to keep our code DRY )&lt;&#x2F;p&gt;
&lt;h2 id=&quot;7-when-making-assertions-always-start-with-status-code&quot;&gt;7. When Making Assertions Always Start with Status Code&lt;&#x2F;h2&gt;
&lt;p&gt;From the above example still you can see the first assertion that we make is an &lt;code&gt;expect&lt;&#x2F;code&gt; on the response status code. I&#x27;ve found having this check first will save yourself a lot of headache.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;201&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When I first began writing API automation with playwright I typically would have this after setting the &lt;code&gt;const body = await response.json();&lt;&#x2F;code&gt; line of code, that were in most of my tests. The issue is sometimes a test would fail with an error like this. &lt;code&gt;SyntaxError: Unexpected end of JSON input&lt;&#x2F;code&gt;. I thought well, is my test broken, or is it the system I am checking broken. I didn&#x27;t have a clear indicator what the problem was from this error message.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-12.png&quot; alt=&quot;Image 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The reason this error happened in this case was the response body was not in &lt;code&gt;JSON&lt;&#x2F;code&gt; format, it was returned as &lt;code&gt;TEXT&lt;&#x2F;code&gt; from the endpoint because the response code was not &lt;code&gt;201&lt;&#x2F;code&gt; it was &lt;code&gt;403&lt;&#x2F;code&gt;. Adjusting these two lines back around, now the test fails with a really useful error message.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-13.png&quot; alt=&quot;Image 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;8-have-a-test-data-strategy&quot;&gt;8. Have a Test Data Strategy&lt;&#x2F;h2&gt;
&lt;p&gt;I do believe the hardest part of Test Automation is creating a strategy with your test data. I discuss this in-depth on Part 6, it&#x27;s worth checking out if you haven&#x27;t already.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-testcreating-a-datafactory-to-manage-test-data&#x2F;&quot;&gt;The Definitive Guide to API Test Automation With Playwright: Part 6&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There are of course other strategies you could employ such as seeding the data through setup scripts, reseting the database to get data to a good state, or creating static data that will always be available. Each situation may require a different solution but what&#x27;s important you need have &quot;A Strategy&quot; in place and make sure it&#x27;s communicated far and wide for anyone contributing to the automation or using the environments the automation runs against.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;9-utilize-aftereach-when-logging-out&quot;&gt;9. Utilize afterEach() When Logging Out&lt;&#x2F;h2&gt;
&lt;p&gt;If you have any kind of tests that utilize a saved cookie or logged in state, you need to make sure that if you have a logout test that destroy&#x27;s the session your automation is using, that you utilize the &lt;code&gt;afterEach()&lt;&#x2F;code&gt; block of the test to re-create a newly authenticated state. I don&#x27;t have an example of this within this codebase because I create new authentication any time I need authorization.&lt;&#x2F;p&gt;
&lt;p&gt;You can also utilzie &lt;code&gt;afterEach()&lt;&#x2F;code&gt; to clean up any data that other tests my rely on. This can be seen in the test below, specifically on the &lt;code&gt;PUT&lt;&#x2F;code&gt; test. you can see that after I change the branding, I run checks on the response body, and right after the assertions, another &lt;code&gt;PUT&lt;&#x2F;code&gt; request with the original body is made to update the data.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; .&#x2F;tests&#x2F;branding&#x2F;branding.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;branding&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: PUT &#x2F;branding&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBranding&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updatedBranding &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validateJsonSchema &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;validateJsonSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validateAgainstSchema &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;validateAgainstSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&#x2F; GET requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBranding&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET website branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBody&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET_branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateAgainstSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateAgainstSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;contact&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Contact&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateAgainstSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Map&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; This test has the potential to cause other UI tests to fail as this branding endpoint is a critical part of the entire UI of the website
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&#x2F; PUT requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBranding&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updatedBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updatedBranding&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;afterEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT website branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updatedBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;202&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updatedBody&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT_branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateAgainstSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;10-things-to-test-for-when-writing-api-checks&quot;&gt;10. Things to Test For When Writing API Checks&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Review the API documentation when writing automation. Check for all the different types of parameters that can be sent, and what parameters should be returned.&lt;&#x2F;li&gt;
&lt;li&gt;You should have shoot for at least 3 test cases where you can send: All Parameters, Required Parameters, and No Parameters, if there inputs on the endpoint.&lt;&#x2F;li&gt;
&lt;li&gt;Test for negative scenarios. This may sound crazy but the moment, an unauthorized API call returns a &lt;code&gt;200&lt;&#x2F;code&gt; rather than a &lt;code&gt;403&lt;&#x2F;code&gt; your security team and developer team will be thankful.&lt;&#x2F;li&gt;
&lt;li&gt;Another negative scenario could be attempting to send a number instead of a string to the API and check the response. It should be a clear error message for the end user, that you can assert on.&lt;&#x2F;li&gt;
&lt;li&gt;Create a &lt;code&gt;test&#x2F;scenarios&lt;&#x2F;code&gt; folder under  if there are multiple API calls that need to be made in sequence. These examples are typically tests that need chcecks across mutliple API endpoints such as: email notifications, apikey creation and validation (create an api key &amp;gt; then attempt to log in with it), permission endpoints that require steps to not just validate the permission was created, but that the permission rules are followed.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;m sure there are few great practices that I missed but for now this should get you to a great starting point!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-15.png&quot; alt=&quot;Image 10&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks for reading! If you found this helpful, reach out and let me know on &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; or consider &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;buying me a cup of coffee&lt;&#x2F;a&gt;. If you want more content delivered to you in your inbox subscribe below, and be sure to leave a ❤️ to show some love.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-12-best-practices&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-10-02T12:30:56.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何使用共享session来加速 Playwright 运行速度</title>
        <published>2023-09-25T00:00:00+00:00</published>
        <updated>2023-09-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/speeding-up-tests-execution-in-playwright-shared-session/"/>
        <id>https://playwright.itest.info/blog/speeding-up-tests-execution-in-playwright-shared-session/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/speeding-up-tests-execution-in-playwright-shared-session/">&lt;p&gt;测试执行越快越好 — 我们可以更快地检查更改的结果。然而,一如既往,这取决于我们正在测试的应用程序的复杂性。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yong-li-miao-shu-jian-jie&quot;&gt;&lt;strong&gt;用例描述 — 简介&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;假设我们有一个应用程序,它至少允许三种类型的用户:只读、读写和管理员。每种类型都有一些可以执行的专用操作,但 UI 通常是相同的。还有一个登录表单用于验证特定上下文。我们也可以假设我们至少有六个 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;next&#x2F;api&#x2F;class-test#test-describe-1&quot;&gt;describe&lt;&#x2F;a&gt; — 在 Playwright (以及 Protractor) 中意味着测试组(或测试套件)。每个 describe 都在单个 spec 文件中 — 如下例所示。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:313&#x2F;1*wNJjaL8-2bovtmvM3vIY7w.png&quot; alt=&quot;图片 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;每个 describe 至少包含 3 个测试用例,这些测试完全独立于彼此。项目配置为使用 &#x27;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;next&#x2F;api&#x2F;class-testproject#test-project-fully-parallel&quot;&gt;&lt;strong&gt;fullyParallel&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&#x27; 标志,该标志已启用。顺便说一下,每个 describe 都包含一个 &#x27;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;next&#x2F;api&#x2F;class-test#test-before-all-1&quot;&gt;&lt;strong&gt;beforeAll&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&#x27; 钩子,其中执行登录过程。describe 内的测试使用相同的上下文,即相同的用户。因此,如果我们以 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;next&#x2F;test-parallel#serial-mode&quot;&gt;&lt;strong&gt;串行模式&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; 运行,那么每个 describe 将只执行一次登录过程,然后测试将一个接一个地执行。但我们使用的是 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;next&#x2F;api&#x2F;class-testproject#test-project-fully-parallel&quot;&gt;&lt;strong&gt;fullyParallel&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; 模式,这意味着每个实例都将为自己执行登录过程。&lt;&#x2F;p&gt;
&lt;p&gt;第一个问题:如果我们只有 3 个用户上下文(只读、读写、管理员),为什么不只使用 3 个 &#x27;&lt;strong&gt;describe&lt;&#x2F;strong&gt;&#x27;?我的观点是,如果我们使用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;next&#x2F;api&#x2F;class-testproject#test-project-fully-parallel&quot;&gt;&lt;strong&gt;fullyParallel&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; 模式,每个启动的浏览器实例都将为自己执行登录过程。我还有一种感觉,我们将套件分得越多,就越能更好地管理它们,并行性也越好。在我的实验中,有时同一个 describe 内的用例的并行执行会被阻塞 — 不知道为什么 — 也许是某些 playwright 功能。;)&lt;&#x2F;p&gt;
&lt;p&gt;言归正传:我们如何加速这个过程?这里我将重点关注登录过程。在 playwright 中,我们可以轻松处理浏览器的 session 存储,正如你所知,所有负责用户授权(或保持用户身份验证)的 cookie、令牌和其他内容都存储在那里。因此,有一个名为 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;next&#x2F;api&#x2F;class-browsercontext#browser-context-storage-state&quot;&gt;&lt;strong&gt;storageState&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; 的方法 — 允许你将所有浏览器存储导出到文件中。但如何使用它呢?让我们看看需要做哪些更改。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-li-yong-fa&quot;&gt;&lt;strong&gt;示例用法&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;当前状态:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;示例 mati-qa-suite2.spec.js 内容:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*Rt0W-cF7hD7bAGZ3-1Hwkg.png&quot; alt=&quot;图片 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;默认的 playwright.config.js:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*vV8rOAzhR1rYghknpNU6Zw.png&quot; alt=&quot;图片 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如你所见,登录过程是从辅助文件中导入的(因为我们试图保持 &lt;strong&gt;PageObjectPattern&lt;&#x2F;strong&gt;) — 所以,所有登录所需的代码都存储在那里。这里是该方法可能的样子:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*iVRC5ewpPFtBSs2A-dfeMQ.png&quot; alt=&quot;图片 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;最后一行(使用 &#x27;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;next&#x2F;api&#x2F;class-locator#locator-wait-for&quot;&gt;&lt;strong&gt;waitFor&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt;&#x27; 方法)是为了确保我们已登录并可以继续。所有的 spec.js 文件都类似,只是用户上下文不同(只读、读写、管理员)。因此,再次强调,由于 &lt;strong&gt;fullyParallel&lt;&#x2F;strong&gt; 模式,每个浏览器实例都将使用自己的身份验证 — 这可能需要长达 10 秒的时间。在最坏的情况下,它将尝试为每个测试单独进行用户身份验证 — 大约 18 次 — 180 秒 — 3 分钟。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;更改&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;为了加速这些过程,需要进行以下更改:&lt;&#x2F;p&gt;
&lt;p&gt;将我们的登录过程调用提取到一个单独的 spec 文件中,比如 &lt;strong&gt;read-only.session-creator.spec.js&lt;&#x2F;strong&gt;。基本上,你只需创建一个新的 spec.js 文件,并从那里调用 loginToThePortal 方法。唯一的变化是添加一行代码,在成功登录后导出存储状态。以下是它可能的样子:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:666&#x2F;1*CqSDEvGM2G01eKkSsFuYFw.png&quot; alt=&quot;图片 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如你所见,这几乎是一个常规的 spec.js 文件。唯一的区别是我们从 Playwright 导入 &lt;strong&gt;test&lt;&#x2F;strong&gt; 并将其别名为 &lt;strong&gt;setup&lt;&#x2F;strong&gt;(代码的第一行)。然后我们定义一个 const 变量,包含我们将存储收集的浏览器 session 的文件路径。无论文件是否存在 — 如果不存在,将创建文件;如果存在,将覆盖文件。然后有一个 &#x27;&lt;strong&gt;setup&lt;&#x2F;strong&gt; == &lt;strong&gt;test&lt;&#x2F;strong&gt;&#x27; 块,其中我们调用我们的登录方法,最后(第 10 行)我们使用 &lt;strong&gt;storageState&lt;&#x2F;strong&gt; 方法和参数路径(定义的变量)保存&#x2F;保存浏览器 session 存储以供进一步使用。&lt;&#x2F;p&gt;
&lt;p&gt;在 playwright.config.js 中更改项目。我们需要为每个用户上下文添加一个额外的项目。这些项目只负责运行那些 session-creator.spec.js 文件。然后,我们需要为相关的测试套件添加依赖项。以下是其中一对可能的样子(由于我们有三个用户上下文,我们需要三对):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:615&#x2F;1*hLWGaPp6epvjddruA3pQ1g.png&quot; alt=&quot;图片 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如你所见,我们有一个具有特定名称的新项目和 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;next&#x2F;api&#x2F;class-testproject#test-project-test-match&quot;&gt;&lt;strong&gt;testMatch&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; 属性,它可以是正则表达式或应执行的套件的直接名称。在我们的示例中,这是我们的 read-only.session-creator.spec.js 文件。然后我们需要根据名称为我们的主项目添加依赖项(上面屏幕的第 25 行,等于第 20 行),并在 use 标签内,我们需要设置将使用的 &lt;strong&gt;storageState&lt;&#x2F;strong&gt;(第 28 行)。我们还应该更新&#x2F;重新考虑我们的 spec 文件名。我在这里建议为每个 spec 文件添加用户上下文名称作为前缀。因此,我们将使用 read-only.mati-qa-suite1.spec.js 而不是 mati-qa-suite1.spec.js。我认为,这是根据用户上下文区分套件的最简单方法。我们也可以在 describe 中使用标签,但这样查找使用特定上下文的 spec 会有点痛苦,因为我们需要打开每个文件并查看 describe 标题。&lt;&#x2F;p&gt;
&lt;p&gt;在我们执行登录的地方进行小改动 — 我们只需要调用输入 URL,而不是登录过程。因此,在我们包含所有用例的主 describe 中,我们需要做以下更改:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*0x9wrBtkPLYO04L7Hl_eJA.png&quot; alt=&quot;图片 8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;就是这样。现在,无论运行多少个浏览器实例,每个用户流程登录只会运行一次 — 所以,总共 3 次。经过所有更改后,我们的 &#x27;spec 结构&#x27; 应该是这样的:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:355&#x2F;1*Ud2NjOqVeHEAEGq6icVTpA.png&quot; alt=&quot;图片 9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;当然,我们现在可以稍微组织一下(在文件夹之间拆分,例如,admin、read-write、read-only),但这只是一个例子,所以不必漂亮。;)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-lun&quot;&gt;结论&lt;&#x2F;h2&gt;
&lt;p&gt;如你所见,有时即使是这样一个简单的更改也可能加速你的测试执行速度。当然,一切都取决于具体情况,但在大多数情况下,如果你的 &#x2F; 你正在测试的应用程序包含登录流程,使用这种方法(共享 session)总是比显式登录更好。你永远不知道,当你的测试数量超过 100 时,每一秒都很重要。所以,玩得开心,多试验。;)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mati-qa.medium.com&#x2F;speeding-up-tests-execution-in-playwright-shared-session-aca3d4d1b092&quot;&gt;URL 来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-09-25&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>The Definitive Guide to API Test Automation With Playwright: Part 11 - Adding Multi Environment Support</title>
        <published>2023-09-25T00:00:00+00:00</published>
        <updated>2023-09-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-11-adding-multi-environment-support/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-11-adding-multi-environment-support/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-11-adding-multi-environment-support/">&lt;p&gt;In part 11 of this series I will cover how you can add multi environment support to your Playwright project. There are multiple ways to handle this depending on how you have built your project. I&#x27;ll cover the way I handled it which includes having different .env files per environment. As an added bonus I&#x27;ll also cover how I added an AI code review bot to my repository.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re just joining us now go check out the &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;introduction post&lt;&#x2F;a&gt; which links to all parts we&#x27;ve covered so far. For our examples we will use the playwright-api-test-demo repository linked below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&quot;&gt;GitHub - playwrightsolutions&#x2F;playwright-api-test-demo&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;multi-environment-support&quot;&gt;Multi-Environment Support&lt;&#x2F;h2&gt;
&lt;p&gt;Adding multi-environment support is a critical step in making the test automation project more useful to you and your team members. Adding this should allow us to run our automation against any of our environments (assuming we planned well on our test data strategy) along with the ability for developers to run the automation against their local developer environments.&lt;&#x2F;p&gt;
&lt;p&gt;If we go back to Part 1 of this series you can see where we implemented the &lt;code&gt;.env&lt;&#x2F;code&gt; file with a URL as an environment variable.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-1-basics-of-api-testing-get&#x2F;&quot;&gt;The Definitive Guide to API Test Automation With Playwright&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As we proceeded our final environment variables included added:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;URL&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;ADMIN_NAME&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;ADMIN_PASSWORD&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;SECRET_API_KEY&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It&#x27;s possible that these could be different values in different environments. There are multiple ways to account for these changes, you could just override environment variables when running your playwright tests with something like&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;URL=http:&#x2F;&#x2F;localhost:3000 npx playwright test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The above command would override the environment variable &lt;code&gt;URL&lt;&#x2F;code&gt; with whatever we passed in using the default &lt;code&gt;.env&lt;&#x2F;code&gt; variable that we have configured. This is a decent strategy if you don&#x27;t require a lot of additional environment variables per environment. This is actually how I have things configured in my repositories at work with over 130 UI tests and over 1300 API tests. This setup runs against local dev environments, our staging environments, and any number of ephemeral environments with unique URLs we spin up based on every pull request in our different front end and back end repositories. This strategy does get out of hand if you have different test data in different environments that needs to be specified in your &lt;code&gt;.env&lt;&#x2F;code&gt; environments.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;adding-an-env-class&quot;&gt;Adding an Env class&lt;&#x2F;h3&gt;
&lt;p&gt;The first thing I did was create an &lt;code&gt;Env&lt;&#x2F;code&gt; class that will allows me more easily have access to my environment variables. this will give me type-ahead capabilities for my environment variables.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; .&#x2F;lib&#x2F;helpers&#x2F;env.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;Env {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public static readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;URL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.URL;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public static readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_NAME &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_NAME&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public static readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_PASSWORD &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_PASSWORD&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public static readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;SECRET_API_KEY &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;SECRET_API_KEY&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once this was in place I was able to update all the files in the repository where I was using a &lt;code&gt;process.env...&lt;&#x2F;code&gt; variable to specificy the environment variables. This included updating 8 files, which includes the below example.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; .&#x2F;lib&#x2F;helpers&#x2F;createHeaders.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Env &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;env&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; original
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; const username = process.env.ADMIN_NAME;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; const password = process.env.ADMIN_PASSWORD;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; new updates
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Env&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_NAME&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Env&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_PASSWORD&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;updates-to-playwright-config-ts&quot;&gt;Updates to playwright.config.ts&lt;&#x2F;h3&gt;
&lt;p&gt;I also made some updates to &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; to check for an environment variable named &lt;code&gt;test_env&lt;&#x2F;code&gt;. This allows you to specificy which environment you want to run the tests against. If this environment variable is specified, it will load the environment file &lt;code&gt;.env.${test_env}&lt;&#x2F;code&gt;. If the &lt;code&gt;test_env&lt;&#x2F;code&gt; environment variable is not set, it will use the original &lt;code&gt;.env&lt;&#x2F;code&gt; file we created, that logic is seen in the else block below.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; playwright.config.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* This allows you to pass in a `test_env` environment variable
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;to specify which environment you want to run the tests against *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test_env&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.env.${process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test_env&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    override: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that this code is in place I can add a new config &lt;code&gt;.env.local&lt;&#x2F;code&gt; which will allow me to run the automation suite against my local machine using this config.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; .env.local
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;URL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_NAME&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;admin
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_PASSWORD&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;SECRET_API_KEY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;secret
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;running-the-system-under-test-locally&quot;&gt;Running the System Under Test Locally&lt;&#x2F;h3&gt;
&lt;p&gt;In order to run the automation against a local environment we have to run the API server on our local machine, to do this we I&#x27;ll clone the repository to my local machine from GitHub. The link to the repo is below. I&#x27;ll use the &lt;code&gt;git clone&lt;&#x2F;code&gt; command utilizing SSH my prefered method.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mwinteringham&#x2F;restful-booker-platform&#x2F;tree&#x2F;trunk&quot;&gt;GitHub - mwinteringham&#x2F;restful-booker-platform&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; from my terminal where I want to install the project I run
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt; clone git@github.com:mwinteringham&#x2F;restful-booker-platform.git
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; cd into the directory
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;cd&lt;&#x2F;span&gt;&lt;span&gt; restful-booker-platform
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; run docker compose command
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;docker&lt;&#x2F;span&gt;&lt;span&gt; compose up
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once downloaded I &lt;code&gt;cd&lt;&#x2F;code&gt; into the directory and then run the docker command to create the developer environment using Docker Compose. If you have never used docker before getting started is easy, just &lt;a href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;get-docker&#x2F;&quot;&gt;download docker desktop&lt;&#x2F;a&gt;, and run it to initialize. Running the &lt;code&gt;docker compose up&lt;&#x2F;code&gt; command will utilize the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mwinteringham&#x2F;restful-booker-platform&#x2F;blob&#x2F;trunk&#x2F;docker-compose.yml&quot;&gt;docker-compose.yml&lt;&#x2F;a&gt; file that is a part of the repository, provided by the developer. Once run, all the api services will be spun up on different ports, along with the main web service which will be available on port &lt;code&gt;80&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-2.png&quot; alt=&quot;Image 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;running-the-tests-against-localhost&quot;&gt;Running the tests against localhost&lt;&#x2F;h3&gt;
&lt;p&gt;Now to run the tests using the &lt;code&gt;.env.local&lt;&#x2F;code&gt; config file we will use this command from the playwright main directory (be sure to go here in your terminal from the previous step).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test_env&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;local &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Boom, all the tests ran against the local environment, this is double checked using the &lt;code&gt;npx playwright show-report&lt;&#x2F;code&gt; and going into the trace file to see the actual network request.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-3.png&quot; alt=&quot;Image 8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-4.png&quot; alt=&quot;Image 9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;adding-a-script-to-make-running-this-easy&quot;&gt;Adding a script to make running this easy!&lt;&#x2F;h3&gt;
&lt;p&gt;Now that we have a working example let&#x27;s add a script to the &lt;code&gt;package.json&lt;&#x2F;code&gt; file to make running tests even easier in different configurations. I&#x27;ve added 2 different test configurations one for a staging environment an another for the local environment.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; package.json
&lt;&#x2F;span&gt;&lt;span&gt;..
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ut&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;jest --verbose&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test:staging&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test_env=staging npx playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test:local&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test_env=local npx playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92649;color:#f8f8f0;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I also went ahead and updated Playwright to the latest version &lt;code&gt;[1.38.1](https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;release-notes#version-138)&lt;&#x2F;code&gt; to take advantage of the latest updates to the trace viewer making it even easier to see the API requests when debugging.&lt;&#x2F;p&gt;
&lt;p&gt;A link to the initial implementation can be found below along with some other changes.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pull&#x2F;13&quot;&gt;Update env variables by BMayhew&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;adding-ai-to-code-review-process&quot;&gt;Adding AI to code review process&lt;&#x2F;h2&gt;
&lt;p&gt;I am using the GitHub Action &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;coderabbitai&#x2F;ai-pr-reviewer&quot;&gt;ai-pr-reviewer&lt;&#x2F;a&gt;, using the file below. All the docs can be found on the docs of the action. I did modify the original action to utilize &lt;code&gt;gpt-3.5-turbo&lt;&#x2F;code&gt; mainly because I am cheap, knowing that I will have lower quality results. I also updated the output attempting to make the reviewer snarky, arrogant, opinionated, critical, funny, and using slang that millennials use.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&#x2F; .github&#x2F;workflows&#x2F;openai-pr-reviewer.yml
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Code Review
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;permissions&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;contents&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;read
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pull-requests&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;write
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pull_request&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pull_request_review_comment&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;types&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;created&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;concurrency&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;group&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ github.repository }}-${{ github.event.number || github.head_ref ||
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;github.sha }}-${{ github.workflow }}-${{ github.event_name ==
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pull_request_review_comment&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pr_comment&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92649;color:#f8f8f0;&quot;&gt;| &amp;#39;pr&amp;#39; }}&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;  cancel-in-progress: ${{ github.event_name != &amp;#39;pull_request_review_comment&amp;#39; }}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;review&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;coderabbitai&#x2F;ai-pr-reviewer@latest
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;env&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;GITHUB_TOKEN&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ secrets.GITHUB_TOKEN }}
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;OPENAI_API_KEY&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ secrets.OPENAI_API_KEY }}
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;debug&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;review_simple_changes&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;review_comment_lgtm&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;openai_light_model&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;gpt-3.5-turbo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;openai_heavy_model&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;gpt-3.5-turbo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;summarize_release_notes&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            Craft concise release notes for the pull request.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            Focus on the purpose and user impact, categorizing changes as &amp;quot;New Feature&amp;quot;, &amp;quot;Bug Fix&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            &amp;quot;Documentation&amp;quot;, &amp;quot;Refactor&amp;quot;, &amp;quot;Style&amp;quot;, &amp;quot;Test&amp;quot;, &amp;quot;Chore&amp;quot;, or &amp;quot;Revert&amp;quot;. Provide a bullet-point list,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            e.g., &amp;quot;- New Feature: Added search functionality to the UI&amp;quot;. Limit your response to 50-100 words
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            and emphasize features visible to the end-user while omitting code-level details. You should end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            every summary with a funny pun or dad joke based on the code that you reviewed.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            There is no need to call it a dad joke or put just say it. An example of a
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            dad joke is &amp;quot;I&amp;#39;m afraid for the calendar. Its days are numbered.&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;system_message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            You are `@coderabbitai` (aka `github-actions[bot]`), a language model
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            trained by OpenAI. Your purpose is to act as a highly experienced
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            professional with focus on cloud-native. You should use slang words that
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            milenials use such as yeet, lit, bruh, fam, etc. You should also use emojis.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            When you do respond, you should respond as arrogant
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            and snarkey letting the developer know that they are not as good as
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            you are. You should also be very opinionated and not afraid to share
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            your opinion. You should also be very critical of the developer&amp;#39;s
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            code, and not let them off easy when they make an error. Whenever there
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            is a chance to make a joke or pun, you should do so.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            Company context -
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            CodeRabbit is an AI-powered Code reviewer.It boosts code quality and cuts manual effort.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            Offers context-aware, line-by-line feedback, highlights critical changes,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            enables bot interaction, and lets you commit suggestions directly from GitHub.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            When reviewing or generating content focus on key areas such as -
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            - Accuracy
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            - Relevance
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            - Clarity
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            - Technical depth
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            - Call-to-action
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            - SEO optimization
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            - Brand consistency
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            - Grammar and prose
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            - Typos
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            - Hyperlink suggestions
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            - Graphics or images (suggest Dall-E image prompts if needed)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            - Empathy
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            - Engagement
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The review can get a bit long, but it could be useful. The last thing you will need to do in order for the GitHub Action to work is to Create an &lt;a href=&quot;https:&#x2F;&#x2F;platform.openai.com&#x2F;overview&quot;&gt;OpenAI API key&lt;&#x2F;a&gt; and add it as a GitHub Secret on your project like so &lt;code&gt;Settings &amp;gt; Secrets and variables &amp;gt; Actions &amp;gt; New repository secret&lt;&#x2F;code&gt;. It needs to be named &lt;code&gt;OPENAI_API_KEY&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-7.png&quot; alt=&quot;Image 12&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Once added on your pull requests you will see summaries also reviews of each files included like the screenshots below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-5.png&quot; alt=&quot;Image 13&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-6.png&quot; alt=&quot;Image 14&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A link to the review can be found here. I will say I&#x27;ve found mixed quality in the results, but it is really interesting to see how far technology is continuing to progress.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pull&#x2F;16&quot;&gt;Updating .env and playwright version by BMayhew&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you wanted to take this a step further you could even configure the GitHub Actions to run against different environments based on what Environment variable is passed in. This way you can truly automate your CI&#x2F;CD pipelines. An example of how to trigger automation from another repo can be found below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-do-i-trigger-playwright-tests-to-run-across-repositories-using-github-actions&#x2F;&quot;&gt;How do I make a GitHub Action trigger&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-8.png&quot; alt=&quot;Image 19&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks for reading! If you found this helpful, reach out and let me know on &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; or consider &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;buying me a cup of coffee&lt;&#x2F;a&gt;. If you want more content delivered to you in your inbox subscribe below, and be sure to leave a ❤️ to show some love.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-11-adding-multi-environment-support&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-09-25&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>The Definitive Guide to API Test Automation With Playwright: Part 10 - Adding Unit Tests for Helpers</title>
        <published>2023-09-18T00:00:00+00:00</published>
        <updated>2023-09-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-10-adding-unit-tests-for-helpers/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-10-adding-unit-tests-for-helpers/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-10-adding-unit-tests-for-helpers/">&lt;p&gt;Welcome back! In this weeks guide I&#x27;m going walk you through the a pull request that was made by a reader &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;adam-pajda-b0ba03118&#x2F;&quot;&gt;Adam Pajda&lt;&#x2F;a&gt;. He took some time and added unit tests around 4 different functions that I created through this series. For this article I will walk through the different code that was added and why.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re just joining us now go check out the &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;introduction post&lt;&#x2F;a&gt; which links to all parts we&#x27;ve covered so far. For our examples we will use the playwright-api-test-demo repository linked below.&lt;&#x2F;p&gt;
&lt;p&gt;All code that was added can be found in the pull request linked below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pull&#x2F;15&quot;&gt;Add Unit Tests support for some helpers functions by pajdekPL&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;installation-and-docs&quot;&gt;Installation and Docs&lt;&#x2F;h2&gt;
&lt;p&gt;First off for unit testing &lt;a href=&quot;https:&#x2F;&#x2F;jestjs.io&#x2F;&quot;&gt;Jest&lt;&#x2F;a&gt; was the test framework of choice. A quote from the Jest documentation below.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Jest is a delightful JavaScript Testing Framework with a focus on simplicity.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Jest is a very popular testing framework. In the early days of Playwright before there was a built in JS&#x2F;TS test runner, Jest was a popular choice to implement.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;jestjs.io&#x2F;&quot;&gt;Jest&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To install Jest into your project use the command below to install jest, typescript, ts-jest, and @types&#x2F;jest. this command will update your &lt;code&gt;package.json&lt;&#x2F;code&gt; file with the appopriate dependencies, so when you run install your dependencies &lt;code&gt;npm install&lt;&#x2F;code&gt; on other machines or in CI all the correct packages are installed.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -D&lt;&#x2F;span&gt;&lt;span&gt; jest typescript ts-jest @types&#x2F;jest
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;creating-the-jest-project&quot;&gt;Creating the Jest project&lt;&#x2F;h2&gt;
&lt;p&gt;The command to initialize a &lt;code&gt;ts-jest&lt;&#x2F;code&gt; project is below.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; ts-jest config:init
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This command will create a file &lt;code&gt;jest.config.js&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; What the init script creates
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;** &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;{import(&amp;#39;ts-jest&amp;#39;).JestConfigWithTsJest} *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;exports &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  preset: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ts-jest&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  testEnvironment: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; jest.config.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Final edits to the config for our specific project
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* eslint-disable @typescript-eslint&#x2F;no-var-requires *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* eslint-disable no-undef *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;pathsToModuleNameMapper &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ts-jest&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;compilerOptions &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;tsconfig&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;** &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;{import(&amp;#39;ts-jest&amp;#39;).JestConfigWithTsJest} *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;exports &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  preset: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ts-jest&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;Below lines are added to make it possible to use paths from the tsconfig.json file in UTs
&lt;&#x2F;span&gt;&lt;span&gt;  modulePaths: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;compilerOptions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseUrl&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;  moduleNameMapper: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;pathsToModuleNameMapper&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;compilerOptions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;  roots: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With the final config we add some ESLint disable sections, and we also make it possible to utilize the paths we setup in the &lt;code&gt;tsconfig.json&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;writing-the-unit-tests&quot;&gt;Writing the unit tests&lt;&#x2F;h2&gt;
&lt;p&gt;The unit tests we will add are around the helper functions&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;createAssertions&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;createHeaders&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;schemaHelperFunctions&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I didn&#x27;t write these unit tests, but I was able to follow through each one and understand what is being checked.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;create-assertions-tests&quot;&gt;Create Assertions Tests&lt;&#x2F;h3&gt;
&lt;p&gt;Within this test, there is a single test case that checks if calling the &lt;code&gt;createAssertions&lt;&#x2F;code&gt; function has the proper output when passing in a complex json object (which would typically be a response body) into the function.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;helpers&#x2F;tests&#x2F;createAssertions.test.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createAssertions &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;createAssertions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; eslint-disable-next-line @typescript-eslint&#x2F;no-empty-function
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;log &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;jest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;spyOn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mockImplementation&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;createAssertions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;createAssertions logs proper assertions to console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      one: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      two: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      three: {
&lt;&#x2F;span&gt;&lt;span&gt;        four: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cuatro&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;        five: [
&lt;&#x2F;span&gt;&lt;span&gt;          {
&lt;&#x2F;span&gt;&lt;span&gt;            six: [],
&lt;&#x2F;span&gt;&lt;span&gt;          },
&lt;&#x2F;span&gt;&lt;span&gt;          {
&lt;&#x2F;span&gt;&lt;span&gt;            seven: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;          },
&lt;&#x2F;span&gt;&lt;span&gt;        ],
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expectedLogConsoleCalls &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[
&lt;&#x2F;span&gt;&lt;span&gt;      [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;expect(body.one).toBe(1);&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;expect(body.two).toBe(&amp;quot;2&amp;quot;);&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;expect(body.three.four).toEqual([&amp;quot;4&amp;quot;,&amp;quot;cuatro&amp;quot;]);&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;expect(body.three.five[0].six).toEqual([]);&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;expect(body.three.five[1].seven).toBeNull();&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    ];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createAssertions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mock&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;calls&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expectedLogConsoleCalls&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The next set of tests is exercising the &lt;code&gt;createHeaders&lt;&#x2F;code&gt; and &lt;code&gt;createInvalidHeaders&lt;&#x2F;code&gt; functions. It also mocks external dependencies such as &lt;code&gt;Env&lt;&#x2F;code&gt; and &lt;code&gt;createCookies&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The first test ensures that &lt;code&gt;createHeaders&lt;&#x2F;code&gt; returns the expected header when provided with a token. The second test verifies that &lt;code&gt;createHeaders&lt;&#x2F;code&gt; correctly calls &lt;code&gt;createCookies&lt;&#x2F;code&gt; with credentials from &lt;code&gt;Env&lt;&#x2F;code&gt; when no token is given. Lastly, there&#x27;s a test to confirm that &lt;code&gt;createInvalidHeaders&lt;&#x2F;code&gt; generates a header with an invalid cookie. There is a heavy use of &lt;code&gt;jest.mock()&lt;&#x2F;code&gt;, which you can read more about in the &lt;a href=&quot;https:&#x2F;&#x2F;jestjs.io&#x2F;docs&#x2F;mock-functions#mocking-modules&quot;&gt;Jest Documentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;helpers&#x2F;tests&#x2F;createHeaders.test.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createInvalidHeaders &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createCookies &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@datafactory&#x2F;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Env &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;env&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;jest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mock&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;env&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  __esModule: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  default: {
&lt;&#x2F;span&gt;&lt;span&gt;    URL: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;MockedUrl&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    ADMIN_NAME: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;MockedAdminName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    ADMIN_PASSWORD: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;MockedAdminPassword&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    SECRET_API_KEY: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;MockedSecretApiKey&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  namedExport: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;jest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;}));
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;jest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mock&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@datafactory&#x2F;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;createHeaders return header with given token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;input &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;mockedToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expected &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;token=${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;actual &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;actual&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expected&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;createHeaders call createCookies with credentials from Env when token is not given&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createCookiesMock &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;jest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mocked&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createCookies&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createCookiesMock&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mockResolvedValue&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;token=mockedToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expected &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;token=${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;mockedToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;actual &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createCookies&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveBeenCalledWith&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Env&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_NAME&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Env&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_PASSWORD
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;actual&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expected&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;createInvalidHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;should return header with invalid cookie&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expected &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cookie=invalid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;actual &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createInvalidHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;actual&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expected&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;schema-helper-functions-tests&quot;&gt;Schema Helper Functions Tests&lt;&#x2F;h3&gt;
&lt;p&gt;In this section we are actually creating 2 types of tests within two files. The first is an integration test, that tests more than one function. This test focuses on testing two functions: &lt;code&gt;writeJsonFile&lt;&#x2F;code&gt; and &lt;code&gt;createJsonSchema&lt;&#x2F;code&gt; from the &lt;code&gt;schemaHelperFunctions&lt;&#x2F;code&gt; module.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;writeJsonFile&lt;&#x2F;code&gt; test ensures that the &lt;code&gt;writeJsonFile&lt;&#x2F;code&gt; function correctly writes data to the specified path, and it also cleans up the test file after the test.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;createJsonSchema&lt;&#x2F;code&gt; test validates that the &lt;code&gt;createJsonSchema&lt;&#x2F;code&gt; function successfully creates and writes a new JSON schema file to a specific directory, verifying that the generated schema matches the expected schema structure. It also sets up and cleans up a test directory for this test.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;helpers&#x2F;tests&#x2F;schemaHelperFunctions.integration.test.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;writeJsonFile&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;schemaHelperFunctions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;fs&#x2F;promises&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;schemaHelperFunctions Integration Tests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;writeJsonFile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inputFile &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&#x2F;helpers&#x2F;tests&#x2F;fake.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;afterAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;unlink&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inputFile&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;writeJsonFile should write data properly to the given path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;({ some: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;MockedField&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;writeJsonFile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inputFile&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedData&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;readFile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inputFile&lt;&#x2F;span&gt;&lt;span&gt;, { encoding: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;utf8&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedData&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;createJsonSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpointDir &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;mocked_endpoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpointFullPath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.api&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpointDir&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaName &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;get_mocked&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpointFullPath&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;afterAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;rm&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpointFullPath&lt;&#x2F;span&gt;&lt;span&gt;, { recursive: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;, force: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;should create and write a new schema file to the endpoints directory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpointSchemaExpectedPath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpointFullPath&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}_schema.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{ some: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;MockedField&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expected &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        type: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        properties: {
&lt;&#x2F;span&gt;&lt;span&gt;          some: {
&lt;&#x2F;span&gt;&lt;span&gt;            type: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;          },
&lt;&#x2F;span&gt;&lt;span&gt;        },
&lt;&#x2F;span&gt;&lt;span&gt;        required: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;some&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaName&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpointDir&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedData&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;readFile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpointSchemaExpectedPath&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;        encoding: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;utf8&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expected&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The second is test is a unit test that tests similar functions. In the first test the &lt;code&gt;writeJsonFile&lt;&#x2F;code&gt; function is exercised. The test ensures that &lt;code&gt;fs.writeFile&lt;&#x2F;code&gt; is called with the provided file path and data.&lt;&#x2F;p&gt;
&lt;p&gt;In the &lt;code&gt;createJsonSchema&lt;&#x2F;code&gt; test, it verifies that the &lt;code&gt;createJsonSchema&lt;&#x2F;code&gt; function utilizes the &lt;code&gt;createSchema&lt;&#x2F;code&gt; function from the &quot;genson-js&quot; library to create a schema based on provided data and then writes the resulting schema as JSON to a specified file path using &lt;code&gt;fs&#x2F;promises&lt;&#x2F;code&gt;. Mocks are used to isolate these functions and capture their interactions.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;helpers&#x2F;tests&#x2F;schemaHelperFunctions.test.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;writeJsonFile &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;schemaHelperFunctions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;fs&#x2F;promises&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Schema&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createSchema &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;genson-js&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;jest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mock&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;fs&#x2F;promises&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;jest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mock&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;genson-js&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;schemaHelperFunctions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;jest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;clearAllMocks&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;writeJsonFile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;should call fs.writeFile with given filePath and Data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inputFile &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;mocked&#x2F;input&#x2F;path.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;({ someMocked: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;MockedField&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;writeJsonFile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inputFile&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedData&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;writeFile&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeCalledWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;inputFile&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedData&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;createJsonSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createSchemaMock &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;jest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mocked&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createSchema&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;should create schema using createSchema from genson-js and write file using fs&#x2F;promises&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpointPath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;mocked_endpoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaFile &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;get_mocked&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expectedFullPath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.api&#x2F;mocked_endpoint&#x2F;get_mocked_schema.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{ someMocked: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;MockedField&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedSchema &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        type: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        properties: {
&lt;&#x2F;span&gt;&lt;span&gt;          someMocked: {
&lt;&#x2F;span&gt;&lt;span&gt;            type: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;          },
&lt;&#x2F;span&gt;&lt;span&gt;        },
&lt;&#x2F;span&gt;&lt;span&gt;        required: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;someMocked&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;Schema;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createSchemaMock&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mockReturnValueOnce&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedSchema&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expectedSchemaJsonContent &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedSchema&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaFile&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpointPath&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedData&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createSchema&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeCalledWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mockedData&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;writeFile&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeCalledWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expectedFullPath&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expectedSchemaJsonContent&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With the unit test additions &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;adam-pajda-b0ba03118&#x2F;&quot;&gt;Adam Pajda&lt;&#x2F;a&gt; also refactored the &lt;code&gt;schemaHelperFunctions.ts&lt;&#x2F;code&gt; file with changes that can be found &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pull&#x2F;15&#x2F;files#diff-1d40a1077f0749978b82e2033fb3389bbca5703a748cf761e2084b68db110ef1&quot;&gt;here&lt;&#x2F;a&gt;. This included renaming one of my functions to make it more clear, and adding an export to one of the functions so the unit test could utilize the function in the test.&lt;&#x2F;p&gt;
&lt;p&gt;Running all the tests will give you the following output.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image-1.png&quot; alt=&quot;Image 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;updating-your-ci-pipeline&quot;&gt;Updating your CI Pipeline&lt;&#x2F;h2&gt;
&lt;p&gt;Don&#x27;t forget to add a script in your &lt;code&gt;package.json&lt;&#x2F;code&gt; file so you can run &lt;code&gt;npm run ut&lt;&#x2F;code&gt; to run the jest tests.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; package.json
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ut&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;jest --verbose&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test:test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test_env=test npx playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now you can go ahead and update your GitHub actions file so this command is run whenever we make a pull request as a part of our CI pipeline.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&#x2F; .github&#x2F;workflows&#x2F;run-playwright.yml
&lt;&#x2F;span&gt;&lt;span&gt;---
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run lint
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run lint
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run prettier
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run prettier
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run UnitTests
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run ut
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;09&#x2F;image.png&quot; alt=&quot;Image 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks for reading! If you found this helpful, reach out and let me know on &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; or consider &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;buying me a cup of coffee&lt;&#x2F;a&gt;. If you want more content delivered to you in your inbox subscribe below, and be sure to leave a ❤️ to show some love.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-10-adding-unit-tests-for-helpers&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-09-18T12:30:33.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在 Playwright Test 中全局设置每个测试前后的钩子函数?</title>
        <published>2023-09-04T00:00:00+00:00</published>
        <updated>2023-09-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-do-you-set-hooks-before-or-after-each-spec-globally-in-playwright-test/"/>
        <id>https://playwright.itest.info/blog/how-do-you-set-hooks-before-or-after-each-spec-globally-in-playwright-test/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-do-you-set-hooks-before-or-after-each-spec-globally-in-playwright-test/">&lt;p&gt;这是最近在 Playwright &lt;a href=&quot;https:&#x2F;&#x2F;discord.com&#x2F;channels&#x2F;807756831384403968&#x2F;1139590010111266816&#x2F;1139590010111266816&quot;&gt;Discord 频道&lt;&#x2F;a&gt;中有人提出的问题。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;我想在每个测试用例结束后对特定数据表进行某种&quot;重置&quot;操作。我可以在每个测试文件中设置 &lt;code&gt;afterAll&lt;&#x2F;code&gt; 钩子来实现,但这会导致大量重复代码(想象一下有 100 个测试文件)。所以我们之前在 &lt;code&gt;test.ts&lt;&#x2F;code&gt; 文件中添加了钩子函数。我们真的以为这样就能全局生效了。&quot;哦,既然我们在测试文件中定义了这些钩子,它们就会像全局钩子一样工作&quot;。但我们后来发现事实并非如此。我知道 Playwright 有全局的设置和清理函数,但据我所知它们只会在所有测试开始前和结束后各运行一次。我们确实需要一个在每个测试文件结束后运行的钩子函数。如果能在每个 &lt;code&gt;describe&lt;&#x2F;code&gt; 块后运行也可以,但我觉得我们无法覆盖它。这个功能目前是否在讨论或已被提出?如果没有,我该怎么做?或者有什么方法&#x2F;技巧可以实现我们想要的效果?先谢谢大家!我很想听听你们的建议。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;帖子中的第一个回答是来自 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;tallybarak&#x2F;&quot;&gt;Tally Barak&lt;&#x2F;a&gt; 的自动 fixture(Automatic Fixtures)链接。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-fixtures#automatic-fixtures&quot;&gt;自动 fixture&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;自动 fixture&lt;&#x2F;p&gt;
&lt;p&gt;文档中说:&quot;自动 fixture 会为每个测试&#x2F;工作进程设置,即使测试没有直接列出它们。要创建自动 fixture,请使用元组语法并传递 &lt;code&gt;{ auto: true }&lt;&#x2F;code&gt;。&quot;&lt;&#x2F;p&gt;
&lt;p&gt;这意味着只要在文件顶部导入了 fixture,无论 fixture 中包含什么内容,都会自动运行,而不需要在 &lt;code&gt;test()&lt;&#x2F;code&gt; 块中引用它。&lt;&#x2F;p&gt;
&lt;p&gt;下面的解决方案并不能完全满足原始需求,因为问题提出者想要在每个 describe 块后或每个测试文件 &lt;code&gt;xxxx.spec.ts&lt;&#x2F;code&gt; 完成后运行某些操作。据我所知,目前还无法实现这一点。下面的解决方案将涵盖在每个测试运行后执行 afterEach 的用例。&lt;&#x2F;p&gt;
&lt;p&gt;我只实现过几个 fixture,所以我想尝试为这个问题提供一个解决方案。下面是一个示例。这个 fixture 扩展了 &lt;code&gt;test&lt;&#x2F;code&gt; 类,允许在 &lt;code&gt;await use()&lt;&#x2F;code&gt; 之前添加代码(这些代码将在测试代码之前执行),而在 &lt;code&gt;await use()&lt;&#x2F;code&gt; 之后添加的代码将在测试代码之后执行。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;fixtures&#x2F;hook.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;testHook&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;}&amp;gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  testHook: [
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;来自fixture的 BEFORE EACH 钩子&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这里的任何代码都将作为 before each 钩子运行
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;来自fixture的 AFTER EACH 钩子&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在这里放置你想在每个测试后自动运行的代码
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    { auto: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 从基础测试中导出 &amp;#39;expect&amp;#39;,以便在 spec.ts 文件中使用
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;使用该 fixture 的测试规范如下。我在这里使用 &lt;code&gt;console.log()&lt;&#x2F;code&gt; 来展示每行代码的运行时机。注意,我在这个测试文件中做的唯一改变是从我们上面创建的 fixture &lt;code&gt;..&#x2F;..&#x2F;lib&#x2F;fixtures&#x2F;hook&lt;&#x2F;code&gt; 中导入 &lt;code&gt;test&lt;&#x2F;code&gt; 和 &lt;code&gt;expect&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;ui&#x2F;hooks.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;fixtures&#x2F;hook&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;最高层级&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;--所有测试之前的问候--&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;afterAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;--所有测试之后的问候--&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;afterEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&amp;lt;&amp;lt;每个测试之后的问候&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&amp;gt;&amp;gt;每个测试之前的问候&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;afterAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;所有测试之后的问候&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试一&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试一 v2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试二&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试二 v2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;测试结果的输出如下所示。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-24.png&quot; alt=&quot;Image 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这些更改的 pull request 可以在这里找到。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;playwright-demo&#x2F;pull&#x2F;29&quot;&gt;pull request&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;总结一下,如果你需要在每个测试之前或之后执行代码或捕获信息,使用这样的 fixture 可能会非常有用。&lt;&#x2F;p&gt;
&lt;p&gt;⚠️ 需要考虑的一个风险是,如果你在 before 或 after 钩子中通过数据库 seed 脚本(创建或删除数据库中数据的脚本)或重置脚本修改测试数据,并且运行多个 &lt;code&gt;shard&lt;&#x2F;code&gt; 或多个 &lt;code&gt;worker&lt;&#x2F;code&gt;,那么数据修改可能会影响其他正在运行的测试。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读!如果你觉得这篇文章有帮助,请在 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; 上联系我,或考虑 &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;给我买杯咖啡&lt;&#x2F;a&gt;。如果你想在收件箱中收到更多内容,请在下方订阅,别忘了留下一个 ❤️ 来表示你的喜爱。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-do-you-set-hooks-before-or-after-each-spec-globally-in-playwright-test&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-09-04T12:30:14.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>The Definitive Guide to API Test Automation With Playwright: Part 9 - Validating JSON Schema</title>
        <published>2023-08-28T00:00:00+00:00</published>
        <updated>2023-08-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-9-validating-json-schema/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-9-validating-json-schema/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-9-validating-json-schema/">&lt;p&gt;Welcome back! In this weeks guide I&#x27;m going walk you through the tooling I use to help me create JSON schema snapshots, and asserting on those snapshots within Playwright API tests.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re just joining us now go check out the &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;introduction post&lt;&#x2F;a&gt; which links to all parts we&#x27;ve covered so far. For our examples we will use the playwright-api-test-demo repository linked below.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-json-schema&quot;&gt;What is JSON Schema?&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;JSON Schema&lt;&#x2F;strong&gt; is a declarative language that allows you to &lt;strong&gt;annotate&lt;&#x2F;strong&gt; and &lt;strong&gt;validate&lt;&#x2F;strong&gt; JSON documents. - &lt;a href=&quot;https:&#x2F;&#x2F;json-schema.org&#x2F;&quot;&gt;json-schema.org&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is different than OpenAPI spec but shares some similarities. I like to take a hands on approach when learning new things so let&#x27;s dive right in.&lt;&#x2F;p&gt;
&lt;p&gt;For our testing I will capture the response body in json from the &lt;code&gt;booking&#x2F;summary?roomid=1&lt;&#x2F;code&gt; test.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;2022-02-01&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;2022-02-05&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  ]
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see this is very basic information, I took this body and took it over to &lt;a href=&quot;https:&#x2F;&#x2F;www.jsonschema.net&#x2F;app&#x2F;schemas&#x2F;329125&quot;&gt;https:&#x2F;&#x2F;www.jsonschema.net&#x2F;app&#x2F;schemas&#x2F;329125&lt;&#x2F;a&gt; with the following settings.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-19.png&quot; alt=&quot;Image 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And generated the following JSON schema&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;$schema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;json-schema.org&#x2F;draft&#x2F;2019-09&#x2F;schema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;required&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;properties&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;required&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;properties&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;required&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;properties&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;              },
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;              }
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;          }
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see the JSON schema give us all the information about the body that was received. This is a very simple example, as these can get very complex, especially using large response body where some values may be null while others are strings. What we will be using JSON schema for is validating that the snapshot we create and save actually matches our response body that we will convert to schema and then compare. To learn more about JSON Schema, check out the article below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;testerops.com&#x2F;2023&#x2F;03&#x2F;13&#x2F;json-schema-validation&#x2F;&quot;&gt;JSON Schema&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;validatejsonschema-ts&quot;&gt;validateJsonSchema.ts&lt;&#x2F;h2&gt;
&lt;p&gt;I won&#x27;t walk through each section of this code, but I do use the below libraries for different needs. &lt;code&gt;genson-js&lt;&#x2F;code&gt; for creating the schema we save to a file in the &lt;code&gt;.api&#x2F;...&lt;&#x2F;code&gt; directory, and &lt;code&gt;ajv&lt;&#x2F;code&gt; for converting and comparing the live response from a test to the snapshot. We also use playwright &lt;code&gt;expect&lt;&#x2F;code&gt; in order to get feedback within the test report if the assertion fails.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;ajv&quot;&gt;ajv Another JSON Schema Validator&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;genson-js&quot;&gt;genson-js&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;helpers&#x2F;validateJsonSchema.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createJsonSchema &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;schemaHelperFunctions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Ajv &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ajv&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * Validates an object against a JSON schema.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;{string} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fileName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; - The first part of the name of the JSON schema file. The full name will be `${fileName}_schema.json`.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;{string} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;filePath&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; - The path to the directory containing the JSON schema file.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;{object} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; - The object to validate against the JSON schema.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;{boolean} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;[createSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;false]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; - Whether to create the JSON schema if it doesn&amp;#39;t exist.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *    const body = await response.json();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *    &#x2F;&#x2F; This will run the assertion against the existing schema file
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *    await validateJsonSchema(&amp;quot;POST_booking&amp;quot;, &amp;quot;booking&amp;quot;, body);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *    &#x2F;&#x2F; This will create or overwrite the schema file
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *    await validateJsonSchema(&amp;quot;POST_booking&amp;quot;, &amp;quot;booking&amp;quot;, body, true);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateJsonSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;fileName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;filePath&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;createSchema &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;jsonName &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fileName&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;path &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;filePath&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createSchema&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;jsonName&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; eslint-disable-next-line @typescript-eslint&#x2F;no-var-requires
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;existingSchema &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;.api&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;jsonName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}_schema.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ajv &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;Ajv({ allErrors: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ajv&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;compile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;existingSchema&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validRes &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validRes&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;SCHEMA ERRORS:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validate&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;RESPONSE BODY:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validRes&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;helpers&#x2F;schemaHelperFunctions.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createSchema &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;genson-js&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;fs&#x2F;promises&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createJsonSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;filePath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;.api&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;filePath&lt;&#x2F;span&gt;&lt;span&gt;, { recursive: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schema &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaString &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schema&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaName &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.api&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}_schema.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;writeJsonFile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaName&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;schemaString&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;JSON Schema created and saved.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;catch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;writeJsonFile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;writeFile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;catch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To actually use this functionality in a test you will first need to create a schema file. For our example we will use the &lt;code&gt;booking.get.spec.ts&lt;&#x2F;code&gt; file. Before we start asserting on an existing JSON schema snapshot, we will need to generate one. By Importing the helper and adding this line after assigning the &lt;code&gt;body&lt;&#x2F;code&gt; variable in the test, you will be able to generate a JSON schema snapshot file.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET_booking_summary&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The full test can be seen here&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;booking&#x2F;booking.get.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;booking&#x2F;summary
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isValidDate &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createInvalidHeaders &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validateJsonSchema &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;validateJsonSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addWarning &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;warnings&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F; GET requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;invalidHeader&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;invalidHeader &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createInvalidHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET booking summary with specific room id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;summary?roomid=1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThanOrEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET_booking_summary&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The JSON schema file that gets created is below. If you compare this to the file we received from the online tool you will notice it is very similar.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; .api&#x2F;booking&#x2F;GET_booking_summary_schema.json
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;properties&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;properties&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;properties&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;              },
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;              }
&lt;&#x2F;span&gt;&lt;span&gt;            },
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;required&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;          }
&lt;&#x2F;span&gt;&lt;span&gt;        },
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;required&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;required&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have the JSON schema snapshot we will need to update our function removing &lt;code&gt;true&lt;&#x2F;code&gt; as the last passed in parameter. The only time you should use that in the future is if you need to generate a new snapshot if the response body has changed.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET_booking_summary&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we should be able to run the test and validate things pass! One thing I like to do when writing automation is making the test fail when it should fail. How can we really trust our tests if we aren&#x27;t testing them? So for this example I will save a new variable from the &lt;code&gt;body&lt;&#x2F;code&gt;, response and modify it and pass it into the validateJsonSchema function.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;booking&#x2F;booking.get.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;booking&#x2F;summary
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isValidDate &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createInvalidHeaders &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validateJsonSchema &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;validateJsonSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addWarning &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;warnings&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F; GET requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;invalidHeader&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;invalidHeader &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createInvalidHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET booking summary with specific room id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;summary?roomid=1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThanOrEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;badBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;badBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1234567890&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET_booking_summary&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;badBody&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When running the test in this state this is the failure. The highest level error tells us that the validateJsonSchema failed. When viewing the test or the console.log() of the test run you can see the reasons why.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-20.png&quot; alt=&quot;Image 9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-22.png&quot; alt=&quot;Image 10&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The full error message is below, you can see we overwrote the &lt;code&gt;checkin&lt;&#x2F;code&gt; property with a &lt;code&gt;number&lt;&#x2F;code&gt; instead of an expected string in our test above. The validateJsonSchema failed because a &lt;code&gt;number&lt;&#x2F;code&gt; != &lt;code&gt;string&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;[{&quot;instancePath&quot;:&quot;&#x2F;bookings&#x2F;0&#x2F;bookingDates&#x2F;&lt;strong&gt;checkin&lt;&#x2F;strong&gt;&quot;,&quot;schemaPath&quot;:&quot;#&#x2F;properties&#x2F;bookings&#x2F;items&#x2F;properties&#x2F;bookingDates&#x2F;properties&#x2F;&lt;strong&gt;checkin&lt;&#x2F;strong&gt;&#x2F;&lt;strong&gt;type&lt;&#x2F;strong&gt;&quot;,&quot;keyword&quot;:&quot;type&quot;,&quot;params&quot;:{&quot;&lt;strong&gt;type&lt;&#x2F;strong&gt;&quot;:&quot;&lt;strong&gt;string&lt;&#x2F;strong&gt;&quot;},&quot;message&quot;:&quot;must be string&quot;}]&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully you can see with that example how this can be a a really quick way to add some checks in place around JSON schema.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;things-to-look-out-for&quot;&gt;Things to Look out For&lt;&#x2F;h2&gt;
&lt;p&gt;The JSON schema comparison isn&#x27;t perfect, and won&#x27;t solve all your problems, but it should help give you some guard rails. Here are a few things to consider as you add these assertions to your project.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Depending on your API, if responses allow both values or &lt;code&gt;null&lt;&#x2F;code&gt; this can cause issues. for example let&#x27;s say you create your schema for a GET all rooms, while only 1 room exists, but in reality there may be 10-15 rooms that have been created by your automation, that may have a null value (which may be allowed). Your snapshot doesn&#x27;t match reality and your test will fail. If you create a new snapshot with the 10-15 rooms that had different data, that may be a workaround. That or updating the snapshot manually.&lt;&#x2F;li&gt;
&lt;li&gt;When new properties are added to your API response body, the snapshot will still pass, and you don&#x27;t have any kind of alert&#x2F;notice to investigate the new property. This may be something that can be overcome by updating the logic, but this is a risk I didn&#x27;t plan on solving with this tool.&lt;&#x2F;li&gt;
&lt;li&gt;If you commit the &lt;code&gt;createSchema=true&lt;&#x2F;code&gt; to your repo, your test isn&#x27;t checking against the recorded snapshot, and it&#x27;s in a bad state.  Luckily there is a workaround for that!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;add-an-esling-rule&quot;&gt;Add an ESLing rule&lt;&#x2F;h2&gt;
&lt;p&gt;If you followed the previous article we configured our project with ESLint and Husky for pre-commit hooks, if any of the linting from ESLint fails it will block you committing the code on your local machine. Knowing this I wanted to create a safeguard to prevent me from committing the &lt;code&gt;createSchema=true&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In the below code snippet I&#x27;ve added a new custom rule for &lt;code&gt;no-restricted-syntax&lt;&#x2F;code&gt;. If you attempt to commit &lt;code&gt;validateJsonSchema(*,*,*,true)&lt;&#x2F;code&gt; the linter will block your commit and alert you to your mistake. Now I won&#x27;t make that same mistake twice!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; .eslintrc.cjs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* eslint-env node *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;exports &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  extends: [
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;eslint:recommended&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;plugin:@typescript-eslint&#x2F;recommended&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;plugin:@typescript-eslint&#x2F;stylistic&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;prettier&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;  parser: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@typescript-eslint&#x2F;parser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  plugins: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@typescript-eslint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;  root: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  rules: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;no-console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;no-restricted-syntax&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      {
&lt;&#x2F;span&gt;&lt;span&gt;        selector: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;CallExpression[callee.property.name=&amp;#39;only&amp;#39;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        message: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;We don&amp;#39;t want to leave .only on our tests😱&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;      {
&lt;&#x2F;span&gt;&lt;span&gt;        selector:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;CallExpression[callee.name=&amp;#39;validateJsonSchema&amp;#39;][arguments.length!=3]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        message: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;We don&amp;#39;t want to commit validateJsonSchema(*,*,*,true)😎&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    ],
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now if I attempt to commit with a &lt;code&gt;true&lt;&#x2F;code&gt; in &lt;code&gt;validateJsonSchema()&lt;&#x2F;code&gt; I will get an error shown below. This should help prevent mistakes!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-27.png&quot; alt=&quot;Image 11&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;update-your-readme-md&quot;&gt;Update your README.md&lt;&#x2F;h2&gt;
&lt;p&gt;Be a good steward, and document how you expect the function to be used within your repo. My readme example is below.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;json-schema&quot;&gt;Json Schema&lt;&#x2F;h3&gt;
&lt;p&gt;We generate json schemas with a &lt;code&gt;POST, PUT, PATCH and GET&lt;&#x2F;code&gt; test but not with a delete. To generate a json schema. An example of a test that generates a schema is below. It&#x27;s best to follow the similar naming conventions&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Creates a snapshot of the schema and save to .api&#x2F;booking&#x2F;POST_booking_schema.json
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST_booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Asserts that the body matches the snapshot found at .api&#x2F;booking&#x2F;POST_booking_schema.json
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST_booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Example of how this is used in a test:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validateJsonSchema &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;validateJsonSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F; POST requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST new booking with full body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;201&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST_booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;updating-a-test-to-not-be-flaky&quot;&gt;Updating a test to not be flaky&lt;&#x2F;h2&gt;
&lt;p&gt;The spec that was flaky was the &lt;code&gt;tests&#x2F;booking&#x2F;booking.put.spec.ts&lt;&#x2F;code&gt; specifically the first test. The main issue is we hard coded the &lt;code&gt;roomid&lt;&#x2F;code&gt; as 1, which is a room that is a part of the seed data. The problem with this is to be able to make a &lt;code&gt;PUT&lt;&#x2F;code&gt; request to a booking we have to create a booking. When using &lt;code&gt;roomid&lt;&#x2F;code&gt; as 1, there are other tests that use &lt;code&gt;roomid&lt;&#x2F;code&gt; 1 creating data, so when we attempt to get a future booking date, there is another tests getting a similar booking date, and I would get a &lt;code&gt;409&lt;&#x2F;code&gt; response letting me know there was a conflict, another booking was already booked for that time slot. With the built in retries in Playwright it would pass the 2nd time it ran most of the time, but we can easily refactor it to not be flaky at all.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;booking&#x2F;booking.put.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: PUT &#x2F;booking&#x2F;{id}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingById&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureOpenCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createFutureBooking&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@datafactory&#x2F;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;stringDateByDays &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createInvalidHeaders &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRoom &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@datafactory&#x2F;room&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validateJsonSchema &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;validateJsonSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;{id} PUT requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;invalidHeader&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;room&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Happy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;McPathy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testy@mcpathyson.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;phone &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;5555555555555&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureBooking&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;invalidHeader &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createInvalidHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;room &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createRoom&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Flaky&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;67&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;room&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureBooking &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createFutureBooking&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureBooking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;futureOpenCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT booking with specific room id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      bookingid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      roomid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      firstname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      lastname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      depositpaid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      phone: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;phone&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      bookingdates: {
&lt;&#x2F;span&gt;&lt;span&gt;        checkin: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;        checkout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;validateJsonSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT_booking_id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Verify booking was updated&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getBookingById&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingDates &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What was really nice about this change is I only had to add&#x2F;update 4 lines of code.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;import{createRoom}from&quot;@datafactory&#x2F;room&quot;&lt;&#x2F;code&gt; importing the createRoom datafactory&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;let room;&lt;&#x2F;code&gt; and updated &lt;code&gt;let roomId;&lt;&#x2F;code&gt; within the describe block&lt;&#x2F;li&gt;
&lt;li&gt;I updated the beforeEach block to have&lt;code&gt;room = await createRoom(&quot;Flaky&quot;, 67);&lt;&#x2F;code&gt; and then &lt;code&gt;roomId = room.roomid;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Once this was updated, this spec is no longer flaky, because we are creating a room specifically for this tests, and not relying on static data, which was getting modified. Again, one of the hardest problems in test automation is TEST DATA!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;warning-message-functionality&quot;&gt;Warning Message Functionality&lt;&#x2F;h2&gt;
&lt;p&gt;I won&#x27;t cover this too much as there isn&#x27;t quite a use case for this yet, but I did add the ability to create and send warning messages from within your tests, that can print out in the console after all your tests run.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-23.png&quot; alt=&quot;Image 12&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Below is an example how you could call this within a test. I&#x27;ve found this useful when you may want to notify your future self, without failing an actual test.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;await addWarning(&amp;quot;This test should be refactored: &amp;#39;&amp;quot; + test.info().title + &amp;quot;&amp;#39; to use custom assertions&amp;quot;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can dig through the Pull Request and see the changes made with this article including the &lt;code&gt;addWarning()&lt;&#x2F;code&gt; function.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pull&#x2F;11&quot;&gt;Json schema by BMayhew&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;final-notes&quot;&gt;Final Notes&lt;&#x2F;h2&gt;
&lt;p&gt;The way I a checking for JSON schema is 1 of many ways to accomplish this goal. The below article &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;tim-deschryver&#x2F;&quot;&gt;Tim Deschryver&lt;&#x2F;a&gt; who has some great &lt;a href=&quot;https:&#x2F;&#x2F;timdeschryver.dev&#x2F;blog?q=Playwright&quot;&gt;Playwright content&lt;&#x2F;a&gt;, walks us through how to validate using &lt;a href=&quot;https:&#x2F;&#x2F;zod.dev&#x2F;&quot;&gt;zod&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;timdeschryver.dev&#x2F;blog&#x2F;playwright-api-testing-with-zod&quot;&gt;Playwright API testing with zod&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-28.png&quot; alt=&quot;Image 17&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks for reading! If you found this helpful, reach out and let me know on &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; or consider &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;buying me a cup of coffee&lt;&#x2F;a&gt;. If you want more content delivered to you in your inbox subscribe below, and be sure to leave a ❤️ to show some love.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-9-validating-json-schema&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-08-28T12:30:39.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>The Definitive Guide to API Test Automation With Playwright: Part 8 - Adding ESlint, Prettier, and Husky</title>
        <published>2023-08-21T00:00:00+00:00</published>
        <updated>2023-08-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-8-adding-eslint-prettier-and-husky/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-8-adding-eslint-prettier-and-husky/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-8-adding-eslint-prettier-and-husky/">&lt;p&gt;I&#x27;m a big fan of consistency within a codebase. I somewhat care less about the specifics as long as it&#x27;s the same. Today I&#x27;ll cover extra tooling that are common in the JS&#x2F;TS ecosystem to help solve common errors and consistency. If you&#x27;re just joining us now go check out the &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;introduction post&lt;&#x2F;a&gt; which links to all parts we&#x27;ve covered so far.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;eslint.org&#x2F;&quot;&gt;ESLint&lt;&#x2F;a&gt; - ESLint statically analyzes your code to quickly find problems. It is built into most text editors and you can run ESLint as part of your continuous integration pipeline.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;prettier.io&#x2F;&quot;&gt;Prettier&lt;&#x2F;a&gt; - An opinionated code formatter - By far the biggest reason for adopting Prettier is to stop all the ongoing debates over styles.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;typicode.github.io&#x2F;husky&#x2F;&quot;&gt;Husky&lt;&#x2F;a&gt; - Modern native git hooks made easy&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each of these tools bring something different to the table. In this guide I&#x27;ll be covering how I currently configure these tool in my repos.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;eslint&quot;&gt;ESLint&lt;&#x2F;h2&gt;
&lt;p&gt;For ESLint I will be using a typescript-eslint, following this &lt;a href=&quot;https:&#x2F;&#x2F;typescript-eslint.io&#x2F;getting-started&quot;&gt;guide&lt;&#x2F;a&gt;. This is the command &lt;code&gt;npm install --save-dev @typescript-eslint&#x2F;parser @typescript-eslint&#x2F;eslint-plugin eslint typescript&lt;&#x2F;code&gt;. This will install all the depencies that we need.&lt;&#x2F;p&gt;
&lt;p&gt;Next create a file &lt;code&gt;.eslintrc.cjs&lt;&#x2F;code&gt; at the root of the directory, this will be our config file.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; .eslintrc.cjs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* eslint-env node *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;exports &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  extends: [
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;eslint:recommended&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;plugin:@typescript-eslint&#x2F;recommended&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;plugin:@typescript-eslint&#x2F;stylistic&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;  parser: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@typescript-eslint&#x2F;parser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  plugins: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@typescript-eslint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;  root: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  parserOptions: {
&lt;&#x2F;span&gt;&lt;span&gt;    project: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    tsconfigRootDir: __dirname,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Running &lt;code&gt;npx eslint .&lt;&#x2F;code&gt; will run ESLint and you will more than likely have a lot of errors. This is because all files in the directory are being linted, there are many files that we don&#x27;t care about such as the files in the &lt;code&gt;node_modules&lt;&#x2F;code&gt; folder or our playwright test folders, let&#x27;s make sure those aren&#x27;t in the scope of our lint command by creating an ignore file. Creating a file named &lt;code&gt;.eslintignore&lt;&#x2F;code&gt; at the root directory and add the following text.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; .eslintignore
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Playwright Specific
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;node_modules&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test-results&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;playwright-report
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;summary.json
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# IDE - VSCode
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;.vscode&#x2F;*
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# System Files
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;.DS_Store
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Thumbs.db
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;mode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;full&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;isActive&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;:false&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Docs files
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;*_spec3.json
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now if you run &lt;code&gt;npx eslint .&lt;&#x2F;code&gt; you will notice a much smaller number, and if your code is the exact same as mine, you will have the below output.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-2.png&quot; alt=&quot;Image 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I would highly encourage you to take time and read each new error you come across, and understand the &quot;why&quot; to why the linter isn&#x27;t happy. I&#x27;ve learned a lot about good coding practices from linting my own code over the years in Ruby (&lt;a href=&quot;https:&#x2F;&#x2F;rubocop.org&#x2F;&quot;&gt;RuboCop&lt;&#x2F;a&gt; I still remember our love hate relationship).&lt;&#x2F;p&gt;
&lt;p&gt;Once you have a decent understanding of the changes, you can also utilize ESLint to &quot;fix&quot; our issues if it knows how by passing in the --fix option into the command like so &lt;code&gt;npx eslint . --fix&lt;&#x2F;code&gt;. After doing this I went ahead and ran all the tests to ensure that all the code is still functional, and all tests passed. Now when I run my &lt;code&gt;npx eslint .&lt;&#x2F;code&gt; command I have 15 failures that I need to resolve on my own.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-3.png&quot; alt=&quot;Image 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I took 5 minutes and was able to update all but 1 of the issues in my files. The specific error was &#x27;createAssertions&#x27; is defined but never used. This is an import statement, where I have &lt;code&gt;createHeaders()&lt;&#x2F;code&gt; commented out in my code below. I want to keep the import there, and have ESLint ignore that line of code. To accomplish this I add a comment to the line that I want ignored &lt;code&gt;&#x2F;&#x2F; eslint-disable-line&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;room&#x2F;room.get.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;room&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;room&#x2F;{id}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRoom&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultRoom &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@datafactory&#x2F;room&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createAssertions &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;createAssertions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; eslint-disable-line
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;room&#x2F; GET requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;room&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;..
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now when re-running the linter I have everything passing! Go ahead and take a few minutes and read through all the other ways you can disable ESLint at the below site. The below is a great resource worth bookmarking.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;learn.coderslang.com&#x2F;favicon.png&quot;&gt;How to disable ESLint for some lines, files or folders ESLint&lt;&#x2F;a&gt;](https:&#x2F;&#x2F;learn.coderslang.com&#x2F;0023-eslint-disable-for-specific-lines-files-and-folders&#x2F;)&lt;&#x2F;p&gt;
&lt;p&gt;Now onto prettier, what we will be using for our formatting&#x2F;styling needs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;prettier&quot;&gt;Prettier&lt;&#x2F;h2&gt;
&lt;p&gt;For Prettier we will continue to use the typescript-eslint guide linked below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;typescript-eslint.io&#x2F;linting&#x2F;troubleshooting&#x2F;formatting&quot;&gt;What About Formatting?&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Before we install we need to install this package eslint-config-prettier. This will disable settings that may conflict with the prettier plugin.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;npm install --save-dev eslint-config-prettier&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Once installed we will need to update our ESLint config file by adding &lt;code&gt;prettier&lt;&#x2F;code&gt; to the extends array.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; eslintrc.cjs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* eslint-env node *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;exports &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  extends: [
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;eslint:recommended&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;plugin:@typescript-eslint&#x2F;recommended&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;plugin:@typescript-eslint&#x2F;stylistic&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;prettier&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;  parser: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@typescript-eslint&#x2F;parser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  plugins: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@typescript-eslint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;  root: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we should install Prettier itself as a node package. The guide I&#x27;m using to install Prettier can be found &lt;a href=&quot;https:&#x2F;&#x2F;prettier.io&#x2F;docs&#x2F;en&#x2F;install&quot;&gt;here&lt;&#x2F;a&gt; on their site. The command to install is &lt;code&gt;npm install --save-dev --save-exact prettier&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now you will still need to create a &lt;code&gt;.prettierrc&lt;&#x2F;code&gt; file in your projects root directory. This file will include the configurations you want prettier to use, mine is below. The different options that can be added&#x2F;edited can be found in the docs &lt;a href=&quot;https:&#x2F;&#x2F;prettier.io&#x2F;docs&#x2F;en&#x2F;options&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; .prettierrc
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;semi&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;trailingComma&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;es5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;singleQuote&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;printWidth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;120
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You should also go ahead and create a &lt;code&gt;.prettierignore&lt;&#x2F;code&gt; file that should look the same as your &lt;code&gt;.eslintignore&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; .prettierignore
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Playwright Specific
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;node_modules&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test-results&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;playwright-report
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;summary.json
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# IDE - VSCode
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;.vscode&#x2F;*
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# System Files
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;.DS_Store
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Thumbs.db
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;mode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;full&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;isActive&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;:false&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# Docs files
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;*_spec3.json
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have that in place let&#x27;s run the prettier command and see the results. &lt;code&gt;npx prettier . --check&lt;&#x2F;code&gt;. When I ran this command this was my output.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-5.png&quot; alt=&quot;Image 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To go ahead and apply the changes prettier wants to make, you can run the command &lt;code&gt;npx prettier . --write&lt;&#x2F;code&gt;. A full list of CLI commands can be found in their &lt;a href=&quot;https:&#x2F;&#x2F;prettier.io&#x2F;docs&#x2F;en&#x2F;cli&quot;&gt;docs&lt;&#x2F;a&gt;. Once the files are updated, I will go ahead and commit and push them to my working branch. At this point, we have a working ESLint command and working prettier command. Now how can we force ourselves, teams, and collaborators to use this tools without thinking about it?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;husky&quot;&gt;Husky&lt;&#x2F;h2&gt;
&lt;p&gt;We will utilize a package called &lt;a href=&quot;https:&#x2F;&#x2F;typicode.github.io&#x2F;husky&#x2F;&quot;&gt;Husky&lt;&#x2F;a&gt; to help us create and manage our local git pre-commit hooks. This will allow us to insert commands to run our tooling before we commit code to our local branch every single time. I&#x27;ll be following this &lt;a href=&quot;https:&#x2F;&#x2F;typicode.github.io&#x2F;husky&#x2F;getting-started.html&quot;&gt;guide&lt;&#x2F;a&gt;. The first command that needs to be run &lt;code&gt;npx husky-init &amp;amp;&amp;amp; npm install&lt;&#x2F;code&gt;. This command will:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;prepare&lt;&#x2F;code&gt; script to &lt;code&gt;package.json&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Create a sample &lt;code&gt;pre-commit&lt;&#x2F;code&gt; hook that you can edit in the directory .husky&#x2F;pre-commit&lt;&#x2F;li&gt;
&lt;li&gt;Configure Git hooks path&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-6.png&quot; alt=&quot;Image 8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The default file is above, We will modify this file to run the commands that we want to run as a part of a pre-commit. Before we just add the long commands that were listed in this article, we should go ahead and create some scripts in our &lt;code&gt;package.json&lt;&#x2F;code&gt; file to make things simpler. In the scripts section let&#x27;s add a scripts for the following. This will allow us to run &lt;code&gt;npm run lint&lt;&#x2F;code&gt; and have all our files linted.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; package.json
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;....
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test --ui&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;eslint .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lint:fix&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;eslint . --fix&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;prettier&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;prettier . --check&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;prettier:fix&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;prettier . --write&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;prepare&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;husky install&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt; ....
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s go back to our &lt;code&gt;.husky&#x2F;pre-commit&lt;&#x2F;code&gt; file and modify the file to include 2 of the scripts we just created.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; .husky&#x2F;pre-commit
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;#!&#x2F;usr&#x2F;bin&#x2F;env sh
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;. &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dirname&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt; -- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;)&#x2F;_&#x2F;husky.sh&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; run lint &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; run prettier
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we should be able to add our files, and attempt to commit our code and our commands should run by default. Below is an example of the checks running and everything passing with no issue, which allowed the commit to be created.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-7.png&quot; alt=&quot;Image 9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes though you will try and commit code that will fail the linter, this is what the error will look like, when this happens. This will block your commit from committing, and force you to fix the issue before proceeding. The nice thing is we just created a command that should fix the lint issues if is automatically fixable &lt;code&gt;npm run lint:fix&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-8.png&quot; alt=&quot;Image 10&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;pre-commit ESLint failure&lt;&#x2F;p&gt;
&lt;p&gt;Similarly when you attempt to commit code that doesn&#x27;t meet the prettier formatting, you will receive an error that matches the below, and your commit will not be committed. You must update the file(s) in question with the correct formatting, this can be achieved by &lt;code&gt;npm run prettier:fix&lt;&#x2F;code&gt; to run the custom script we created.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-10.png&quot; alt=&quot;Image 11&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;pre-commit prettier failure&lt;&#x2F;p&gt;
&lt;p&gt;If you ever run into a situation where you want to break the rules and force a commit, you can add a &lt;code&gt;--no-verify&lt;&#x2F;code&gt; option to the end of your git commit message. Ex: &lt;code&gt;git commit -m &quot;forcing the commit&quot; --no-verify&lt;&#x2F;code&gt;. This is not something that should be done often if even at all, but this will allow you to bypass the checks if needed. If you force the commit more than likely other folks that work in your repo will be faced with the same error when they pull down the changes that caused the error. USE WISELY....&lt;&#x2F;p&gt;
&lt;h2 id=&quot;update-your-readme&quot;&gt;Update your Readme&lt;&#x2F;h2&gt;
&lt;p&gt;One thing that irks me when working in a new repo is not having all the information I need to get started in some sort of documentation. Below is an example of what I&#x27;ve added to my readme, so everyone can get up to speed in contributing with pre-commit hooks after cloning the repo down.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;&#x2F;&#x2F; README.md
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;## Contributing to playwright-api-test-demo
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;### Husky, ESLint, and Prettier
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;We use a mix of [Husky](https:&#x2F;&#x2F;github.com&#x2F;typicode&#x2F;husky), [ESLint](https:&#x2F;&#x2F;eslint.org&#x2F;) and [Prettier](https:&#x2F;&#x2F;prettier.io&#x2F;) within our repository to help enforce consistent coding practices. Husky is a tool that will install a pre-commit hook to run the linter any time before you attempt to make a commit. This replaces the old pre-commit hook that was used before. to install the pre-commit hook you will need to run
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;```bash
&lt;&#x2F;span&gt;&lt;span&gt;npm run prepare
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;npx husky install
&lt;&#x2F;span&gt;&lt;span&gt;```
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;You shouldn&amp;#39;t have to run this command but for reference, the command used to generate the pre-commit file was
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;```bash
&lt;&#x2F;span&gt;&lt;span&gt;npx husky add .husky&#x2F;pre-commit &amp;quot;npm run lint &amp;amp;&amp;amp; npm run prettier&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;```
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;You are still able to bypass the commit hook by passing in --no-verify to your git commit message if needed.
&lt;&#x2F;span&gt;&lt;span&gt;ESLint is a popular javascript&#x2F;typescript linting tool. The configuration for ESLint can be found in `.eslintrc.cjs` file. Prettier, helps with styling (spaces, tabs, quotes, etc), config found in `.prettierrc`.
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;## Extending ESLint to account for custom errors
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;One of my big problems that I used to run into a lot before ESLint was introduced into my life, was accidentally committing `.only` into my test files. I would use this syntax `test.only()` to execute&#x2F;debug a single test quickly from the command line. When the `.only` would get committed it would only run that 1 test as a part of our CI and cause a lot of back and forth. To solve this I created a custom error rule and added it to my `eslintrc.cjs`. See the rules section specifically `no-restricted-syntax`. I set the message as error, and followed the docs to create a matcher that would flag this issue and block commits with a custom message.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;```javascript
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&#x2F; eslintrc.cjs
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;* eslint-env node *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;module.exports = {
&lt;&#x2F;span&gt;&lt;span&gt;  extends: [
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;eslint:recommended&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;plugin:@typescript-eslint&#x2F;recommended&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;plugin:@typescript-eslint&#x2F;stylistic&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;prettier&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;  parser: &amp;quot;@typescript-eslint&#x2F;parser&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;  plugins: [&amp;quot;@typescript-eslint&amp;quot;],
&lt;&#x2F;span&gt;&lt;span&gt;  root: true,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;rules&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;no-console&amp;quot;: 0,
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;no-restricted-syntax&amp;quot;: [
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;error&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;      {
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;selector&amp;quot;: &amp;quot;CallExpression[callee.property.name=&amp;#39;only&amp;#39;]&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;message&amp;quot;: &amp;quot;We don&amp;#39;t want to leave .only on our tests😱&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    ],
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;```
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;![Image 12](https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-11.png)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;The sky is the limit on what you can do with this knowledge!
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;## Other Resources
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;I personally don&amp;#39;t use the `eslint-plugin-playwright` but it can easily be added to the ESLint configuration and setup you just created for your repo. Start by Installing `npm install -D eslint-plugin-playwright` and updating `.eslintrc.cjs` to include `&amp;quot;plugin:playwright&#x2F;recommended&amp;quot;` in the extends array you can have access to some specific Playwright rules.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[eslint-plugin-playwright ESLint](https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;eslint-plugin-playwright?activeTab=readme)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;### VS Code Extensions
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;These VS code extensions are helpful as they will give you feedback
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[ESLint](https:&#x2F;&#x2F;marketplace.visualstudio.com&#x2F;items?itemName=dbaeumer.vscode-eslint)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[Prettier](https:&#x2F;&#x2F;marketplace.visualstudio.com&#x2F;items?itemName=esbenp.prettier-vscode)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;### VS Code Settings file
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;The last recommendation that I enjoy enabling for my repos is the auto format&#x2F;auto save. Below is my setting file which also includes some eslint settings as well that I found [here](https:&#x2F;&#x2F;www.aleksandrhovhannisyan.com&#x2F;blog&#x2F;format-code-on-save-vs-code-eslint&#x2F;#automatically-formatting-code-on-save).
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;```
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&#x2F; .vscode&#x2F;settings.json
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;eslint.validate&amp;quot;: [&amp;quot;javascript&amp;quot;, &amp;quot;typescript&amp;quot;],
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;editor.codeActionsOnSave&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;source.fixAll.eslint&amp;quot;: true
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;editor.formatOnSave&amp;quot;: true,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;editor.defaultFormatter&amp;quot;: &amp;quot;esbenp.prettier-vscode&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;```
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;You should be all set and good to go with all the changes, you now can have consistency within your files, and an extra set of checks that will help guard you from pushing bad code to your repository!
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;All the code I added is available in this repo.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[GitHub - playwrightsolutions&#x2F;playwright-api-test-demo: This repository will serve as a place where I add API test Automation checks for articles written at https:&#x2F;&#x2F;playwrightsolutions.com This repository will serve as a place where I add API test Automation checks for articles written at https:&#x2F;&#x2F;playwrightsolutions.com - GitHub - playwrightsolutions&#x2F;playwright-api-test-demo: This re... ![Image 19](https:&#x2F;&#x2F;github.com&#x2F;fluidicon.png)GitHubplaywrightsolutions ![Image 20](https:&#x2F;&#x2F;opengraph.githubassets.com&#x2F;8987a9e40cd893d5385debe614ac508b935822c9bee5a97f4bcf8885d14c8cda&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo)](https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;![Image 21](https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-12.png)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;---
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Thanks for reading! If you found this helpful, reach out and let me know on [LinkedIn](https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew) or consider [buying me a cup of coffee](https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew). If you want more content delivered to you in your inbox subscribe below, and be sure to leave a ❤️ to show some love.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-8-adding-eslint-prettier-and-husky&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-08-21T12:30:22.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何使用组件来重构playwright的代码</title>
        <published>2023-08-20T00:00:00+00:00</published>
        <updated>2023-08-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-component-demo/"/>
        <id>https://playwright.itest.info/blog/playwright-component-demo/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-component-demo/">&lt;h2 id=&quot;playwright-component-demo&quot;&gt;playwright-component-demo&lt;&#x2F;h2&gt;
&lt;p&gt;基于组件模型重写的 Playwright TodoMVC 应用演示测试测试用例&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dong-ji&quot;&gt;动机&lt;&#x2F;h2&gt;
&lt;p&gt;页面对象模型（pom）是组织测试代码的推荐方法。然而,对于大型应用来说,许多元素在不同页面间保持不变,如导航栏、页眉和页脚。这不仅涉及相同的部分,还包括可复用的部分。&lt;&#x2F;p&gt;
&lt;p&gt;以表格为例。通常应用会有一个用于渲染表格内部组件,并在各处使用。因此,编写涉及表格的测试时,所需的逻辑基本上是一样的。许多情况下,应用还会有基于表格组件的复合组件,添加特定逻辑,如带搜索功能的用户表格或可排序的发票表格。&lt;&#x2F;p&gt;
&lt;p&gt;这种可可复用性和可组合性使得基于组件的模型成 web 前端开发的最佳方式。那么,为什么不在编写自动化测试用例时采用同样的方法呢?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-xian&quot;&gt;实现&lt;&#x2F;h2&gt;
&lt;p&gt;我们的核心思想是创建一个中间层的组件来抽象具体的 web 应用,所有测试都基于这些组件。TodoMVC 演示应用的组件可以在&lt;code&gt;tests&#x2F;components&lt;&#x2F;code&gt;文件夹中找到。&lt;&#x2F;p&gt;
&lt;p&gt;一般情况下模版是这样的:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;Component &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;parent&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;options &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;parent&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 组件根节点的定位器
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;child &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 过于简单而不需要单独成为组件的子元素
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;otherComponent &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;OtherComponent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 子组件
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 组件的操作隐藏了内部细节,不暴露具体使用哪个子元素或组件
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;someAction &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;child&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;anotherAction &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;param&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;otherComponent&lt;&#x2F;span&gt;&lt;span&gt;({ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;param &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 导出具体的API接口:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;someAction&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;anotherAction&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 复制Playwright的所有标准断言
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveCustomCondition&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;child&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveClass&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 自定义断言以隐藏内部细节
&lt;&#x2F;span&gt;&lt;span&gt;   })
&lt;&#x2F;span&gt;&lt;span&gt; };
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在测试用例中的使用示例:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;component &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;Component&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;component&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;component&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;someAction&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;component&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveCustomCondition&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;dui-bi&quot;&gt;对比&lt;&#x2F;h2&gt;
&lt;p&gt;以下是原始测试和更新后测试的几个对比:&lt;&#x2F;p&gt;
&lt;h4 id=&quot;shi-li-1&quot;&gt;示例 1&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;应该允许我将所有项目标记为已完成&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 完成所有待办事项
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;全部标记为完成&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 确保所有待办事项都有&amp;#39;completed&amp;#39;类
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;todo-item&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveClass&lt;&#x2F;span&gt;&lt;span&gt;([
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;completed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;completed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;completed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  ]);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;checkNumberOfCompletedTodosInLocalStorage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;对比&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;应该允许我将所有项目标记为已完成&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 完成所有待办事项
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;Header&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;completeAll&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 确保所有待办事项都有&amp;#39;completed&amp;#39;类
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;TodoList&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveAllCompleted&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;checkNumberOfCompletedTodosInLocalStorage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;shi-li-2&quot;&gt;示例 2&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;当没有已完成的项目时应该隐藏&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.todo-list li .toggle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;清除已完成&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;清除已完成&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeHidden&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;对比&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;当没有已完成的项目时应该隐藏&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;TodoList&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;todoAt&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;complete&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;footer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;Footer&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;footer&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;clearCompleted&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;footer&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toAllowClearCompleted&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;shi-li-3&quot;&gt;示例 3&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;应该高亮当前应用的过滤器&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;全部&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveClass&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;selected&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;活动&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 页面变化 - 活动项目
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;活动&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveClass&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;selected&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;已完成&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 页面变化 - 已完成项目
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;已完成&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveClass&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;selected&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;对比&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;应该高亮当前应用的过滤器&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;footer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;Footer&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;footer&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;全部&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeSelected&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;footer&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;selectLink&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;活动&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 页面变化 - 活动项目
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;footer&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;活动&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeSelected&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;footer&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;selectLink&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;已完成&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 页面变化 - 已完成项目
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;footer&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;已完成&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeSelected&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;github.com&#x2F;vangelov&#x2F;playwright-component-demo&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>The Definitive Guide to API Test Automation With Playwright: Part 7 - Creating Helper Functions for Common tasks</title>
        <published>2023-08-14T00:00:00+00:00</published>
        <updated>2023-08-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-7-creating-helper-functions-for-common-tasks/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-7-creating-helper-functions-for-common-tasks/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-7-creating-helper-functions-for-common-tasks/">&lt;p&gt;Welcome back as we continue our series on API Test Automation with Playwright! Today I&#x27;ll walk through the changes made in the pull request, which include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Helper Functions&lt;&#x2F;li&gt;
&lt;li&gt;Create Assertion Helper&lt;&#x2F;li&gt;
&lt;li&gt;Update to Absolute paths instead of Relative paths&lt;&#x2F;li&gt;
&lt;li&gt;branding, message, report, and room endpoint coverage to 100%&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you&#x27;re just joining us now go check out the &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;introduction post&lt;&#x2F;a&gt; which links to all parts we&#x27;ve covered so far. The pull request with comments and code changes is listed below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pull&#x2F;9&quot;&gt;adding additional coverage by BMayhew · Pull Request #9&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;helper-functions&quot;&gt;Helper Functions&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;branding-helper&quot;&gt;Branding helper&lt;&#x2F;h3&gt;
&lt;p&gt;There are two main helper functions that I created during my coding session. The first was &lt;code&gt;lib&#x2F;helpers&#x2F;branding.ts&lt;&#x2F;code&gt; which is just a variable export for a 2 different json objects to help with the branding api test. The below spec file found below handles both the GET and PUT api requests. The branding api handles what brand the website is using, for example name of the business, phone number, address, email, welcome photo, latitude, longitude, etc. Changing the branding will change the way the site renders, which is something to consider when it comes to the state of the website&#x2F;api. If there are other tests that are expecting this branding information (which we will change with our &lt;code&gt;PUT&lt;&#x2F;code&gt; request) those tests will more than likely fail.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-13.png&quot; alt=&quot;Image 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With this context in mind, I made a choice to but both the &lt;code&gt;GET&lt;&#x2F;code&gt; and &lt;code&gt;PUT&lt;&#x2F;code&gt; tests within the same test file. With my current configuration, they will not run at the same time, even if I am running my tests in parallel. Notice I am importing &lt;code&gt;defaultBranding&lt;&#x2F;code&gt; and &lt;code&gt;updatedBranding&lt;&#x2F;code&gt; from the branding helper. One other thing to note with the &lt;code&gt;PUT&lt;&#x2F;code&gt; spec, I am utilizing an &lt;code&gt;afterEach&lt;&#x2F;code&gt; block where I update the branding to the default setting.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;branding&#x2F;branding.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;branding&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: PUT &#x2F;branding&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBranding&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updatedBranding &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&#x2F; GET requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBranding&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET website branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBody&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; This test has the potential to cause other UI tests to fail as this branding endpoint is a critical part of the entire UI of the website
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&#x2F; PUT requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBranding&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updatedBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updatedBranding&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;afterEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT website branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updatedBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;202&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updatedBody&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For the helper file, I am keeping this very simple, I am exporting two variables as JSON objects that I can use for the api request body in my tests. I decided to hard code these values as there didn&#x27;t seem like a lot of value to randomize the inputs here, as the branding information is likely not to change often once it&#x27;s set.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;helpers&#x2F;branding.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultBranding &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Shady Meadows B&amp;amp;B&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  map: {
&lt;&#x2F;span&gt;&lt;span&gt;    latitude: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;52.6351204&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    longitude: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1.2733774&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  logoUrl: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.mwtestconsultancy.co.uk&#x2F;img&#x2F;rbp-logo.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  description:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Welcome to Shady Meadows, a delightful Bed &amp;amp; Breakfast nestled in the hills on Newingtonfordburyshire. A place so beautiful you will never want to leave. All our rooms have comfortable beds and we provide breakfast from the locally sourced supermarket. It is a delightful place.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  contact: {
&lt;&#x2F;span&gt;&lt;span&gt;    name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Shady Meadows B&amp;amp;B&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    address: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;The Old Farmhouse, Shady Street, Newfordburyshire, NE1 410S&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    phone: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;012345678901&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;fake@fakeemail.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updatedBranding &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Test Name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  map: {
&lt;&#x2F;span&gt;&lt;span&gt;    latitude: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;41.8781&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    longitude: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;87.6298&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  logoUrl: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;media.tenor.com&#x2F;KaCUHzQxVWcAAAAC&#x2F;house.gif&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  description: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  contact: {
&lt;&#x2F;span&gt;&lt;span&gt;    name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Testy McTester&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    address: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;100 Testing Way&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    phone: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;5555555555&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testy@testymtesterface.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;room-features-helper&quot;&gt;Room features helper&lt;&#x2F;h3&gt;
&lt;p&gt;The rooms helper we have built has a bit more functionality. First off we do have a variable named &lt;code&gt;roomFeatures&lt;&#x2F;code&gt; which is an array of different types of features&#x2F;test data that we will be using, a function that returns all room features, one that returns a random room feature, and one that will return X number of room features you pass into the function. Having these helpers makes it easy to quickly generate random data for our tests and allow me to simplify my datafactory methods.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;helpers&#x2F;roomFeatures.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@faker-js&#x2F;faker&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomFeatures &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;TV&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;WiFi&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Safe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Mini Bar&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Tea&#x2F;Coffee&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Balcony&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Bath&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Shower&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Sea View&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Mountain View&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;City View&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;River View&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Garden View&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Pool View&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Patio&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Terrace&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Air Conditioning&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Heating&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Kitchen&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Dining Area&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Sofa&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Fireplace&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Private Entrance&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Soundproofing&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Wardrobe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Clothes Rack&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Ironing Facilities&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Desk&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Seating Area&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Sofa Bed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;allRoomFeatures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomFeatures&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;randomRoomFeatures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomFeatures&lt;&#x2F;span&gt;&lt;span&gt;[
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;({ min: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, max: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomFeatures&lt;&#x2F;span&gt;&lt;span&gt;.length &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;  ];
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;randomRoomFeaturesCount&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;features &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;randomRoomFeatures&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; This will remove all duplicates from the array
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Array&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span&gt;Set(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;One of the specs that I&#x27;ve updated was the &lt;code&gt;room.put.spec.ts&lt;&#x2F;code&gt;, by adding a 2nd test utilizing the &lt;code&gt;randomRoomFeaturesCount()&lt;&#x2F;code&gt; function. Adding this spec was very easy, before I make a &lt;code&gt;PUT&lt;&#x2F;code&gt; call to the API endpoint, I create a variable &lt;code&gt;randomFeatures&lt;&#x2F;code&gt; with an array of 10 random room features, I then use the existing &lt;code&gt;updateRoomBody&lt;&#x2F;code&gt; (which gets set in the &lt;code&gt;beforeEach&lt;&#x2F;code&gt; block) and overwrite the values for the &lt;code&gt;features&lt;&#x2F;code&gt; with the 10 random room features. Then make the &lt;code&gt;PUT&lt;&#x2F;code&gt; api request, and validate the response of the features is the same as the &lt;code&gt;randomFeatures&lt;&#x2F;code&gt; variable we set earlier.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;room&#x2F;room.put.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: PUT &#x2F;room&#x2F;{id}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRoom&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRandomRoomBody &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@datafactory&#x2F;room&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randomRoomFeaturesCount &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;roomFeatures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;room&#x2F; PUT requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;room&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;authHeaders&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;room &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createRoom&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;50&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;room&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;authHeaders &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createRandomRoomBody&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT &#x2F;room to update values&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;room&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;authHeaders&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;202&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.name).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.name);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessible&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessible&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.description).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.description);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomName&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomName&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.type).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.type);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT &#x2F;room to update features&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randomFeatures &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;randomRoomFeaturesCount&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Overwrites the features array with random features
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;features &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randomFeatures&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;room&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;authHeaders&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;202&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.name).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.name);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessible&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessible&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.description).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.description);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randomFeatures&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomName&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomName&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.type).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;updateRoomBody&lt;&#x2F;span&gt;&lt;span&gt;.type);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I also simplified our &lt;code&gt;room&lt;&#x2F;code&gt; datafactory utilizing &lt;code&gt;randomRoomFeaturesCount()&lt;&#x2F;code&gt; function to create random room features. Now anytime a new room is created using &lt;code&gt;createRandomRoomBody()&lt;&#x2F;code&gt; there will be random features returned. If you follow along with the code you will see that I am actually setting a variable named &lt;code&gt;features&lt;&#x2F;code&gt; to &lt;code&gt;randomRoomFeaturesCount(6)&lt;&#x2F;code&gt;, and replace my hardcoded features list.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-14.png&quot; alt=&quot;Image 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;datafactory&#x2F;room.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@faker-js&#x2F;faker&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randomRoomFeaturesCount &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;roomFeatures&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.URL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createRandomRoomBody&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;roomName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;roomPrice&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomType &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Single&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Double&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Twin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;features &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;randomRoomFeaturesCount&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    roomName: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomName &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;numeric&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    type: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomType&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;Math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;floor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;Math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;random&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomType&lt;&#x2F;span&gt;&lt;span&gt;.length)], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; returns a random value from the array
&lt;&#x2F;span&gt;&lt;span&gt;    accessible: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;Math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;random&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;returns true or false
&lt;&#x2F;span&gt;&lt;span&gt;    image: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;urlLoremFlickr&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;      category: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cat&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      width: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;500&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      height: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;500&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    }),
&lt;&#x2F;span&gt;&lt;span&gt;    description: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;hacker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;phrase&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;    features: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;sort&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.5 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;Math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;random&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;slice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; returns 3 random values from the array
&lt;&#x2F;span&gt;&lt;span&gt;    roomPrice: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomPrice &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;numeric&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomBody&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;createassertions&quot;&gt;createAssertions()&lt;&#x2F;h2&gt;
&lt;p&gt;The next helper to be discussed is &lt;code&gt;createAssertions()&lt;&#x2F;code&gt;. All the credit for this goes to &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;sgapanovich&#x2F;&quot;&gt;Sergei Gapanovich&lt;&#x2F;a&gt; for the JS&#x2F;TS implementation, and &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;joel-black-1344a267&#x2F;&quot;&gt;Joel Black&lt;&#x2F;a&gt;, for the O.G. Ruby implementation which I discussed in a &lt;a href=&quot;https:&#x2F;&#x2F;www.ministryoftesting.com&#x2F;testbash-talks&#x2F;1f8d5688?s_id=15535642&quot;&gt;Ministry of Testing Test.Bash() talk&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-15.png&quot; alt=&quot;Image 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The helper code is below for our Playwright repo as an async function, that requires 2 parameters to be passed, first the body object and second a name for the object, typically &quot;body&quot; that the assertion builder will use.&lt;&#x2F;p&gt;
&lt;p&gt;🌮&lt;&#x2F;p&gt;
&lt;p&gt;Big thanks to &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;pakhilov&#x2F;&quot;&gt;Dmitry Pakhilov&lt;&#x2F;a&gt; for a suggestion to simply the createAssertions.ts file by removing the index loop and using a for loop!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;helper&#x2F;createAssertions.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* eslint-disable @typescript-eslint&#x2F;prefer-for-of *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;*
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;  this function logs in console ready to use expects
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;  example: passing the following object (body) to the function
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;  {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    &amp;quot;one&amp;quot;: 1,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    &amp;quot;two&amp;quot;: &amp;quot;2&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    &amp;quot;three&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;      &amp;quot;four&amp;quot;: [&amp;quot;4&amp;quot;, &amp;quot;cuatro&amp;quot;],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;      &amp;quot;five&amp;quot;: [
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;        {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;          &amp;quot;six&amp;quot;: []
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;        },
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;        {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;          &amp;quot;seven&amp;quot;: null
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;        }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;      ]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;  would generate the following ready to use assertions:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;  expect(body.one).toBe(1);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;  expect(body.two).toBe(&amp;quot;2&amp;quot;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;  expect(body.three.four).toEqual([&amp;quot;4&amp;quot;,&amp;quot;cuatro&amp;quot;]);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;  expect(body.three.five[0].six).toEqual([]);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;  expect(body.three.five[1].seven).toBe(null);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;*&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createAssertions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;paramName &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;expect(${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paramName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}.${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}).toBe(&amp;quot;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;quot;);&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;expect(${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paramName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}.${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}).toBeNull();&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;expect(${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paramName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}.${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}).toBe(${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;});&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Array&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isArray&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;)) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;.length &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;expect(${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paramName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}.${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}).toEqual([]);&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createAssertions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paramName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}.${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newArray &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;item &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&amp;quot;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;` &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;item &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;          );
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;expect(${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paramName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}.${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}).toEqual([${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}]);&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;      } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Object&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;keys&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;).length &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;expect(${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paramName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}.${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}).toEqual({});&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parseInt&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createAssertions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paramName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}[${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createAssertions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paramName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}.${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So let&#x27;s see this in action. I use the reports get spec to demonstrate how this works, the code is below the video walk through. When adding &lt;code&gt;await createAssertions(body, &quot;body&quot;)&lt;&#x2F;code&gt; after we make an API call, we get a console.log message with our assertions. It is not a good idea to copy, paste, commit these assertions blindly, you will need to think through how best to modify the assertions to work for what you want to test for. For the spec below I used the expects to build out a for loop to do basic date validations and string validations as the data returned from this endpoint will never be consistent.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;reports&#x2F;reports.get.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;report&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;report&#x2F;room&#x2F;{id}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createFutureBooking &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@datafactory&#x2F;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRoom &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@datafactory&#x2F;room&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createAssertions &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;createAssertions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isValidDate &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;report&#x2F; GET requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;room&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;room &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createRoom&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createFutureBooking&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;room&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET a report&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;report&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;report&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThanOrEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Leaving this here for demo purposes
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createAssertions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; I am asserting on each booking in the report array
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;report&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;forEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.start)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;end&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.title).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET room report by id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;report&#x2F;room&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;room&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;report&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;report&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].start)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;report&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;end&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;report&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].title).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Unavailable&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is a really useful tool and gets even more useful the larger the Body that is returned.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;updating-to-absolute-paths&quot;&gt;Updating to Absolute paths&lt;&#x2F;h2&gt;
&lt;p&gt;One quality of life change that I made with this pull request was update my imports to use Absolute paths. This is a feature that Typescript offers, and really makes the code look a lot cleaner.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-16.png&quot; alt=&quot;Image 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To enable this functionality I had to create a &lt;code&gt;tsconfig.json&lt;&#x2F;code&gt; file at my root directory, and use the compilerOptions &amp;gt; paths to allow absolute paths.  More details about path mapping can be found within the &lt;a href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;docs&#x2F;handbook&#x2F;module-resolution.html#path-mapping&quot;&gt;docs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tsconfig.json
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;compilerOptions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;baseUrl&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; This must be specified if &amp;quot;paths&amp;quot; is.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@datafactory&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&#x2F;datafactory&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@helpers&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&#x2F;helpers&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;endpoint-coverage-to-100&quot;&gt;Endpoint coverage to 100%&lt;&#x2F;h2&gt;
&lt;p&gt;I was able to add coverage for all the rest of the endpoints. This did include branding, message, report, and room endpoints. The message endpoint also had a datafactory for &lt;code&gt;message&lt;&#x2F;code&gt; as well that is worth checking out on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pulls&quot;&gt;Pull Request&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-17.png&quot; alt=&quot;Image 8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;What&#x27;s nice is if new endpoints get added to the existing areas our coverage percentage will drop and we can take action. If you need a refresher on how we built the endpoint coverage check out &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-5-calculating-endpoint-coverage&#x2F;&quot;&gt;Part 5 of the series&lt;&#x2F;a&gt;. Also keep in mind, the 100% means we have at least 1 test per endpoint, not scenario that should be tested 😁.&lt;&#x2F;p&gt;
&lt;p&gt;The pull request can be found below with all the changes that were made this round.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pull&#x2F;9&quot;&gt;adding additional coverage by BMayhew · Pull Request #9 · playwrightsolutions&#x2F;playwright-api-test-demo Summary by CodeRabbitNew Features: Added new modules for managing messages, room configurations, branding data, and room features.Enhanced booking.ts with updated faker methods for more realisti... &lt;img src=&quot;https:&#x2F;&#x2F;github.com&#x2F;fluidicon.png&quot; alt=&quot;Image 9&quot; &#x2F;&gt;GitHubplaywrightsolutions &lt;img src=&quot;https:&#x2F;&#x2F;opengraph.githubassets.com&#x2F;e92d1ed099daeb413e715cc56c0a7fd79bdaddd010666b77e0b0a5e382746859&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pull&#x2F;9&quot; alt=&quot;Image 10&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;challenge-for-you&quot;&gt;Challenge for you!&lt;&#x2F;h2&gt;
&lt;p&gt;One thing that I realize as I run my suite, there is 1 flaky test that will pass after a single retry, that can be refactored. If you are following along or want to test your skills, pull down the repo in its current state, and see if you can find a way to make the spec less flakey.&lt;&#x2F;p&gt;
&lt;p&gt;In light of reaching 100% coverage!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-18.png&quot; alt=&quot;Image 11&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks for reading! If you found this helpful, reach out and let me know on &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; or consider &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;buying me a cup of coffee&lt;&#x2F;a&gt;. If you want more content delivered to you in your inbox subscribe below, and be sure to leave a ❤️ to show some love.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-7-creating-helper-functions-for-common-tasks&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-08-14T12:30:25.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright 中能否对特定的 spec 文件设置重试次数</title>
        <published>2023-08-07T00:00:00+00:00</published>
        <updated>2023-08-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/is-it-possible-to-override-retry-attempts-on-a-specific-spec-file-in-playwright-expect-poll-expect-topass/"/>
        <id>https://playwright.itest.info/blog/is-it-possible-to-override-retry-attempts-on-a-specific-spec-file-in-playwright-expect-poll-expect-topass/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/is-it-possible-to-override-retry-attempts-on-a-specific-spec-file-in-playwright-expect-poll-expect-topass/">&lt;p&gt;这个问题最近在 Playwright Discord 的 #help-playwright 频道中被提出。如果你还没加入,现在就&lt;a href=&quot;https:&#x2F;&#x2F;discord.gg&#x2F;playwright-807756831384403968&quot;&gt;加入吧&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;今天的解决方案我将使用 &lt;a href=&quot;https:&#x2F;&#x2F;practicesoftwaretesting.com&#x2F;#&#x2F;&quot;&gt;https:&#x2F;&#x2F;practicesoftwaretesting.com&#x2F;&lt;&#x2F;a&gt; 网站作为测试对象。源码的 pull request 可以在下面的链接找到。&lt;&#x2F;p&gt;
&lt;p&gt;主要涉及的主题包括 &lt;code&gt;test.describe.configure({ retries: x })&lt;&#x2F;code&gt;, &lt;code&gt;expect.poll()&lt;&#x2F;code&gt;, 和 &lt;code&gt;expect.toPass()&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-practicesoftwaretesting.com&#x2F;pull&#x2F;2&quot;&gt;Adding Retry Count Logic By Spec by BMayhew&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在深入了解具体解决方案之前,我认为值得问问自己:为什么测试套件中需要重试?许多人会认为,不应该在测试套件中重试,而应该要研究每次失败的用例,要么修复存在的 bug,要么改进不稳定的测试以使其更加稳定。&lt;&#x2F;p&gt;
&lt;p&gt;但实际上,这并非总是可行,具体取决于你的环境。在我目前的环境中,我们确实依赖重试来帮助我们获得正确的结果。这是我们做出的选择和愿意承担风险,对套件中的每个测试进行最多 2 次重试。我们通常会定期审查重试情况,研究测试最初失败的原因,并努力修复测试的逻辑问题。在我的环境中,大多数情况下,不稳定性更多来自于我们在&quot;临时&quot;和&quot;预发布&quot;环境中的机器规格问题。我遇到的许多错误恰好是服务器 502、503,即服务器响应时间过长。对我们来说,这些环境比生产环境小,并不总能优雅地处理我们的自动化测试所产生的所有流量。我发现我们的测试并不是不稳定的,而是我们的机器规格和基础设施不稳定。&lt;&#x2F;p&gt;
&lt;p&gt;话虽如此,我们的自动化检查实际上帮助我们识别了一个负载均衡器计时问题(在 TCP&#x2F;IP 级别),这是由大量的失败而引起的,重新运行后就通过了。我们在深入研究&quot;重试&quot;的测试用例并与 SRE 团队一起回顾后,才发现了应用程序会话超时和负载均衡器健康检查超时之间的配置错误。&lt;&#x2F;p&gt;
&lt;p&gt;说完这些,让我们进入解决方案吧!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;test-describe-configure-retries-x&quot;&gt;test.describe.configure({ retries: x })&lt;&#x2F;h2&gt;
&lt;p&gt;在 &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; 文件中,我将重试次数设置为 &lt;code&gt;2&lt;&#x2F;code&gt;。在特定的测试文件中,我可以在文件开头(在任何 &lt;code&gt;test.describe()&lt;&#x2F;code&gt; 或 &lt;code&gt;test()&lt;&#x2F;code&gt; 块之外)使用 &lt;code&gt;test.describe.configure()&lt;&#x2F;code&gt; 来覆盖重试次数。以下代码将把该文件中的所有测试设置为重试 5 次。这是一种非常直接的方法,但还有其他方法!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;configure&lt;&#x2F;span&gt;&lt;span&gt;({ retries: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5 &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试重试 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;({ storageState: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.auth&#x2F;customer01.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用有效的客户凭据登录并验证品牌页面不可访问&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nav-user-menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;innerText&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Jane Doe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;#&#x2F;admin&#x2F;brands&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;#&#x2F;auth&#x2F;login1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-retries#retries&quot;&gt;官方文档里的描述&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;值得一提的是,你可以通过在 Playwright 测试中使用 &lt;code&gt;expect.poll()&lt;&#x2F;code&gt; 或 &lt;code&gt;expect.toPass()&lt;&#x2F;code&gt; 功能来解决许多与测试重试相同的问题。让我们看看下面每种方法的例子。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;expect-poll&quot;&gt;expect.poll()&lt;&#x2F;h2&gt;
&lt;p&gt;expect.poll() 是一个很有用的工具，它可以让我们重复检查某个条件是否满足，直到成功或超时。想象你在等一个网页加载，但不知道具体要多久。你可能会不断刷新页面，直到看到你想要的内容。expect.poll() 就是在自动帮你做这件事。&lt;&#x2F;p&gt;
&lt;p&gt;在下面的例子中，我们设置了 20 秒的等待时间。代码会不断尝试，看是否能找到包含 &quot;&#x2F;#&#x2F;auth&#x2F;login1&quot; 的内容。如果找到了，就立即停止；如果 20 秒内都没找到，就会报告失败。&lt;&#x2F;p&gt;
&lt;p&gt;这个方法特别适合处理那些需要一定时间才能完成的操作。比如，在我的一个项目中，我们首先让浏览器打开一个网页，然后等待它可能发生的跳转。我们不断检查当前的网址，直到它变成我们期望的地址。这样，即使网页经过了几次跳转，我们最终也能确认它到达了正确的位置。&lt;&#x2F;p&gt;
&lt;p&gt;不过要注意，如果在这个等待过程中，我们设置的任何检查条件失败了（比如找不到某个元素），整个测试就会立即失败，不会继续等待了。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试轮询 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;({ storageState: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.auth&#x2F;customer01.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用有效的客户凭据登录并验证品牌页面不可访问&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nav-user-menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;innerText&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Jane Doe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;poll&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;#&#x2F;admin&#x2F;brands&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果下面的断言失败,测试就失败
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        },
&lt;&#x2F;span&gt;&lt;span&gt;        {
&lt;&#x2F;span&gt;&lt;span&gt;          timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;20_000&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;      )
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;#&#x2F;auth&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions#expectpoll&quot;&gt;官方文档的描述&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;expect-topass&quot;&gt;expect.toPass()&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;toPass()&lt;&#x2F;code&gt; 方法和前面说的轮询方法很像，但它有个很重要的不同点。想象你在做一道复杂的数学题，里面有好几个步骤。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;使用 &lt;code&gt;expect.poll()&lt;&#x2F;code&gt; 就像你在检查最后一个步骤是否正确。如果最后一步错了，整个题就算做错了。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;而使用 &lt;code&gt;toPass()&lt;&#x2F;code&gt; 就像老师给你多次机会重做整道题。如果任何一个步骤做错了，你就要从头再来，直到所有步骤都做对，或者直到用完了所有的重做机会（也就是到达了设定的超时时间）。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;具体到代码中：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;在 &lt;code&gt;await expect(async()....&lt;&#x2F;code&gt; 这个代码块里，可能有多个检查点（就像数学题的多个步骤）。&lt;&#x2F;li&gt;
&lt;li&gt;如果其中任何一个检查失败了，&lt;code&gt;toPass()&lt;&#x2F;code&gt; 会让整个代码块重新运行，从头再来。&lt;&#x2F;li&gt;
&lt;li&gt;这个过程会一直重复，直到所有的检查都通过，或者超过了设定的时间限制。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这就是 &lt;code&gt;expect.poll()&lt;&#x2F;code&gt; 和 &lt;code&gt;expect.toPass()&lt;&#x2F;code&gt; 最大的区别：&lt;code&gt;toPass()&lt;&#x2F;code&gt; 在失败时会重试整个过程，而不仅仅是最后一步。这对于处理那些需要多个步骤都成功的复杂操作特别有用。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试 toPass &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;({ storageState: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.auth&#x2F;customer01.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用有效的客户凭据登录并验证品牌页面不可访问&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nav-user-menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;innerText&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Jane Doe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      );
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;#&#x2F;admin&#x2F;brands&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果下面的断言失败,整个块将被重试
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;#&#x2F;auth&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toPass&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;      timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10_000&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions#expecttopass&quot;&gt;官方文档&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;反复运行测试而不分析原因是很危险的。想象一下，你在做一道难题，一直得不到正确答案。如果你只是不停地重做，而不去思考为什么错了，可能永远也解不开这道题。&lt;&#x2F;p&gt;
&lt;p&gt;我们进行自动化测试的真正目的是什么呢？就像是给开发团队一个&quot;安全网&quot;。每次他们修改代码后，我们都要确保：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;新的改动没有把原本正常工作的功能弄坏。&lt;&#x2F;li&gt;
&lt;li&gt;整个应用程序仍然运行良好，没有变得更糟糕。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;如果测试能稳定地通过，它就像是给了我们一个&quot;绿灯&quot;，让我们有信心说：&quot;好的，这次的改动没问题，我们可以放心地把新版本发布出去了。&quot;&lt;&#x2F;p&gt;
&lt;p&gt;所以，与其盲目地重复测试，不如仔细分析每次失败的原因，这样才能真正提高软件的质量和可靠性。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image.png&quot; alt=&quot;图片 9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读!如果你觉得这篇文章有帮助,请在 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; 上联系我,或考虑&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;给我买杯咖啡&lt;&#x2F;a&gt;。如果你想在收件箱中收到更多内容,请在下方订阅,别忘了留下一个 ❤️ 表示你的喜爱。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-override-retry-attempts-on-a-specific-spec-file-in-playwright-expect-poll-expect-topass&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-08-07&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>为什么我从 Cypress 转向了 Playwright </title>
        <published>2023-08-02T00:00:00+00:00</published>
        <updated>2023-08-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/why-i-switched-from-cypress-to-playwright/"/>
        <id>https://playwright.itest.info/blog/why-i-switched-from-cypress-to-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/why-i-switched-from-cypress-to-playwright/">&lt;p&gt;我从 2019 年开始使用 &lt;a href=&quot;https:&#x2F;&#x2F;www.cypress.io&#x2F;&quot;&gt;Cypress&lt;&#x2F;a&gt;,当时我所在的公司决定在新项目中放弃 &lt;a href=&quot;https:&#x2F;&#x2F;www.protractortest.org&#x2F;&quot;&gt;Protractor&lt;&#x2F;a&gt;。那时我使用的框架是 &lt;a href=&quot;https:&#x2F;&#x2F;angular.io&#x2F;&quot;&gt;Angular&lt;&#x2F;a&gt;,我有机会实施 Cypress 概念验证。最近,我换了工作,现在使用 &lt;a href=&quot;https:&#x2F;&#x2F;react.dev&#x2F;&quot;&gt;React&lt;&#x2F;a&gt;,在那里我也有机会实施 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;&quot;&gt;Playwright&lt;&#x2F;a&gt; 概念验证。&lt;&#x2F;p&gt;
&lt;p&gt;在 Angular 和 React 方面都有经验后,我偏好使用 &lt;code&gt;data-testid&lt;&#x2F;code&gt; 属性进行测试。这让我能够保持一致的 UI 端到端测试方法,我没有观察到 Angular 和 React 应用程序测试之间有任何显著差异。&lt;&#x2F;p&gt;
&lt;p&gt;注意:本文中我使用 React 应用作为 Cypress 和 Playwright 的示例。&lt;&#x2F;p&gt;
&lt;div class=&quot;w-full md:w-1&#x2F;2 mx-auto&quot;&gt;
  &lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:600&#x2F;0*8FsR8lzrCfAf8W2J.jpg&quot;&#x2F;&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;zhe-liang-ge-ce-shi-kuang-jia-ti-gong-liao-shen-me&quot;&gt;这两个测试框架提供了什么?&lt;&#x2F;h2&gt;
&lt;p&gt;Cypress 和 Playwright 都提供了出色的 UI 测试体验(您也可以测试 API)。它们的自动等待功能使开发人员能够轻松编写测试,它们提供了 UI 来可视化运行的测试,还可以生成测试的屏幕截图和视频,并支持 TypeScript。&lt;&#x2F;p&gt;
&lt;p&gt;这两个框架还支持可视化组件测试,但我不会在本文中涉及这个主题。&lt;&#x2F;p&gt;
&lt;p&gt;由于这两个框架提供了非常相似的功能,我将分析它们如何实现以下方面,以及它们如何影响开发人员的体验&#x2F;生产力:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;用于编写测试的语法在学习曲线和每个框架的整体易用性方面起着关键作用。包括但不限于使用自定义命令扩展框架和录制用例。&lt;&#x2F;li&gt;
&lt;li&gt;测试执行和可维护性是影响开发人员对测试信心和花费在调试上的时间的重要因素,特别是在速度和稳定性方面。&lt;&#x2F;li&gt;
&lt;li&gt;测试报告在测试过程中起着关键作用,评估它们的设置 ease 和提供的信息级别对两个框架都很重要。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;wo-de-cypress-jing-yan&quot;&gt;我的 Cypress 经验&lt;&#x2F;h2&gt;
&lt;p&gt;安装 Cypress 很简单,基本上就是一个 NPM 依赖项,然后就可以开始了。然而,不久之后就遇到了需要了解的 Cypress 复杂细节,才能使用它。以下是一些主要的问题:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;用于编写测试的语法:&lt;&#x2F;strong&gt; Cypress 使用类似 promise 的语法来编写测试。这一开始可能看起来并不令人困惑,但开发人员往往认为因为他们的 API 看起来像 promise,所以它的行为就像 promise(async&#x2F;await)。悲哀的现实是它不是这样的,这导致开发人员花费大量时间学习如何使用 Cypress API 并使其适合他们特定的测试场景。&lt;br &#x2F;&gt;
如果您需要使用 async&#x2F;await,您有两个选择:要么将其包装在 Cypress 命令中以便将其添加到 &lt;em&gt;Cypress 命令链&lt;&#x2F;em&gt; 中,要么使用像 &lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;cypress-promise&quot;&gt;cypress-promise&lt;&#x2F;a&gt; 这样的库。以下是每个选项的示例:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; async-await.spec.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;promisify &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cypress-promise&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;milliseconds&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return new &lt;&#x2F;span&gt;&lt;span&gt;Promise(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;milliseconds&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;asyncFunction&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;started asyncFunction &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;finalized asyncFunction &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Async&#x2F;Await Test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cy&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;visit&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;convert promises into cypress commands, do not write tests using async&#x2F;await&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cy&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;wrap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;asyncFunction&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cy&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;wrap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;asyncFunction&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;second&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;convert cypress commands to promises, should be able to code with async&#x2F;await&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;foo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;promisify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cy&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;wrap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bar &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;promisify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cy&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;wrap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bar&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;to&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;equal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bar&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;to&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;equal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bar&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;测试报告:&lt;&#x2F;strong&gt; Cypress 缺乏内置的测试报告功能,这意味着获得所有测试的综合摘要、它们的持续时间以及失败的额外元素(如屏幕截图和链接视频)可能是一个具有挑战性和繁琐的过程。尽管在配置报告方面投入了相当大的努力,但仍可能存在生成的报告中缺少屏幕截图的情况。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*QgXR5prXZLNJ8HBm3tLfqg.png&quot; alt=&quot;图片 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; package.json: 繁琐的测试报告设置示例
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;-------------------- E2E Commands --------------------&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cypress:open&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nyc cypress open&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cypress:run&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm-run-all -s --continue-on-error _clean _cypress-run:run _cypress-run:html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;-------------------- Supporting Commands --------------------&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;_clean&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx rimraf coverage cypress&#x2F;output&#x2F;junit cypress&#x2F;output&#x2F;mocha-json cypress&#x2F;output&#x2F;mocha-html&#x2F;*.html cypress&#x2F;output&#x2F;mocha-html&#x2F;*.json .nyc_output&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;_coverage-report&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx nyc report --reporter html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;_cypress-run:run&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nyc cypress run --headless --browser chrome&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;_cypress-run:html:merge-json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;mochawesome-merge cypress&#x2F;output&#x2F;mocha-json&#x2F;*.json &amp;gt; cypress&#x2F;output&#x2F;mocha-html&#x2F;merged-mochawesome.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;_cypress-run:html:gen-html-from-json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;marge cypress&#x2F;output&#x2F;mocha-html&#x2F;merged-mochawesome.json -f cypress -o cypress&#x2F;output&#x2F;mocha-html -i true --charts true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;_cypress-run:html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm-run-all -s _cypress-run:html:merge-json _cypress-run:html:gen-html-from-json _coverage-report&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;如何运行有界面测试:&lt;&#x2F;strong&gt; Cypress 在其自己的浏览器应用程序内运行您的 web 应用程序。它首先打开一个列出所有 &lt;code&gt;specs&lt;&#x2F;code&gt; 的页面,然后您可以单击一个 spec 来运行该 spec 内的所有测试。我可能会补充说,所有这些都相当慢。此外,如果您只想运行 &lt;code&gt;spec&lt;&#x2F;code&gt; 中的一个测试,您需要将其标记为 &lt;code&gt;only&lt;&#x2F;code&gt;,保存,然后只有该测试会自动重新加载。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*DA01oS_nrO9ZdiSIb37IRw.png&quot; alt=&quot;图片 4: 在 Cypress 浏览器中运行的应用程序。&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;如何在无界面运行中访问浏览器控制台日志:&lt;&#x2F;strong&gt; 浏览器控制台日志仅在以有界面形式运行测试时可见(打开 DevTools)。这意味着如果您的测试在本地成功运行但在 CI 流水线上失败,那么您将花费很长时间试图将这些日志带到流水线控制台。幸运的是,有一个插件可以简化这个过程,但由于 Cypress 插件的工作方式,它不仅仅是即插即用的工作。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*pBBISRJOKqAbxp0kxmQlXg.png&quot; alt=&quot;图片 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;只有在打开 DevTools 时才能看到浏览器控制台日志。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; cypress&#x2F;plugins&#x2F;index.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;interface &lt;&#x2F;span&gt;&lt;span&gt;Browsers {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;family&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;interface &lt;&#x2F;span&gt;&lt;span&gt;LaunchOptions {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;cypressPLugins &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;unknown&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;unknown&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@cypress&#x2F;code-coverage&#x2F;task&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 将 console.* 消息记录到 cypress 控制台,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 当 CI&#x2F;CD 流水线上出现错误时会有帮助
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cypress-log-to-output&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;install&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;consoleToLogConfig&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@cypress&#x2F;react&#x2F;plugins&#x2F;react-scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; @ts-ignore
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;before:browser:launch&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Browsers, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;launchOptions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;LaunchOptions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;family &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;chrome&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;chrome&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Adding chrome config...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;launchOptions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;--disable-dev-shm-usage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;launchOptions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;--lang=en&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;launchOptions&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;consoleToLogConfig &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;_type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;unknown&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  event&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;level&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 从此插件返回 true 或 false 以控制事件是否记录在 cypress 控制台上
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; `type` 是 `console` 或 `browser`
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果 `type` 是 `browser`, `event` 是 `LogEntry` 类型的对象:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;  https:&#x2F;&#x2F;chromedevtools.github.io&#x2F;devtools-protocol&#x2F;tot&#x2F;Log&#x2F;#type-LogEntry
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果 `type` 是 `console`, `event` 是传递给 `Runtime.consoleAPICalled` 的对象类型:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;  https:&#x2F;&#x2F;chromedevtools.github.io&#x2F;devtools-protocol&#x2F;tot&#x2F;Runtime&#x2F;#event-consoleAPICalled
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 只显示错误事件:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;event.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;level &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span&gt;event.type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cypressPLugins&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;调试测试:&lt;&#x2F;strong&gt; Cypress 有几种不同的调试选项,网上有很多关于这个主题的文章。主要的要点是,由于 Cypress 命令异步运行,您不能简单地添加一个 &lt;code&gt;debugger&lt;&#x2F;code&gt; 命令。&lt;br &#x2F;&gt;
本质上,Cypress 为您提供了两个有用的命令: &lt;code&gt;debug()&lt;&#x2F;code&gt; 和 &lt;code&gt;pause()&lt;&#x2F;code&gt;。第一个将 &lt;code&gt;debugger&lt;&#x2F;code&gt; 命令添加到 Cypress 运行器中,而后者则在该点停止测试运行。两者都允许您检查 DOM,但只有使用 &lt;code&gt;pause()&lt;&#x2F;code&gt; 才能逐步执行测试中即将到来的每个 Cypress 命令。&lt;br &#x2F;&gt;
鉴于 Cypress 使用类似 promise 的链式 API,您可以将这两个命令中的任何一个与任何 Cypress 命令链接起来:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cy&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;testid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;username-input&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;my-username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;pause&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*Z1gGMf2DP-xhqNg8YIQDkA.png&quot; alt=&quot;图片 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;测试并行化:&lt;&#x2F;strong&gt; Cypress 不支持在本地并行运行测试。有很多库可以帮助您实现并行化,但这并非易事。在我之前的公司,我们试图并行化我们的测试以改善流水线运行时间,但努力结果如此巨大,以至于我们降低了它的优先级。&lt;br &#x2F;&gt;
副作用是,开发人员很少在本地运行完整的 Cypress 测试套件,他们只是等待流水线的反馈。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;速度:&lt;&#x2F;strong&gt; Cypress 在无界面和有界面浏览器格式下运行时都很慢。这是我们想要并行化测试的主要原因。即使在本地运行测试也非常慢。在开发测试时,当您修改文件时,测试会自动重新加载,即使在我的 MacBook Pro 上,这也可能需要几秒钟。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;测试稳定性:&lt;&#x2F;strong&gt; Cypress 以在 CI&#x2F;CD 流水线上不稳定而闻名,这导致开发人员花费大量时间追踪幽灵。我见过开发人员实施的常见 &lt;em&gt;黑客解决方案&lt;&#x2F;em&gt; 是在测试中添加 &lt;code&gt;cy.wait(&amp;lt;ms&amp;gt;)&lt;&#x2F;code&gt;,并在 CI&#x2F;CD 流水线中添加重试。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;自定义命令:&lt;&#x2F;strong&gt; 如果您在测试中经常使用常见功能,您很可能想要为其创建一个自定义 Cypress 命令。不幸的是,添加这样的命令既不直观也不类型安全(除非您额外努力):&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; cypress&#x2F;support&#x2F;commands.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Cypress&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Commands&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;selector&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;unknown&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cy&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-testid=${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;selector&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; cypress&#x2F;typings&#x2F;cypress.d.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;declare &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;namespace &lt;&#x2F;span&gt;&lt;span&gt;Cypress {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;interface &lt;&#x2F;span&gt;&lt;span&gt;Chainable {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;selector&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;unknown&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Chainable;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 您现在可以在测试中使用此命令:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;it&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;should type username in username input&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cy&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;username-input&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;my-username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;录制用例:&lt;&#x2F;strong&gt; Cypress 现在提供了 &lt;em&gt;Cypress Studio&lt;&#x2F;em&gt;,这是一个我个人还没有尝试过的实验性功能。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;wo-de-playwright-jing-yan&quot;&gt;我的 Playwright 经验&lt;&#x2F;h2&gt;
&lt;p&gt;我从 2023 年初开始使用 React,当时我的团队还没有任何 UI 测试。因此,我决定考虑引入 Cypress。然而,我们也开始引入 web 组件,并计划使用 &lt;a href=&quot;https:&#x2F;&#x2F;storybook.js.org&#x2F;&quot;&gt;Storybook&lt;&#x2F;a&gt; 来记录和测试我们的 web 组件。由于 Storybook 在底层使用 Playwright,我调查了 Playwright 能提供什么。最初的吸引力是:我的团队只需要学习一个框架就可以测试 web 组件和我们的 UI。&lt;&#x2F;p&gt;
&lt;p&gt;安装 Playwright 非常简单:不到一小时我就完成了全面的测试。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;用于编写测试的语法:&lt;&#x2F;strong&gt; 编写 Playwright 测试就像编写普通的 TypeScript 代码一样简单,没有特殊的 API 需要学习。最好的部分是您可以编写普通的 async&#x2F;await 代码,因此您可以使用所有正常的 TypeScript 支持函数。示例:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright test with async&#x2F;await&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;milliseconds&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return new &lt;&#x2F;span&gt;&lt;span&gt;Promise(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;milliseconds&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;asyncFunction&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;started asyncFunction &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;finalized asyncFunction &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;should open storybook and click around&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;localhost:6006&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;        name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Storybook 7.1.1 is available! Your current version is: 7.0.2 Dismiss notification&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      })
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Storybook&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#internal-components-overview--docs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;should test the async methods used in previous example&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  }) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;asyncFunction&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;asyncFunction&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;second&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;测试报告:&lt;&#x2F;strong&gt; 这是一个如此令人愉快的惊喜:我不需要做任何事情!Playwright 生成的开箱即用报告非常棒。您还可以非常轻松地在多个浏览器上运行测试(只需更改配置,Playwright 将为您安装所需的浏览器)。&lt;br &#x2F;&gt;
我将其配置为仅包含失败测试的跟踪信息、屏幕截图和视频;否则,就像 Cypress 一样,测试运行会很慢。在下面的示例中,我们的第一个测试失败了。如果我们点击它,我们会得到以下信息:导致失败的异常、测试的哪些步骤失败了、测试的视频以及测试的完整跟踪。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*i7YqewHSh9Pu93jRBCioew.png&quot; alt=&quot;图片 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Playwright HTML 报告概览&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*VVDm5lT4OEQbhKZ0HCoscA.png&quot; alt=&quot;图片 8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;失败测试 Playwright 报告摘要&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*WOa3ZZLGfQGUR7GTfRxPLw.png&quot; alt=&quot;图片 9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 测试跟踪&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;如何运行有界面测试:&lt;&#x2F;strong&gt; Playwright 由 Microsoft 开发,所以他们当然为 &lt;a href=&quot;https:&#x2F;&#x2F;code.visualstudio.com&#x2F;&quot;&gt;VS Code&lt;&#x2F;a&gt; 提供了一个插件。我自己是 IntelliJ 用户,但当我想运行 Playwright 测试时,我发现自己会切换到 VS Code。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;与 Cypress 不同,您不必启动任何东西就可以运行一个或多个测试。只需在 VS Code 中打开您的代码,然后点击常用的单元测试绿色三角形,或者如果您之前已经运行过测试,则点击绿色勾号&#x2F;红色 X。您还可以选择是否要打开浏览器或跟踪查看器。&lt;br &#x2F;&gt;
这里的开发人员体验非常棒。测试打开速度快,反应灵敏。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:1000&#x2F;1*TNZ7F8L-5g02deAoA09xGg.png&quot; alt=&quot;图片 10&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;如何在无界面的浏览器运行中访问浏览器控制台日志:&lt;&#x2F;strong&gt; 与 Cypress 一样,Playwright 不会在运行 Playwright 的控制台中显示浏览器日志。为了实现这一点,您可以扩展 Playwright &lt;code&gt;Page&lt;&#x2F;code&gt; 对象以将浏览器日志转发到控制台日志(还有其他方法可以做到这一点,例如扩展 &lt;code&gt;test&lt;&#x2F;code&gt; 对象本身)。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; utils&#x2F;console.util.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;configureLogForwarding &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;msg&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;PLAYWRIGHT_LOG_TO_CONSOLE &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;switch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;msg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;()) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; eslint-disable-next-line no-console
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Log: &amp;quot;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;msg&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;()}&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;break&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;warning&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; eslint-disable-next-line no-console
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Warning: &amp;quot;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;msg&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;()}&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;break&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;assert&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; eslint-disable-next-line no-console
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Error: &amp;quot;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;msg&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;()}&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;break&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;demo-tests.spec.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright test with async&#x2F;await&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;configureLogForwarding&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;hostAppNavigationPo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;HostAppNavigationPo(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;genericAssetModalPo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;GenericAssetModalPo(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;调试测试:&lt;&#x2F;strong&gt; 这是我最喜欢的功能之一。要调试测试,设置一个断点,然后右键单击绿色三角形&#x2F;绿色勾号&#x2F;红色 X,然后单击 &lt;em&gt;调试测试&lt;&#x2F;em&gt;。浏览器将打开,运行将在您的断点处停止。从这里开始,它就是开发人员习惯的正常调试会话。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*YpMNIAHnhHhMDNG7JWX1zA.png&quot; alt=&quot;图片 11&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在 Playwright 中开始调试会话&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*018sTeChOw29tDN9uGVuJg.png&quot; alt=&quot;图片 12&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 正在进行的调试会话&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;测试并行化:&lt;&#x2F;strong&gt; Playwright 支持开箱即用的并行化(当然,您的被测系统必须能够支持并行运行测试)。考虑到我们的 CI&#x2F;CD 工具没有太多资源,因此速度较慢,我们在流水线中关闭了并行测试。然而,当我在编码时,我能够在本地非常快速地运行所有测试。诚然,我们只使用了 Playwright 大约 5 个月,所以我们只有约 80 个测试。这里有一些非科学的数字:约 80 个测试,在 CI&#x2F;CD 中 5 分钟(1 个工作进程),在本地 2 分钟(1 个工作进程),在本地 55 秒(5 个工作进程)。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; playwright.config.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* 其他配置... *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* 在 CI 上选择退出并行测试。 *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;  workers: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;undefined&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* 并行运行 spec 文件中的测试 *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;  fullyParallel: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;速度:&lt;&#x2F;strong&gt; 当以无界面形式运行等效测试时(就像在 CI&#x2F;CD 流水线中那样),Playwright 比 Cypress 快约 1.5 倍。&lt;br &#x2F;&gt;
此外,Playwright 能够在不到一秒的时间内在浏览器上启动测试(在我的 MacBook Pro 上)。这意味着我可以随时运行任何测试,在任何规范中,而无需启动 &lt;em&gt;Playwright 服务器&lt;&#x2F;em&gt; 等。与 Cypress 不同,对于 Cypress,您首先必须启动 Cypress UI,然后在该 UI 中导航到要运行的测试。&lt;br &#x2F;&gt;
这为开发人员提供了绝佳的体验!&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;测试稳定性:&lt;&#x2F;strong&gt; 我们已经编写测试 5 个月了,我们还没有遇到任何不稳定的情况,也不需要在测试中使用任何等待或睡眠。由于我不再在之前使用 Cypress 的公司工作,很难确定将那些不稳定的测试迁移到 Playwright 是否会使它们稳定下来。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;自定义命令:&lt;&#x2F;strong&gt; Playwright 提供了一种&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-test#test-extend&quot;&gt;扩展基础&lt;&#x2F;a&gt; &lt;code&gt;[test](https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-test#test-extend)&lt;&#x2F;code&gt; 的方法,这样您就可以在测试期间轻松访问自定义命令和&#x2F;或页面对象。这种模式简单易懂,直观且类型安全。Playwright 的示例:&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; my-test.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;TodoPage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;todo-page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span&gt;Options &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultItem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string &lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 通过提供 &amp;quot;defaultItem&amp;quot; 选项和 &amp;quot;todoPage&amp;quot; fixture 来扩展基本测试。
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;Options &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp; &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;todoPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;TodoPage }&amp;gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 定义一个选项并提供默认值。
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 我们稍后可以在配置中覆盖它。
&lt;&#x2F;span&gt;&lt;span&gt;  defaultItem: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Do stuff&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { option: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;}],
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 定义一个 fixture。请注意,它可以使用内置 fixture &amp;quot;page&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 和一个新选项 &amp;quot;defaultItem&amp;quot;。
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;todoPage&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;defaultItem &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;todoPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;TodoPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;todoPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;todoPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;addToDo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defaultItem&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;todoPage&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;todoPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;removeAll&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; example.spec.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;my-test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test 1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;todoPage &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;todoPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;addToDo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;my todo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;录制用例:&lt;&#x2F;strong&gt; Playwright 提供了使用他们的 VS Code 插件记录 UI 测试的选项。您只需按下 &lt;code&gt;Record new&lt;&#x2F;code&gt; 按钮,Playwright 就会为您打开一个浏览器。然后输入 web 应用程序的 URL 并开始点击。Playwright 记录的最好部分是,如果存在 &lt;code&gt;data-testid&lt;&#x2F;code&gt; 选择器,它会选择该选择器 ( &lt;code&gt;await page.getByTestId(&#x27;dialog-title&#x27;);&lt;&#x2F;code&gt; )。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;miro.medium.com&#x2F;v2&#x2F;resize:fit:700&#x2F;1*clsfh46nro1biK36LZC0RQ.png&quot; alt=&quot;图片 13&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 还有许多其他优势,我在本文中没有涵盖,主要是因为我没有大量使用这些方面,但这里有一些例子:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;iFrames:&lt;&#x2F;strong&gt; Playwright 开箱即用地与 iFrames 配合使用,体验非常流畅。我们在当前的工作中使用 iFrames,但在我之前的工作中没有使用(当时我在使用 Cypress)。然而,据我所知,您需要安装 Cypress 插件才能测试 iFrames。这篇&lt;a href=&quot;https:&#x2F;&#x2F;www.cypress.io&#x2F;blog&#x2F;2020&#x2F;02&#x2F;12&#x2F;working-with-iframes-in-cypress&#x2F;&quot;&gt;文章&lt;&#x2F;a&gt;详细解释了为什么 Cypress 在处理 iFrames 时遇到困难以及如何测试它们。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Web 应用服务器:&lt;&#x2F;strong&gt; Cypress 和 Playwright 都需要您的应用程序正在运行才能运行测试。对 Cypress 的一个常见请求是希望框架能够在运行测试之前启动被测系统。我个人在运行测试时总是让我的应用程序保持运行状态,无论是在本地还是在 CI&#x2F;CD 流水线中。然而,开发人员的愿望已经被听到了(被 Playwright):&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; playwright.config.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在开始测试之前运行您的本地开发服务器。
&lt;&#x2F;span&gt;&lt;span&gt;  webServer: {
&lt;&#x2F;span&gt;&lt;span&gt;    command: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm run start&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    url: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;127.0.0.1:3000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    reuseExistingServer: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;禁止&lt;&#x2F;strong&gt; &lt;code&gt;only&lt;&#x2F;code&gt; &lt;strong&gt;关键字:&lt;&#x2F;strong&gt; 一个常见的用例是,当开发人员想要运行单个测试时,他们会将其标记为 &lt;code&gt;only&lt;&#x2F;code&gt;。不幸的是,他们经常忘记在提交之前删除它。Playwright 带有一个配置来检测这一点,而对于 Cypress,我们必须添加 eslint 规则。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; playwright.config.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;* 如果您在源代码中不小心留下了 test.only,在 CI 上使构建失败。 *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;  forbidOnly: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!!&lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; example.spec.ts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright test with async&#x2F;await&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;only&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;should test the async methods used in previous example&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  }) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;asyncFunction1&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;asyncFunction2&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 更多测试...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;多个页面:&lt;&#x2F;strong&gt; Cypress 在其自己的 web 应用程序中运行您的 web 应用程序,这带来了 Cypress 一次只能在一个页面上运行测试的限制。而 Playwright 则可以在每个浏览器中打开多个 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-browsercontext&quot;&gt;BrowserContext&lt;&#x2F;a&gt; 和&#x2F;或多个 &lt;code&gt;Page&lt;&#x2F;code&gt;,所有这些都可以有不同的网站&#x2F;域等,并在它们之间运行测试。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;多语言支持:&lt;&#x2F;strong&gt; 在 Cypress 中,您可以选择使用 JavaScript 或 TypeScript 编写测试。然而,在 Playwright 中,编写测试的语言选项更加多样化。您可以选择使用 JavaScript、TypeScript、Java、Python 或 .NET 编写测试,为开发人员提供了更大的灵活性,可以选择他们喜欢的编程语言进行测试自动化。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;jie-lun&quot;&gt;结论&lt;&#x2F;h2&gt;
&lt;p&gt;在多年成为 Cypress 的粉丝并强烈倡导 UI 测试之后,开始使用一个我不确定是否会像 Cypress 那样好，使用的新框架并不是一个容易的决定。话虽如此,发现 Playwright 不仅可以做到 Cypress 所能做到的,而且在我能想到的所有方面都超越了它,这是一个如此令人愉快的惊喜。&lt;&#x2F;p&gt;
&lt;p&gt;最后,Cypress 和 Playwright 的开发者社区都相当庞大。尽管 Cypress 仍然更受欢迎,但随着更多开发者发现 Playwright 的开发体验有多棒,Playwright 社区正在快速增长。&lt;&#x2F;p&gt;
&lt;p&gt;我建议任何已经在使用 Cypress 或正在考虑进行 UI 测试的人都应该考虑 Playwright 作为一个值得的选择。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@oldiazg&#x2F;why-i-switched-from-cypress-to-playwright-dc41ce4d5e1b&quot;&gt;来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-08-02&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>使用 Playwright 进行现代网页测试</title>
        <published>2023-07-28T00:00:00+00:00</published>
        <updated>2023-07-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/modern-web-testing-with-playwright/"/>
        <id>https://playwright.itest.info/blog/modern-web-testing-with-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/modern-web-testing-with-playwright/">&lt;p&gt;Playwright 是一个出色的新型网页测试框架,它可以帮助你采用现代方法进行网页的测试开发。让我们一起来了解如何使用它。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shen-ru-si-kao-ce-shi-de-ben-zhi&quot;&gt;深入思考测试的本质&lt;&#x2F;h2&gt;
&lt;p&gt;让我问你一系列问题:&lt;&#x2F;p&gt;
&lt;p&gt;问题 1: 你喜欢代码中出现 bug 吗? 大概率不会。Bug 就是麻烦,它们本不该出现,而且还需要花精力去修复。总之,bug 就是个大麻烦。&lt;&#x2F;p&gt;
&lt;p&gt;问题 2: 你愿意让这些 bug 流入生产环境吗? 当然不愿意! 我们希望在用户看到之前就修复 bug。严重的 bug 可能会对系统、业务甚至声誉造成巨大损害。一旦 bug 不小心溜进了生产环境,我们也要尽快发现并修复。&lt;&#x2F;p&gt;
&lt;p&gt;问题 3: 你喜欢创建测试来提前捕获 bug 吗? 嗯...这个问题就不那么容易回答了。大多数人都明白,好的测试能为软件质量提供宝贵的反馈,但并非每个人都乐意投入精力进行测试。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wei-shen-me-dui-ce-shi-bu-tai-gan-mou&quot;&gt;为什么对测试不太感冒?&lt;&#x2F;h2&gt;
&lt;p&gt;为什么不是所有人都喜欢测试呢? &lt;strong&gt;因为测试很难!&lt;&#x2F;strong&gt; 以下是我经常听到的抱怨:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;测试太慢 - 运行时间太长!&lt;&#x2F;li&gt;
&lt;li&gt;测试很脆弱 - 应用稍有变动就会失败!&lt;&#x2F;li&gt;
&lt;li&gt;测试不稳定 - 经常莫名其妙地崩溃!&lt;&#x2F;li&gt;
&lt;li&gt;测试难以理解 - 复杂且难以阅读!&lt;&#x2F;li&gt;
&lt;li&gt;测试不创造价值 - 我们应该把时间花在开发新功能上!&lt;&#x2F;li&gt;
&lt;li&gt;测试需要切换上下文 - 打断了开发工作流!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;automationpanda.com&#x2F;wp-content&#x2F;uploads&#x2F;2023&#x2F;07&#x2F;testing-pain-points-1061611341-e1690484881718.png&quot; alt=&quot;图1: 测试挑战&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这些都是合理的理由。为了缓解这些痛点,软件团队历来围绕&lt;a href=&quot;https:&#x2F;&#x2F;automationpanda.com&#x2F;2018&#x2F;08&#x2F;01&#x2F;the-testing-pyramid&#x2F;&quot;&gt;测试金字塔&lt;&#x2F;a&gt;制定测试策略,从上到下将测试分为以下几层:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;UI 测试&lt;&#x2F;li&gt;
&lt;li&gt;API 测试&lt;&#x2F;li&gt;
&lt;li&gt;组件测试&lt;&#x2F;li&gt;
&lt;li&gt;单元测试&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;automationpanda.com&#x2F;wp-content&#x2F;uploads&#x2F;2023&#x2F;07&#x2F;testing-pyramid-3004920334-e1690484079544.png&quot; alt=&quot;图2: 测试金字塔&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;底层测试被认为&quot;更好&quot;,因为它们更接近代码,更容易自动化,执行速度更快。它们也被认为不太容易出现不稳定的情况,因此更易于维护。顶层测试则恰恰相反:规模大、速度慢、成本高。金字塔的形状暗示团队应该在金字塔底部的测试上投入更多时间,而在顶部的测试上投入较少时间。&lt;&#x2F;p&gt;
&lt;p&gt;端到端测试其实非常有价值。遗憾的是,测试金字塔将它们标记为&quot;困难&quot;和&quot;糟糕&quot;,主要是因为不良实践和工具的局限性。这也导致团队制定的测试策略更强调测试的类别,而不是测试所能提供的结果。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhong-xin-si-kao-xian-dai-wang-zi-dong-hua-de-ce-shi-mu-biao&quot;&gt;重新思考现代网自动化的测试目标&lt;&#x2F;h2&gt;
&lt;p&gt;测试不必如此困难,也不必受制于过去的问题。我们应该用全新的方式来测试现代网页应用。&lt;&#x2F;p&gt;
&lt;p&gt;以下是现代网页测试的三个主要目标:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;专注于建立&lt;strong&gt;快速反馈循环&lt;&#x2F;strong&gt;,而不是特定类型的测试。&lt;&#x2F;li&gt;
&lt;li&gt;让测试开发变得&lt;strong&gt;尽可能快速和轻松&lt;&#x2F;strong&gt;。&lt;&#x2F;li&gt;
&lt;li&gt;选择能自然&lt;strong&gt;融入开发工作流&lt;&#x2F;strong&gt;的测试工具。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;automationpanda.com&#x2F;wp-content&#x2F;uploads&#x2F;2023&#x2F;07&#x2F;modern-web-testing-goals-2430785089-e1690484965113.png&quot; alt=&quot;图3: 现代测试目标&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这些目标强调了&lt;em&gt;结果&lt;&#x2F;em&gt;和&lt;em&gt;效率&lt;&#x2F;em&gt;。测试应该成为开发过程中自然而然的一部分,不应产生任何隔阂。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwright-jian-jie&quot;&gt;Playwright 简介&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;&quot;&gt;Playwright&lt;&#x2F;a&gt;是一个现代网页测试框架,可以帮助我们实现这些目标。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;它是微软的一个开源项目。&lt;&#x2F;li&gt;
&lt;li&gt;它通过(超快的)调试协议操控浏览器。&lt;&#x2F;li&gt;
&lt;li&gt;它支持 Chromium&#x2F;Chrome&#x2F;Edge、Firefox 和 WebKit。&lt;&#x2F;li&gt;
&lt;li&gt;它提供自动等待、录制回放、UI 调试模式等功能。&lt;&#x2F;li&gt;
&lt;li&gt;它可以同时测试 UI 和 API。&lt;&#x2F;li&gt;
&lt;li&gt;它为 JavaScript&#x2F;TypeScript、Python、Java 和 C#提供绑定。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;automationpanda.com&#x2F;wp-content&#x2F;uploads&#x2F;2023&#x2F;07&#x2F;playwright-overview-145765983-e1690485021191.png&quot; alt=&quot;图4: Playwright概览&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 采用了独特的浏览器自动化方法。首先,它使用浏览器&lt;em&gt;项目&lt;&#x2F;em&gt;而不是完整的浏览器应用。例如,这意味着你会测试 Chromium 而不是 Google Chrome。浏览器项目更小,占用的资源也比完整浏览器少。Playwright 还会为你管理浏览器项目,所以你不需要安装额外的东西。&lt;&#x2F;p&gt;
&lt;p&gt;其次,它非常高效地使用浏览器:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Playwright 不会为每个测试启动一个全新的浏览器实例,而是为整个测试套件启动一个浏览器&lt;em&gt;实例&lt;&#x2F;em&gt;。&lt;&#x2F;li&gt;
&lt;li&gt;然后,它为每个测试从该实例创建一个唯一的&lt;em&gt;浏览器上下文&lt;&#x2F;em&gt;。浏览器上下文本质上就像一个隐身模式:它有自己的会话存储和标签页,不与其他上下文共享。浏览器上下文的创建和销毁都非常快。&lt;&#x2F;li&gt;
&lt;li&gt;之后,每个浏览器上下文可以有一个或多个&lt;em&gt;页面&lt;&#x2F;em&gt;。所有 Playwright 交互都通过页面进行,比如点击和抓取。大多数测试只需要一个页面。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;automationpanda.com&#x2F;wp-content&#x2F;uploads&#x2F;2023&#x2F;07&#x2F;browsers-contexts-pages-880645586-e1690484653870.png&quot; alt=&quot;图5: Playwright的浏览器、上下文和页面&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 会自动为你处理所有这些设置。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwright-yu-qi-ta-gong-ju-de-bi-jiao&quot;&gt;Playwright 与其他工具的比较&lt;&#x2F;h2&gt;
&lt;p&gt;Playwright 并不是唯一的浏览器自动化工具。其他两个最流行的工具是&lt;a href=&quot;https:&#x2F;&#x2F;www.selenium.dev&#x2F;&quot;&gt;Selenium&lt;&#x2F;a&gt;和&lt;a href=&quot;https:&#x2F;&#x2F;www.cypress.io&#x2F;&quot;&gt;Cypress&lt;&#x2F;a&gt;。以下是一个高层次的比较图表:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;automationpanda.com&#x2F;wp-content&#x2F;uploads&#x2F;2023&#x2F;07&#x2F;tool-comparison-3467232613-e1690485288193.png&quot; alt=&quot;图6: 浏览器自动化工具比较&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这三个都是不错的工具,各有优势。Playwright 的主要优势在于它提供了出色的开发者体验,执行速度最快,支持多种编程语言,并且有几个提高生活质量的特性。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xue-xi-playwright&quot;&gt;学习 Playwright&lt;&#x2F;h2&gt;
&lt;p&gt;如果你想学习如何使用 Playwright 进行网页测试自动化,可以尝试我的教程*&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;AutomationPanda&#x2F;awesome-web-testing-playwright&quot;&gt;使用 Playwright 进行精彩的网页测试&lt;&#x2F;a&gt;*。所有说明和示例代码都在 GitHub 上。这个教程设计为自学指南,快来试试吧!&lt;&#x2F;p&gt;
&lt;p&gt;Test Automation University 还提供了一个&lt;a href=&quot;https:&#x2F;&#x2F;testautomationu.applitools.com&#x2F;learningpaths.html?id=playwright-path&quot;&gt;Playwright 学习路径&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 是一个出色的现代网页测试框架。快来尝试一下,告诉我你用它自动化了什么!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;automationpanda.com&#x2F;2023&#x2F;07&#x2F;28&#x2F;modern-web-testing-with-playwright&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-07-28T13:00:00+00:00&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在 Playwright 测试中途把浏览器改成暗夜模式?</title>
        <published>2023-07-24T00:00:00+00:00</published>
        <updated>2023-07-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/is-it-possible-to-change-colorscheme-in-the-middle-of-a-playwright-tests/"/>
        <id>https://playwright.itest.info/blog/is-it-possible-to-change-colorscheme-in-the-middle-of-a-playwright-tests/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/is-it-possible-to-change-colorscheme-in-the-middle-of-a-playwright-tests/">&lt;p&gt;这个问题最近在 Playwright Discord 服务器的 #help-playwright 频道中被提出。如果你还没加入,现在就&lt;a href=&quot;https:&#x2F;&#x2F;discord.gg&#x2F;playwright-807756831384403968&quot;&gt;加入吧&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;大家好,我希望能在测试过程中改变系统&#x2F;浏览器的主题偏好,因为我的应用可以在不刷新页面的情况下响应用户偏好的变化。为了验证这一点,我需要能够在测试运行时更改系统&#x2F;浏览器的主题。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;值得注意的是,有多种方法可以实现这一目标,让我们来看看!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-test-use&quot;&gt;使用 test.use()&lt;&#x2F;h2&gt;
&lt;p&gt;第一种方法是使用 codegen 工具来构建 UI 测试。使用以下命令时,会以暗夜模式的 &lt;code&gt;color-scheme&lt;&#x2F;code&gt; 启动 codegen 工具。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npx&lt;&#x2F;span&gt;&lt;span&gt; playwright codegen&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --color-scheme&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;dark
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;默认情况下,生成的 codegen 文件如下:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  colorScheme: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dark&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个例子展示了如何在测试用例外使用 &lt;code&gt;test.use()&lt;&#x2F;code&gt; 功能。这将确保该文件中运行的所有测试都使用深色 &lt;code&gt;colorScheme&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;emulation#color-scheme-and-media&quot;&gt;playwright 的模拟能力&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-page-emulatemedia&quot;&gt;使用 page.emulateMedia()&lt;&#x2F;h2&gt;
&lt;p&gt;另一种实现方法是在测试用例里使用 &lt;code&gt;page.emulateMedia()&lt;&#x2F;code&gt;。这允许你在同一个测试中进行多次检查和断言。在下面的例子中:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;我访问 &lt;a href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.reddit.com&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;创建一个名为 postContainer 的定位器变量&lt;&#x2F;li&gt;
&lt;li&gt;将页面设置为深色模式(暗夜模式) &lt;code&gt;colorScheme&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;验证 CSS 背景颜色是深灰色&lt;&#x2F;li&gt;
&lt;li&gt;将页面设置回浅色模式(白天模式) &lt;code&gt;colorScheme&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;验证 CSS 背景颜色是白色&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;selectors&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;验证深色和浅色 CSS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;selectors&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;setTestIdAttribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;data-testid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.reddit.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;postContainer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;post-container&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;emulateMedia&lt;&#x2F;span&gt;&lt;span&gt;({ colorScheme: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dark&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;postContainer&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveCSS&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;background-color&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;rgba(26, 26, 27, 0.8)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;screenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;tests&#x2F;ui&#x2F;reddit.com&#x2F;screenshot&#x2F;reddit-home-dark.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;emulateMedia&lt;&#x2F;span&gt;&lt;span&gt;({ colorScheme: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;light&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;postContainer&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveCSS&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;background-color&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;rgba(255, 255, 255, 0.8)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;screenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;tests&#x2F;ui&#x2F;reddit.com&#x2F;screenshot&#x2F;reddit-home-light.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;特别感谢 Twitter 上的 &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;r_ydv?ref_src=twsrc%5Etfw%7Ctwcamp%5Eembeddedtimeline%7Ctwterm%5Escreen-name%3Ar_ydv%7Ctwcon%5Es2&quot;&gt;@r_ydv&lt;&#x2F;a&gt;,他创建了下面这篇文章,指导你如何进行 CSS 断言!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;testerops.com&#x2F;playwright-and-css-validation&#x2F;&quot;&gt;Playwright 验证 CSS&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zai-newcontext-he-newpage-zhong-zhi-ding&quot;&gt;在 newContext 和 newPage 中指定&lt;&#x2F;h2&gt;
&lt;p&gt;在下面的例子中,我们将在 &lt;code&gt;test&lt;&#x2F;code&gt; 块用 context 动态设置 &lt;code&gt;colorScheme&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;探索其他选项&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;browser &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 创建深色模式的上下文
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    colorScheme: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dark&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 或 &amp;#39;light&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 创建深色模式的页面
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;browser&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    colorScheme: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dark&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 或 &amp;#39;light&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;www.reddit.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;screenshot&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;tests&#x2F;ui&#x2F;reddit.com&#x2F;screenshot&#x2F;reddit-home-dark-context.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h2&gt;
&lt;p&gt;通过这些例子,你应该有足够多的方法来更新你正在测试的浏览器的 &lt;code&gt;colorScheme&lt;&#x2F;code&gt;。所有代码示例都可以在&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;playwright-demo&#x2F;tree&#x2F;master&#x2F;tests&#x2F;ui&#x2F;reddit.com&quot;&gt;这里&lt;&#x2F;a&gt;找到。享受深色模式下的测试乐趣吧!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;07&#x2F;image-12.png&quot; alt=&quot;图片 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读!如果你觉得这篇文章有帮助,请在 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; 上联系我,或考虑&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;给我买杯咖啡&lt;&#x2F;a&gt;。如果你想在收件箱中收到更多内容,请在下面订阅,别忘了留下一个 ❤️ 来表示你的喜爱。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;来源 URL: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-change-colorscheme-in-the-middle-of-a-playwright-tests&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-07-24&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>在 Playwright 中处理不同测试间的多种登录状态</title>
        <published>2023-07-17T00:00:00+00:00</published>
        <updated>2023-07-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/handling-multiple-login-states-between-different-tests-in-playwright/"/>
        <id>https://playwright.itest.info/blog/handling-multiple-login-states-between-different-tests-in-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/handling-multiple-login-states-between-different-tests-in-playwright/">&lt;p&gt;本周，我受到了与同事 &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;author&#x2F;joel&#x2F;&quot;&gt;Joel&lt;&#x2F;a&gt; 的对话以及在 &lt;a href=&quot;https:&#x2F;&#x2F;discord.com&#x2F;channels&#x2F;807756831384403968&#x2F;1128777494401663020&#x2F;1128777494401663020&quot;&gt;Discord 频道&lt;&#x2F;a&gt; 中被提到的问题的启发，写下了如何在 Playwright 项目中处理不同登录状态的文章。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;我按照 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;auth&quot;&gt;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;auth&lt;&#x2F;a&gt; 中的文档将“已登录”状态存储在 JSON 文件中。接下来的测试一切顺利，直到我测试注销功能的那一步。在注销测试场景之后，接下来的测试中用户的登录态没了!!。为什么会这样？请帮我理解并修复它。谢谢！&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;&#x2F;strong&gt;：有许多不同的方法可以解决这个问题，我将介绍我选择的解决方法，这取决于我正在测试的 web 应用程序。如果你有更简单或更健壮的方法来解决这个问题，请与我联系，我很想听听你的意见！&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;注意 2&lt;&#x2F;strong&gt;：我在下面测试用例中的断言是不太完美的。我在这里投入了最少的时间，以突出 &lt;code&gt;sessionStorage&lt;&#x2F;code&gt; 和测试设置。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tan-suo-wo-men-jiang-yao-ce-shi-de-wang-zhan&quot;&gt;探索我们将要测试的网站&lt;&#x2F;h2&gt;
&lt;p&gt;但在开始之前，我们必须了解我们将要编写自动化测试的网站。我们将使用 &lt;a href=&quot;https:&#x2F;&#x2F;practicesoftwaretesting.com&#x2F;&quot;&gt;https:&#x2F;&#x2F;practicesoftwaretesting.com&#x2F;&lt;&#x2F;a&gt; 作为我们的测试系统（有关该站点的更多详细信息，请参阅 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;testsmith-io&#x2F;practice-software-testing&quot;&gt;GitHub 项目页面&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;在我的探索性测试过程中，我发现管理员 &lt;code&gt;auth-token&lt;&#x2F;code&gt; 在登录时生成，并且可以在同一浏览器的不同标签页中用于保持认证状态。如果我打开一个新的隐身窗口，会话不会激活，我没有登录，但我可以使用相同的用户名和密码创建第二个已认证会话。这两个会话可以同时存在。一旦我使用注销功能，我发现与之对应的 &lt;code&gt;auth-token&lt;&#x2F;code&gt; 现在失效了，而另一个 &lt;code&gt;auth-token&lt;&#x2F;code&gt; 仍然可以使用。这告诉我，当我要编写验证注销功能的测试时，直接依赖于当前登录用户的测试是行不通的。&lt;&#x2F;p&gt;
&lt;p&gt;这告诉我，我们正在测试的应用程序具有良好的安全性实践。当我们从系统注销时，&lt;code&gt;auth-token&lt;&#x2F;code&gt; 会失效。因此，当我们创建注销测试时，可能应该避免使用我们的默认 &lt;code&gt;auth-token&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zai-playwright-zhong-chuang-jian-yi-ge-she-zhi-xiang-mu&quot;&gt;在 Playwright 中创建一个设置项目&lt;&#x2F;h2&gt;
&lt;p&gt;我使用的提交代码的仓库可以在下面找到。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-practicesoftwaretesting.com&quot;&gt;使用 Playwright 测试 https:&#x2F;&#x2F;practicesoftwaretesting.com 示例 - GitHub&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我们需要做的第一步是建立一个新的 &lt;code&gt;setup project&lt;&#x2F;code&gt;。在下面的配置文件中，我们有两个项目，第一个是 &lt;code&gt;setup&lt;&#x2F;code&gt;，它查找 &lt;code&gt;*.setup.ts&lt;&#x2F;code&gt; 文件并运行它们，第二个项目是 &lt;code&gt;ui-tests&lt;&#x2F;code&gt;，它依赖于 &lt;code&gt;setup&lt;&#x2F;code&gt; 项目成功运行才能继续。有关 defineConfig 的更多信息，请参阅 playwright 文档中的 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-configuration&quot;&gt;测试配置&lt;&#x2F;a&gt; 部分。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; playwright.config.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import type &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;APIRequestOptions &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;lib&#x2F;fixtures&#x2F;apiRequest&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;TestOptions &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;lib&#x2F;pages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dotenv&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;APIRequestOptions&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp;
&lt;&#x2F;span&gt;&lt;span&gt;  (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;TestOptions &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      projects: [
&lt;&#x2F;span&gt;&lt;span&gt;        { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, testMatch: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;, fullyParallel: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;        {
&lt;&#x2F;span&gt;&lt;span&gt;          name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui-tests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;          dependencies: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;        },
&lt;&#x2F;span&gt;&lt;span&gt;      ],
&lt;&#x2F;span&gt;&lt;span&gt;      testDir: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;tests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      fullyParallel: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      forbidOnly: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!!&lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      retries: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      workers: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;undefined&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      reporter: [[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;list&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]],
&lt;&#x2F;span&gt;&lt;span&gt;      use: {
&lt;&#x2F;span&gt;&lt;span&gt;        testIdAttribute: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;data-test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        baseURL: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;UI_URL&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        apiURL: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;API_URL&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        apiBaseURL: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;API_URL&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        trace: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;tian-jia-wo-men-de-she-zhi-jiao-ben&quot;&gt;添加我们的设置脚本&lt;&#x2F;h2&gt;
&lt;p&gt;如你所见，&lt;code&gt;auth.setup.ts&lt;&#x2F;code&gt; 依赖于我在此文件（.env 和 LoginPage）下的一些代码块。因为这个设置文件被设置为一个项目，我们可以访问重命名为 &lt;code&gt;setup&lt;&#x2F;code&gt; 的测试块，以表明这些是初始化步骤而不是真正的测试。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;auth.setup.ts&lt;&#x2F;code&gt; 文件的前半部分为不同的电子邮件、密码和文件名设置变量。这些变量在每个设置块中使用。分解实际的 &lt;code&gt;setup&lt;&#x2F;code&gt; 步骤，包括：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;创建一个 LoginPage 类，以便我们可以利用页面对象&lt;&#x2F;li&gt;
&lt;li&gt;访问登录页面&lt;&#x2F;li&gt;
&lt;li&gt;使用登录异步函数，传入指定的电子邮件和密码&lt;&#x2F;li&gt;
&lt;li&gt;验证用户已登录&lt;&#x2F;li&gt;
&lt;li&gt;将 &lt;code&gt;storageState&lt;&#x2F;code&gt; 保存到指定文件&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;在三个不同用户（管理员、customer01 和 customer02）的三个设置块中重复这些步骤。&lt;&#x2F;p&gt;
&lt;p&gt;另一个需要注意的是，我在 &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; 中设置了 &lt;code&gt;fullyParallel: true&lt;&#x2F;code&gt;，这将在多个工作线程中运行时同时运行每个 setup 步骤。这将有助于加快 setup 步骤的速度。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;auth.setup.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 通过设置测试将你的存储状态保存到 .auth 目录中的文件
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;LoginPage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@pages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;adminEmail &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_USERNAME&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;adminPassword &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_PASSWORD&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;adminAuthFile &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.auth&#x2F;admin.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer01Email &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CUSTOMER_01_USERNAME&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer01Password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CUSTOMER_01_PASSWORD&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer01AuthFile &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.auth&#x2F;customer01.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer02Email &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CUSTOMER_02_USERNAME&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer02Password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CUSTOMER_02_PASSWORD&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer02AuthFile &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.auth&#x2F;customer02.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Create Admin Auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;LoginPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;adminEmail&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;adminPassword&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navAdminMenu&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;innerText&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;John Doe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;storageState&lt;&#x2F;span&gt;&lt;span&gt;({ path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;adminAuthFile &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Create Customer 01 Auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;LoginPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer01Email&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer01Password&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navUserMenu&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;innerText&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Jane Doe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;storageState&lt;&#x2F;span&gt;&lt;span&gt;({ path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer01AuthFile &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Create Customer 02 Auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;LoginPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer02Email&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer02Password&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navUserMenu&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;innerText&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Jack Howe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;storageState&lt;&#x2F;span&gt;&lt;span&gt;({ path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer02AuthFile &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;首先要注意的是，我使用了 &lt;code&gt;dotenv&lt;&#x2F;code&gt; 包来管理存储在 &lt;code&gt;.env&lt;&#x2F;code&gt; 文件中的环境变量。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; .env
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;# &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;URLS
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;UI_URL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;https:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;practicesoftwaretesting.com
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;API_URL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;https:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;api.practicesoftwaretesting.com
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;# &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Logins
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CUSTOMER_01_USERNAME&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;practicesoftwaretesting&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;com
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CUSTOMER_01_PASSWORD&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;welcome01
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CUSTOMER_02_USERNAME&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;customer2&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;practicesoftwaretesting&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;com
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CUSTOMER_02_PASSWORD&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;welcome01
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_USERNAME&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;admin&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;practicesoftwaretesting&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;com
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_PASSWORD&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;welcome01
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我还使用了 &lt;code&gt;登录页面&lt;&#x2F;code&gt; 的页面对象。这是从我在 &lt;code&gt;tsconfig.json&lt;&#x2F;code&gt; 文件中设置的 &lt;code&gt;@pages&lt;&#x2F;code&gt; 路径导入的。我在任何指南中都没有涵盖这一点，但计划很快写一些文章说明一下，现在只需知道它是一个不错的快捷方式，我可以使用它，而无需在导入中使用完整路径。在页面文件中，我们有一些定位器和两个方法。一个用于访问登录页面，另一个用于登录，需要传入电子邮件和密码作为变量。&lt;&#x2F;p&gt;
&lt;p&gt;这样做可以简化我的测试和设置文件。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;pages&#x2F;loginPage.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Page &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;LoginPage {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;submit &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;login-submit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navUserMenu &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nav-user-menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navAdminMenu &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nav-admin-menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navSignOut &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nav-sign-out&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;readonly &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navSignIn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nav-sign-in&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;UI_URL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&#x2F;sign-in&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;submit&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 添加一个短暂的延迟以确保登录过程完成
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;logout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navSignOut&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d699ff;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navSignIn&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitFor&lt;&#x2F;span&gt;&lt;span&gt;({ state: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;visible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;从页面对象中，我们返回到 &lt;code&gt;auth.setup.ts&lt;&#x2F;code&gt; 文件。我们创建了三个用于认证的文件，这些文件会随着页面一起使用。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; auth.setup.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Create Admin Auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;context &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;LoginPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;login&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;adminEmail&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;adminPassword&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navAdminMenu&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;innerText&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;John Doe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;storageState&lt;&#x2F;span&gt;&lt;span&gt;({ path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;adminAuthFile &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;一旦我们将 &lt;code&gt;setup&lt;&#x2F;code&gt; 设置为一个项目，我们就可以在 &lt;code&gt;ui-tests&lt;&#x2F;code&gt; 项目中使用这些存储的文件。我们可以在 &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; 文件中将其作为存储状态的一部分提供给 &lt;code&gt;context&lt;&#x2F;code&gt; 对象。我们通过设置 &lt;code&gt;storageState&lt;&#x2F;code&gt; 为 &lt;code&gt;authFile&lt;&#x2F;code&gt; 路径中的 &lt;code&gt;storageState&lt;&#x2F;code&gt; 属性来实现。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; playwright.config.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  projects: [
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      testMatch: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      fullyParallel: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui-tests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      dependencies: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;      use: {
&lt;&#x2F;span&gt;&lt;span&gt;        storageState: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.auth&#x2F;admin.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在此示例中，我们可以使用相同的方法为 &lt;code&gt;ui-tests&lt;&#x2F;code&gt; 项目提供任何 &lt;code&gt;authFile&lt;&#x2F;code&gt;。&lt;code&gt;authFile&lt;&#x2F;code&gt; 由实际测试使用，并且可以在存储状态下的 &lt;code&gt;use&lt;&#x2F;code&gt; 属性中设置。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;chuang-jian-wo-men-de-ce-shi&quot;&gt;创建我们的测试&lt;&#x2F;h2&gt;
&lt;p&gt;以下是我们需要的一个简单示例，展示如何在项目中实现这一点。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;auth.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Verify that the authenticated user can log out&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 通过使用存储状态文件，登录用户的状态会自动加载到页面中
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 断言用户已登录
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nav-admin-menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContainText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;John Doe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 调用登出方法
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;LoginPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loginPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;logout&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 断言用户已注销
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;nav-sign-in&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在此示例中，我们导入 &lt;code&gt;test&lt;&#x2F;code&gt; 和 &lt;code&gt;expect&lt;&#x2F;code&gt; 来自 &lt;code&gt;@playwright&#x2F;test&lt;&#x2F;code&gt;，并编写了一个简单的测试，展示了如何使用存储状态来验证用户是否已登录并能够注销。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h2&gt;
&lt;p&gt;如你所见，我们有很多不同的方法可以用来在 Playwright 项目中处理不同的登录状态。通过创建一个 setup 项目，并在 &lt;code&gt;ui-tests&lt;&#x2F;code&gt; 项目中使用存储状态文件，我们能够有效地管理多个用户的登录状态。这样可以确保每个测试都在正确的登录状态下执行，并且在注销测试后，其他测试不会受到影响。&lt;&#x2F;p&gt;
&lt;p&gt;我们希望这篇文章对你有所帮助，并激发你在 Playwright 项目中处理类似问题的灵感。如果你有任何问题或建议，请随时与我们联系！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源：https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;handling-multiple-login-states-between-different-tests-in-playwright&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间：2023-07-17T12:30:51.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>The Definitive Guide to API Test Automation With Playwright: Part 6 - Creating a DataFactory to Manage Test Data</title>
        <published>2023-07-10T00:00:00+00:00</published>
        <updated>2023-07-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-testcreating-a-datafactory-to-manage-test-data/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-testcreating-a-datafactory-to-manage-test-data/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-testcreating-a-datafactory-to-manage-test-data/">&lt;p&gt;I believe the hardest thing about Test Automation is creating and managing test data and system state. Today&#x27;s guide we are going to walk through how I typically tackle the test data problem in my test automation projects. Keep in mind there are multiple ways to solve this problem. If you&#x27;re just joining us now go check out the &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;introduction post&lt;&#x2F;a&gt; which links to all parts we&#x27;ve covered so far. For our examples we will use the playwright-api-test-demo repository linked below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&quot;&gt;GitHub&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;While working through this article I posted this online to get feedback from others. Most of the responses were around solving the problem. The answers included using faker library, using SQL or graphQL to create the data you need in your data storage within your tests, and using scrambled production data. Another response was &#x27;State&#x27; being the hardest problem in automation. I think that is the most correct answer, as data makes up what state the application is in, though you could argue that some applications use data to store the state of the application. (If you&#x27;re not following &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;FriendlyTester&quot;&gt;@FriendlyTester&lt;&#x2F;a&gt; on twitter you should be)&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I would say State. But test data makes up a big part of that&lt;&#x2F;p&gt;
&lt;p&gt;— Richard Bradshaw 🇺🇦 (@FriendlyTester) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;FriendlyTester&#x2F;status&#x2F;1675281540496912389?ref_src=twsrc%5Etfw&quot;&gt;July 1, 2023&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;defining-static-test-data&quot;&gt;Defining Static Test Data&lt;&#x2F;h2&gt;
&lt;p&gt;For the application we are testing (specifically the API), there are a few things to know before we get into my implementation. First the application defaults deletes any test data and re-seeds the database with a minimal amount of test data. For test data that I always expect to exist, I like to refer to this as static test data. This is data that I may hard code in a specific test or set of tests, and always rely it will be there. The rules I put around this data are, automation tests shouldn&#x27;t modify this data and users of the system shouldn&#x27;t modify the data. Enforcing this can be very difficult, which leads me to relying on this strategy for all my data. I typically only use static data when I am doing a POC proof of concept examples, at the beginning of an automation project with the goal of refactor to use generated data, or for high level objects that may only be created once (think highest level account or api keys).&lt;&#x2F;p&gt;
&lt;p&gt;I won&#x27;t be covering creating test data in xlsx, csv, or json files. I really look at this as a form of static test data, that may live within your automation repo that you create or assert with from existing files.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;creating-a-fake-random-data&quot;&gt;Creating a Fake Random Data&lt;&#x2F;h2&gt;
&lt;p&gt;The other option you have rather than creating static data is to create random data while the tests run for your testing needs. The tool I tend to use for this is &lt;code&gt;@faker-js&#x2F;faker&lt;&#x2F;code&gt;. This library has been useful for all my needs!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;@faker-js&#x2F;faker&quot;&gt;@faker-js&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The usage guide can be found here&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;fakerjs.dev&#x2F;guide&#x2F;usage.html&quot;&gt;Usage | Faker Generate massive amounts of fake (but reasonable) data for testing and development.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@faker-js&#x2F;faker&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randomName &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;person&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fullName&lt;&#x2F;span&gt;&lt;span&gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Rowan Nikolaus
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randomEmail &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;internet&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Kassandra.Haley@erich.biz
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;creating-a-datafactory&quot;&gt;Creating a Datafactory&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have a way to create random data, we need to find a way to structure this within our Playwright project. For this I will start by creating a &lt;code&gt;&#x2F;lib&lt;&#x2F;code&gt; folder within that folder we&#x27;ll create a &lt;code&gt;&#x2F;datafactory&lt;&#x2F;code&gt; folder. I&#x27;ll use this folder for any files that directly interact with the system either via &lt;code&gt;SQL&lt;&#x2F;code&gt; or &lt;code&gt;API&lt;&#x2F;code&gt; request in order to create test data. I use the &lt;code&gt;&#x2F;helpers&lt;&#x2F;code&gt; folder to store any helper functions that help arrange test data, or may even call a data factory method to create test data. An example of that would be the &lt;code&gt;createHeaders.ts&lt;&#x2F;code&gt; file which was covered in &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-4-handling-headers-and-authentication&#x2F;&quot;&gt;part 4&lt;&#x2F;a&gt; of the series.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;07&#x2F;image-4.png&quot; alt=&quot;Image 8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;auth-datafactory&quot;&gt;Auth Datafactory&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;code&gt;createHeaders.ts&lt;&#x2F;code&gt; helper file imports the &lt;code&gt;createCookies&lt;&#x2F;code&gt; function from the auth data factory file.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;helpers&#x2F;createHeaders.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createCookies &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;datafactory&#x2F;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_NAME&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_PASSWORD&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; a valid token to be used in the request if one is not provided cookies will be created from default username and password
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; a header object with the token set as a cookie
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * import { createHeaders } from &amp;quot;..&#x2F;lib&#x2F;helpers&#x2F;createHeaders&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * const headers = await createHeaders(token);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *     const response = await request.delete(`booking&#x2F;${bookingId}`, {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;      headers: headers,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    });
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestHeaders&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestHeaders &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;token=${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Authenticate and get cookies
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createCookies&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestHeaders &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestHeaders&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;auth.ts&lt;&#x2F;code&gt; file uses playwright request method to make an api call to either &lt;code&gt;createCookies&lt;&#x2F;code&gt; or &lt;code&gt;createToken&lt;&#x2F;code&gt;. I created both of these based off of needs within the tests. In these examples I am using JSDoc to provide documentation that is easy to read while working within the codebase. One thing to note, when working with exported functions I don&#x27;t have access to &lt;code&gt;baseURL&lt;&#x2F;code&gt; that Playwright provides within the test fixture, so I do have to set the URL. Another approach would be to create fixtures that could be used within a playwright test step, but I found this is easier for me to wrap my head around, and for new joiners within the team to comprehend. I&#x27;ll leave creating fixtures for areas where I need to extend my framework rather than managing test data.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;datafactory&#x2F;auth.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.URL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * Returns valid cookies for the given username and password.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * If a username and password aren&amp;#39;t provided &amp;quot;admin&amp;quot; and &amp;quot;password&amp;quot; will be used
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * import { createCookies } from &amp;quot;..&#x2F;datafactory&#x2F;auth&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * const cookies = createCookies(&amp;quot;Happy&amp;quot;, &amp;quot;Mcpassword&amp;quot;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * const response = await request.put(`booking&#x2F;${bookingId}`, {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;      headers: { cookie: cookies },
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;      data: body,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    });
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createCookies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;admin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;contextRequest &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;contextRequest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    data: {
&lt;&#x2F;span&gt;&lt;span&gt;      username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;set-cookie&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * Returns valid token for the given username and password.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * If a username and password aren&amp;#39;t provided &amp;quot;admin&amp;quot; and &amp;quot;password&amp;quot; will be used
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * import { createToken } from &amp;quot;..&#x2F;datafactory&#x2F;auth&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * const token = createToken(&amp;quot;Happy&amp;quot;, &amp;quot;Mcpassword&amp;quot;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * const response = await request.post(&amp;quot;auth&#x2F;validate&amp;quot;, {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;      data: { token: token },
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    });
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;admin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;contextRequest &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;contextRequest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    data: {
&lt;&#x2F;span&gt;&lt;span&gt;      username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tokenString &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;set-cookie&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;split&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tokenString&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;split&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;bookings-datafactory&quot;&gt;Bookings Datafactory&lt;&#x2F;h2&gt;
&lt;p&gt;Where this gets really interesting is when we start to solve harder &lt;code&gt;state&lt;&#x2F;code&gt; problems within our tests. For example, let&#x27;s say I want to utilize static data, the room id 1 which exists as a part of the seeded data, and I want to write a test that exercises the &lt;code&gt;DELETE&lt;&#x2F;code&gt; &lt;code&gt;booking&#x2F;{id}&lt;&#x2F;code&gt; endpoint. For this I first want to create a booking, and then use that id to delete the booking, and have some assertions on the response, which includes getting the booking summary to assert against. The spec can be seen below.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;booking&#x2F;booking.delete.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: DELETE &#x2F;booking&#x2F;{id}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingSummary&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createFutureBooking&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;datafactory&#x2F;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;{id} DELETE requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureBooking &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createFutureBooking&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureBooking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;DELETE booking with specific room id:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;delete&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;202&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBooking &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getBookingSummary&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBooking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The two datafactory methods &lt;code&gt;getBookingSummary&lt;&#x2F;code&gt; and &lt;code&gt;createFutureBooking&lt;&#x2F;code&gt; both interact with the underlying api to either get or create test data. Let&#x27;s first look at &lt;code&gt;getBookingSummary&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;datafactory&#x2F;bookings.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.URL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;: number for the booking you want to see the summary of
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; the body of the booking&#x2F;summary?roomid=${bookingId} endpoint
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getBookingSummary&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;summary?roomid=${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see this exported function accepts a bookingId as an input, and makes a unauthenticated &lt;code&gt;GET&lt;&#x2F;code&gt; call to &lt;code&gt;booking&#x2F;summary?roomid=${bookingId}&lt;&#x2F;code&gt;. It then makes a check to ensure the response code is 200 (success), and returns the body as a  json object. We can then use this in the assertion to ensure the length of the returned object which is a bookings array has a length of 0.  I abstracted this api call in the datafactory function that can be used for other similar needs. This was a simple example lets look at the &lt;code&gt;createFutureBooking&lt;&#x2F;code&gt; function.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;datafactory&#x2F;bookings.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;stringDateByDays &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;helpers&#x2F;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@faker-js&#x2F;faker&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.URL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * This function will create a booking with provided roomId and a checkinDate
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * A checkout date will be randomly generated between 1 and 4 days after the checkinDate
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;: number for the room to create a booking for
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; the body of the booking just created
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * This code is wrapped in an assert retry details can be found
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions#retrying
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createFutureBooking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;futureOpenCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randBookingLength &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;datatype&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;({ min: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, max: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;4 &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkInString &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toISOString&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;split&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOutString &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randBookingLength&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(&amp;quot;booking length: &amp;quot; + randBookingLength);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(&amp;quot;checkin string: &amp;quot; + checkInString);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(&amp;quot;checkout string: &amp;quot; + checkOutString);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      roomid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      firstname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.name.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;firstName&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;      lastname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.name.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;lastName&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;      depositpaid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;Math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;random&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;returns true or false
&lt;&#x2F;span&gt;&lt;span&gt;      email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;internet&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;      phone: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;phone&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;###########&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      bookingdates: {
&lt;&#x2F;span&gt;&lt;span&gt;        checkin: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkInString&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        checkout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOutString&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;201&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  }).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toPass&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    intervals: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1_000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2_000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5_000&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;20_000&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This function as you can see is a bit more complicated. When walking through the logic we see you first need to pass in a roomid and the function then:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Creates valid headers via datafactory to make authenticated api calls&lt;&#x2F;li&gt;
&lt;li&gt;Gets a valid available future checkin date from another datafactory method &lt;code&gt;futureOpenCheckinDate&lt;&#x2F;code&gt; that will be shown below in the whole booking.ts file&lt;&#x2F;li&gt;
&lt;li&gt;Creates a random amount of days between 1-4 to create a booking length&lt;&#x2F;li&gt;
&lt;li&gt;Sets checkin and checkout date string calculated from the previous two steps&lt;&#x2F;li&gt;
&lt;li&gt;Then use the faker library to build a booking body with fake data and checkin dates from previous steps&lt;&#x2F;li&gt;
&lt;li&gt;Then make an &lt;code&gt;POST&lt;&#x2F;code&gt; call with the body we built above to &lt;code&gt;booking&#x2F;&lt;&#x2F;code&gt; endpoint&lt;&#x2F;li&gt;
&lt;li&gt;Then validate the response code is 200&lt;&#x2F;li&gt;
&lt;li&gt;Then sets &lt;code&gt;body&lt;&#x2F;code&gt; variable to the response body in json format&lt;&#x2F;li&gt;
&lt;li&gt;All the above steps are wrapped in a &lt;code&gt;[toPass()](https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions#expecttopass)&lt;&#x2F;code&gt; method which is a newer method built into Playwright that allows you to retry the block of code if any of the assertions failed. This is here because if I run my tests in parallel it&#x27;s possible that the future date gets booked before the booking code runs resulting in &lt;code&gt;409&lt;&#x2F;code&gt; response codes.&lt;&#x2F;li&gt;
&lt;li&gt;Finally the body is returned for the newly created booking.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This datafactory function allows me to quickly create booking to use in my checks. I&#x27;ve built them in a way that they are flexible and re-usable, abstracting away a lot of the heavy logic from my tests. The full &lt;code&gt;booking.ts&lt;&#x2F;code&gt; file can be found below, there are some other methods we won&#x27;t be specifically covering in this article but are used within my test suite.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;datafactory&#x2F;booking.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;stringDateByDays &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;helpers&#x2F;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@faker-js&#x2F;faker&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.URL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingBody&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOutArray&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createRandomBookingBody&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;checkInString&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;checkOutString&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    roomid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    firstname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.name.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;firstName&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;    lastname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.name.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;lastName&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;    depositpaid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;Math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;random&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;returns true or false
&lt;&#x2F;span&gt;&lt;span&gt;    email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;internet&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;    phone: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;phone&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;###########&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    bookingdates: {
&lt;&#x2F;span&gt;&lt;span&gt;      checkin: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkInString&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      checkout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOutString&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingBody&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * This function will create a booking with provided roomId and a checkinDate
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * A checkout date will be randomly generated between 1 and 4 days after the checkinDate
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;: number for the room to create a booking for
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; the body of the booking just created
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * This code is wrapped in an assert retry details can be found
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-assertions#retrying
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createFutureBooking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;futureOpenCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randBookingLength &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;datatype&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;({ min: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, max: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;4 &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkInString &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toISOString&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;split&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOutString &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;randBookingLength&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(&amp;quot;booking length: &amp;quot; + randBookingLength);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(&amp;quot;checkin string: &amp;quot; + checkInString);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(&amp;quot;checkout string: &amp;quot; + checkOutString);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      roomid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      firstname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.name.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;firstName&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;      lastname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.name.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;lastName&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;      depositpaid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;Math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;random&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;returns true or false
&lt;&#x2F;span&gt;&lt;span&gt;      email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;internet&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;      phone: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;phone&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;###########&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      bookingdates: {
&lt;&#x2F;span&gt;&lt;span&gt;        checkin: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkInString&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        checkout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOutString&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;201&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  }).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toPass&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    intervals: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1_000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2_000&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5_000&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;20_000&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * This function will return all the bookings for a roomId
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;: number for the room you want to get the bookings for
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; the body of the bookings for the room
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getBookings&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;?roomid=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(JSON.stringify(body));
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;: number for the booking you want to see the summary of
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; the body of the booking&#x2F;summary?roomid=${bookingId} endpoint
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getBookingSummary&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;summary?roomid=${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; number for the booking you want to see the details of
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; the body of the booking&#x2F;${bookingId} endpoint
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getBookingById&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; the most future checkout date for a room
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *  let futureCheckinDate = await futureOpenCheckinDate(roomId);        &#x2F;&#x2F; &amp;quot;2023-03-31T00:00:00.000Z&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *  let checkInString = futureCheckinDate.toISOString().split(&amp;quot;T&amp;quot;)[0];  &#x2F;&#x2F; &amp;quot;2023-03-31&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *  let checkOutString = stringDateByDays(futureCheckinDate, 2);        &#x2F;&#x2F; &amp;quot;2023-04-02&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;futureOpenCheckinDate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;currentBookings &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getBookings&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOutArray &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;Array();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Iterate through current bookings and get checkout dates
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;currentBookings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;.length); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;today &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;Date();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOut &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;Date(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;currentBookings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;today &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOut&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; pushing the checkout date into an array
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOutArray&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOut&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Find the most future checkout date and return it if no future dates exist return today
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mostFutureDate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOutArray
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;sort&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      })
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;pop&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| new &lt;&#x2F;span&gt;&lt;span&gt;Date();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(&amp;quot;Last Checkout Date: &amp;quot; + mostFutureDate);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;mostFutureDate&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;adding-a-room-datafactory&quot;&gt;Adding a Room Datafactory&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have a way to quickly create bookings for any room, we still have a data problem in one of our earlier tests. &lt;code&gt;POST new booking with full body&lt;&#x2F;code&gt; from the &lt;code&gt;POST&lt;&#x2F;code&gt; &lt;code&gt;booking&#x2F;&lt;&#x2F;code&gt; endpoint. This test would sometimes pass and sometimes fail due to an error trying to book the hardcoded roomid=1. Let&#x27;s go ahead and build a datafactory function that will create a new room for this test and implement it.&lt;&#x2F;p&gt;
&lt;p&gt;The first thing I did was use the &lt;code&gt;POST&lt;&#x2F;code&gt; &lt;code&gt;room&#x2F;&lt;&#x2F;code&gt; endpoint in the VS Code Thunder API client, while using the &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;room&#x2F;swagger-ui&#x2F;index.html#&#x2F;&quot;&gt;swagger page&lt;&#x2F;a&gt; to understand more about how the endpoint requirements. The first thing I noticed is we do need a valid cookie, so this will be an authenticated request. The 2nd thing after making a &lt;code&gt;POST&lt;&#x2F;code&gt; request is that the example &lt;code&gt;POST&lt;&#x2F;code&gt; body that was given as an example includes roomid, but it looks like that is a value set by the application, not something that can be set from the api request. This seems like a bug in the application is any value I add to roomid it never gets used, and always gets overwritten to a new unused sequential value from the system under test.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;roomName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Suite&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;accessible&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;roomPrice&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;999
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;07&#x2F;image-5.png&quot; alt=&quot;Image 9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Example of request body | response&lt;&#x2F;p&gt;
&lt;p&gt;Knowing this I decided to leave out the &lt;code&gt;roomid&lt;&#x2F;code&gt; as a field that I pass in via the &lt;code&gt;POST&lt;&#x2F;code&gt; body. Below is the full &lt;code&gt;room.ts&lt;&#x2F;code&gt; datafactory file. It includes 2 exported functions. The first to &lt;code&gt;createRandomRoomBody()&lt;&#x2F;code&gt;, with two optional parameters, &lt;code&gt;roomName&lt;&#x2F;code&gt; and &lt;code&gt;roomPrice&lt;&#x2F;code&gt;. This is used by the 2nd function &lt;code&gt;createRoom()&lt;&#x2F;code&gt;. With these datafactory functions we can now implement them within the &lt;code&gt;POST new booking spec&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;datafactory&#x2F;room.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@faker-js&#x2F;faker&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.URL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createRandomRoomBody&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;roomName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;roomPrice&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomType &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Single&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Double&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Twin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;features &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;TV&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;WiFi&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Safe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Mini Bar&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Tea&#x2F;Coffee&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Balcony&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    roomName: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomName &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;random&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;numeric&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    type: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomType&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;Math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;floor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;Math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;random&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomType&lt;&#x2F;span&gt;&lt;span&gt;.length)], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; returns a random value from the array
&lt;&#x2F;span&gt;&lt;span&gt;    accessible: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;Math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;random&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;returns true or false
&lt;&#x2F;span&gt;&lt;span&gt;    image: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;imageUrl&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;500&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;500&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cat&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    description: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;hacker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;phrase&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;    features: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;features&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;sort&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0.5 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;Math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;random&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;slice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; returns 3 random values from the array
&lt;&#x2F;span&gt;&lt;span&gt;    roomPrice: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomPrice &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;random&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;numeric&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomBody&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * This function will create a room with provided name and a price
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;: string for the room to create
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomPrice&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;: number for the price of the room
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; the body of the room just created with a unique roomid in the response
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * let room = await createRoom(&amp;quot;My Room&amp;quot;, 100);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * let roomId = room.roomid;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createRoom&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;roomName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;roomPrice&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createRandomRoomBody&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomName&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomPrice&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;room&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;201&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Using the image below you can see the refactor to the test with the datafactory already built only took 4 lines of code to be modified. We removed the hardcoded value and then set the roomId to the newly created &lt;code&gt;roomid&lt;&#x2F;code&gt; that was created by the datafactory.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;07&#x2F;image-6.png&quot; alt=&quot;Image 10&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now that I have the room datafactory I could refactor all of my tests very quickly to always create it&#x27;s own data.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;abstractions-are-fun&quot;&gt;Abstractions are Fun!&lt;&#x2F;h2&gt;
&lt;p&gt;Really all we&#x27;ve done through the datafactory is added a layer of &lt;a href=&quot;https:&#x2F;&#x2F;stackify.com&#x2F;oop-concept-abstraction&#x2F;&quot;&gt;abstraction&lt;&#x2F;a&gt;, making are tests more readable, and building functions that can be used over and over again throughout our API tests. An abstractions is a core principle of &lt;a href=&quot;https:&#x2F;&#x2F;www.techtarget.com&#x2F;searchapparchitecture&#x2F;definition&#x2F;object-oriented-programming-OOP#:~:text=Object%2Doriented%20programming%20(OOP)%20is%20a%20computer%20programming%20model,has%20unique%20attributes%20and%20behavior.&quot;&gt;Object Oriented Programming&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;07&#x2F;ezgif.com-optimize.gif&quot; alt=&quot;Image 11&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The pull request for these changes can be found below&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pull&#x2F;7&quot;&gt;adding a rooms datafactory and updating booking post spec by BMayhew · Pull Request #7&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks for reading! If you found this helpful, reach out and let me know on &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; or consider &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;buying me a cup of coffee&lt;&#x2F;a&gt;. If you want more content delivered to you in your inbox subscribe below, and be sure to leave a ❤️ to show some love.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-testcreating-a-datafactory-to-manage-test-data&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-07-10T12:30:12.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>playwright社区一些非常好用的工具</title>
        <published>2023-07-03T00:00:00+00:00</published>
        <updated>2023-07-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-community-highlight/"/>
        <id>https://playwright.itest.info/blog/playwright-community-highlight/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-community-highlight/">&lt;p&gt;本周我想尝试一些不同的内容。我不是准备下一篇文章，而是想要聚焦 Luc Gagan 对 Playwright 社区的贡献。Luc 加入社区才几个月，但在他的网站 https:&#x2F;&#x2F;ray.run 上已经创造了许多优质内容、教程和工具。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ask-ai&quot;&gt;Ask AI&lt;&#x2F;h2&gt;
&lt;p&gt;Luc 发布的工具中，我最喜欢的是 Ask AI（https:&#x2F;&#x2F;ray.run&#x2F;ask）。这个工具让人印象深刻，它有类似 ChatGPT 的界面，可以回答你关于 Playwright 的问题，并提供代码示例。&lt;&#x2F;p&gt;
&lt;p&gt;另一个我常用的工具集是 https:&#x2F;&#x2F;ray.run&#x2F;tools。这里有大量实用工具，得益于内置的键盘快捷搜索功能，使用起来非常方便。converters、formatters、sorters、encoders 和 decoders 应有尽有！这已经成为我的新宠，因为所有工具都在同一页面上，而且没有广告！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playwright-xiang-guan-gong-zuo&quot;&gt;Playwright 相关工作&lt;&#x2F;h2&gt;
&lt;p&gt;最后我要介绍的是一个专门列出 Playwright 相关工作的平台 - https:&#x2F;&#x2F;ray.run&#x2F;jobs。虽然目前列表还不长，但随着 Playwright 日益普及和被广泛采用，我相信这里将成为寻找下一份 Playwright 工作的首选之地。&lt;&#x2F;p&gt;
&lt;p&gt;除此之外，还有大量资源以及精彩的博客文章和教程。感谢 Luc 为 Playwright 社区所做的一切贡献！&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读！如果你觉得这篇文章有帮助，欢迎在 LinkedIn 上与我联系，或者考虑请我喝杯咖啡。如果你想收到更多内容，可以在下方订阅。别忘了点个赞来表达你的喜爱哦！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源：https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;playwright-community-highlight&#x2F;
发布时间：2023 年 7 月 3 日 12:30:45&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在playwright中创建自定义断言</title>
        <published>2023-06-26T00:00:00+00:00</published>
        <updated>2023-06-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/creating-custom-expects-in-playwright-how-to-write-your-own-assertions/"/>
        <id>https://playwright.itest.info/blog/creating-custom-expects-in-playwright-how-to-write-your-own-assertions/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/creating-custom-expects-in-playwright-how-to-write-your-own-assertions/">&lt;p&gt;我喜欢阅读，特别是在气温在 80 华氏度中段的时候。坐在泳池边，手捧一本好书，是多么惬意的享受。当一本书写得很好时，阅读起来就很轻松，我可以花几个小时翻页，让我的想象力创造场景和人物……其实代码也是一样的。当代码写得很好时，我通过阅读它就能轻松理解测试&#x2F;断言在做什么，就像读书一样。我尽量写出可读性强的代码：从有意义的变量名到在逻辑上合适的地方添加空行（“意大利面条类型的代码”我实在看不下去）。&lt;&#x2F;p&gt;
&lt;p&gt;Playwright 可以比较容易的编写出可读性强的代码。以下是一些示例：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;student&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;grade&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThanOrEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;student&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;loan&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeTruthy&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;student&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;employer&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeNull&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;mo-ren-duan-yan&quot;&gt;默认断言&lt;&#x2F;h2&gt;
&lt;p&gt;这是一个通用值断言的&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-genericassertions&quot;&gt;链接&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;但是如果你期望一个字符串而你不太关心其值，只想检查其类型该怎么办呢？不幸的是，没有开箱即用的方法，所以我以前是这样写的：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 我以前如何检查字符串
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;framework &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;framework&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我不是说它不可读，但我更希望代码写成这样：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 我现在如何检查字符串
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;framework &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;framework&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeString&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果你阅读了上面代码块中的注释（另一种使代码可读的方法），你可能已经猜到可以编写自己的 expect。你来到这个页面是为了扩展你的知识，现在我们要扩展你的 expect！&lt;&#x2F;p&gt;
&lt;p&gt;要使 &lt;code&gt;toBeString()&lt;&#x2F;code&gt; 检查工作，你需要在 &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; 中添加以下内容&lt;&#x2F;p&gt;
&lt;p&gt;playwright.config.ts👇&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeString&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;check &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;passed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;toBeString() 断言失败。&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你期望 &amp;#39;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;#39; 是一个字符串，但它是一个 ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;typeof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;解释：我们通过添加 &lt;code&gt;toBeString()&lt;&#x2F;code&gt; 断言扩展了我们的 expect。即使看起来断言接受一个参数，但实际上并没有。 &lt;code&gt;received&lt;&#x2F;code&gt; 是 &lt;code&gt;expect(value)&lt;&#x2F;code&gt; 的第一个括号中的值。然后我们检查接收到的值是否是字符串类型，如果是，则检查通过。如果不是，我们就让检查失败，并通过添加自定义消息使代码更具可读性。&lt;&#x2F;p&gt;
&lt;p&gt;让我们看看它是如何工作的。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;123&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeString&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个断言会失败 ☹️&lt;&#x2F;p&gt;
&lt;p&gt;由于在这个例子中 &lt;code&gt;value&lt;&#x2F;code&gt; 显然不是字符串，所以我预计它会失败。果然不出所料，注意自定义的错误信息，这个很好 👇。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;06&#x2F;2023-06-24_09-35-32-1.jpg&quot; alt=&quot;图片2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我还在我的工具包中添加了 &lt;code&gt;.toBeNumber()&lt;&#x2F;code&gt; 和 &lt;code&gt;.toBeBoolean()&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;几周前，我写了 &lt;code&gt;.toBeOneOfValues()&lt;&#x2F;code&gt;，这是一个改变游戏规则的方法。我在无法预测确切值的时候使用它，但我知道它应该是已知值之一。我主要用于为 GET 请求编写 API 检查。例如，当我有一个返回的收费参数，它可能是“pending”（待处理）、“failed”（失败）或“successful”（成功），我只想检查每一个返回的结果都有一个预期的值。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validValues &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;failed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pending&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;successful&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;testValue &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;created&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;testValue&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeOneOfValues&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;validValues&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在底层我们将检查数组是否包含传递的值&lt;&#x2F;p&gt;
&lt;p&gt;同样，它会打印出自定义的错误信息 👇。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;06&#x2F;2023-06-24_10-56-02.jpg&quot; alt=&quot;图片3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;以下是需要添加到 &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; 中的源代码&lt;&#x2F;p&gt;
&lt;p&gt;playwright.config.ts&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;extend&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeOneOfValues&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;check &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;通过&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;toBeOneOfValues() 断言失败。&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你期望 [${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}] 包含 &amp;#39;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;received&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        pass: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      };
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这里最重要的收获是要意识到你可以创建你需要的自定义断言，以提高可读性。对我有帮助的是查看我要检查的值，并在脑海中说“我希望这个值……”然后用英语完成这个句子。这样你就得到了伪代码。(中文用户表示增加英文的可读性其实 😂，你懂的)&lt;&#x2F;p&gt;
&lt;p&gt;这篇文章是基于&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-configuration#add-custom-matchers-using-expectextend&quot;&gt;此文档&lt;&#x2F;a&gt;编写的。如果你使用 TypeScript，则需要额外的一步使自定义断言工作，这涉及添加一个 &lt;code&gt;global.d.ts&lt;&#x2F;code&gt; 文件。以下是我为上述两个 expect 编写的文件。&lt;&#x2F;p&gt;
&lt;p&gt;global.d.ts&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span&gt;{};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;declare &lt;&#x2F;span&gt;&lt;span&gt;global {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;namespace &lt;&#x2F;span&gt;&lt;span&gt;PlaywrightTest {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;interface &lt;&#x2F;span&gt;&lt;span&gt;Matchers&amp;lt;R&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeOneOfValues&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;R;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeString&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;R;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;creating-custom-expects-in-playwright-how-to-write-your-own-assertions&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-06-26T12:30:49.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在playwright的测试用例里参数化被测接口的域名? </title>
        <published>2023-06-19T00:00:00+00:00</published>
        <updated>2023-06-19T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-do-you-define-an-apiurl-along-with-the-baseurl-in-playwright/"/>
        <id>https://playwright.itest.info/blog/how-do-you-define-an-apiurl-along-with-the-baseurl-in-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-do-you-define-an-apiurl-along-with-the-baseurl-in-playwright/">&lt;p&gt;最近有人问我如何在 Playwright 测试中处理 apiURL 和 baseURL。Playwright 的一大优势是可以直接进行 API 调用来创建数据,或者拦截网络请求以获取用于断言的数据。并非所有人都会遇到这个问题,但如果你的 API 和 UI 使用不同的 URL,希望这篇文章能对你有所帮助。&lt;&#x2F;p&gt;
&lt;p&gt;我发现有 4 种方法可以解决这个问题:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;在测试中用例里硬编码 API URL&lt;&#x2F;li&gt;
&lt;li&gt;使用 process.env 环境变量&lt;&#x2F;li&gt;
&lt;li&gt;使用类来创建公共值&lt;&#x2F;li&gt;
&lt;li&gt;使用 Fixtures(我会介绍两种不同的方法)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;在示例中,我将使用&lt;a href=&quot;https:&#x2F;&#x2F;practicesoftwaretesting.com&#x2F;&quot;&gt;Practice Software Testing&lt;&#x2F;a&gt;,这是一个现代化的结账体验演示网站,由&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;TheWebTester&quot;&gt;Roy De Kleijn&lt;&#x2F;a&gt;创建。以下是一些了解该网站的资源:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;api.practicesoftwaretesting.com&#x2F;api&#x2F;documentation&quot;&gt;Practice Software Testing API Swagger 文档&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;TheWebTester&#x2F;status&#x2F;1593906285300400128&quot;&gt;Twitter 上介绍不同选项的帖子&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;testsmith-io&#x2F;practice-software-testing&quot;&gt;Practice Software Testing GitHub 仓库&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;zai-ce-shi-zhong-ying-bian-ma-api-url&quot;&gt;在测试中硬编码 API URL&lt;&#x2F;h2&gt;
&lt;p&gt;第一种方法是直接在代码中使用 API URL,这并不推荐。因为如果你想测试本地环境、临时环境或沙盒环境,就需要在多处更新代码,这并不理想。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-process-env-huan-jing-bian-liang&quot;&gt;使用 process.env 环境变量&lt;&#x2F;h2&gt;
&lt;p&gt;第二种方法是在代码中使用环境变量。这也不是最佳方案,但可以让你的项目快速启动。当你有相当数量的测试后,可能需要进一步抽象。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-lei-lai-chuang-jian-gong-gong-zhi&quot;&gt;使用类来创建公共值&lt;&#x2F;h2&gt;
&lt;p&gt;第三种方法是使用静态类。在类中,你可以定义一个公共静态变量,在导入类时可以在任何 Playwright 文件中使用。这种方法非常灵活且可扩展,因为你还可以添加多个静态数据信息或环境变量,同时还能享受智能提示的便利!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;helpers&#x2F;staticVariables.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span&gt;StaticVariables {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;public static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;staticApiURL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;API_URL&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;以下 3 个例子可以在名为&lt;code&gt;login.ts&lt;&#x2F;code&gt;的数据工厂文件中看到。这个数据工厂函数&lt;code&gt;getLoginToken()&lt;&#x2F;code&gt;的作用是在 UI 测试中使用任意邮箱&#x2F;密码组合进行 API 调用,获取认证 token,然后将其保存到会话存储中,以便在 Angular UI 应用程序中进行身份验证。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;datafactory&#x2F;login.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;StaticVariables &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;helpers&#x2F;staticVariables&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiURL&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 硬编码URL
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiURL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;api.practicesoftwaretesting.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 直接使用环境变量
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiURL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;API_URL&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用专用类访问变量
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiURL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;StaticVariables&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;staticApiURL&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getLoginToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRequestContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiURL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;users&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    data: {
&lt;&#x2F;span&gt;&lt;span&gt;      email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;access_token&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这 3 种方法中,我最喜欢的是第 3 种。这种方法可以在任何 TypeScript 文件中使用,这让我觉得它是一个很好的前进方向。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-fixtures-chuang-jian-apiurl&quot;&gt;使用 Fixtures 创建 apiURL&lt;&#x2F;h2&gt;
&lt;p&gt;我将提供两种方法。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fang-fa-1-shi-yong-page-ts-fixture&quot;&gt;方法 1:使用 page.ts fixture&lt;&#x2F;h3&gt;
&lt;p&gt;这是我最初解决问题的方法。这种方法创建了一个 fixture 来扩展&lt;code&gt;test&lt;&#x2F;code&gt;,在 Test Options 中添加了&lt;code&gt;apiURL&lt;&#x2F;code&gt;类型的字符串。默认情况下,如果没有提供 apiURL,将分配一个空字符串。我把下面的例子添加到了&lt;code&gt;lib&#x2F;pages.ts&lt;&#x2F;code&gt;文件中,这个文件是我用于基础页面对象的,这样我就不用在每个 UI 测试中导入所有文件。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;pages.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;pages&#x2F;loginPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;pages&#x2F;homePage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;pages&#x2F;checkoutPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span&gt;TestOptions &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiURL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这允许你在playwright.config.ts中设置apiURL
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;extend &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;TestOptions &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  {
&lt;&#x2F;span&gt;&lt;span&gt;    apiURL: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { option: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;}],
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;要使用这个方法,你必须在 Playwright 测试用例中从 lib&#x2F;pages 导入 test,并在&lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt;中设置&lt;code&gt;apiURL&lt;&#x2F;code&gt;(注意我们这里使用的是&lt;code&gt;apiURL&lt;&#x2F;code&gt;,下一节我们将使用&lt;code&gt;apiBaseURL&lt;&#x2F;code&gt;。我这样做主要是为了看两个 fixtures 如何并行工作)。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; playwright.config.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import type &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;APIRequestOptions &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;lib&#x2F;fixtures&#x2F;apiRequest&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;TestOptions &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;lib&#x2F;pages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dotenv&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;APIRequestOptions&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp;
&lt;&#x2F;span&gt;&lt;span&gt;  (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;TestOptions &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      testDir: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;tests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      fullyParallel: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      forbidOnly: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!!&lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      retries: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      workers: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CI &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;undefined&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      reporter: [[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;list&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]],
&lt;&#x2F;span&gt;&lt;span&gt;      use: {
&lt;&#x2F;span&gt;&lt;span&gt;        baseURL: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;UI_URL&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        apiURL: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;API_URL&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        apiBaseURL: process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;API_URL&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        trace: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;retain-on-failure&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在下面的测试文件中,注意我们从&quot;..&#x2F;lib&#x2F;pages&quot;导入&lt;code&gt;test&lt;&#x2F;code&gt;,这让我们可以在 beforeEach 块中使用在&lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt;中设置的 apiURL。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;checkoutWithPageFixture.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CheckoutPage&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;HomePage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;lib&#x2F;pages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用Page Fixture的基本UI检查&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;USERNAME &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;PASSWORD &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;apiURL &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用fixture中的apiBaseURL通过API调用获取登录token
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiURL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;users&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: {
&lt;&#x2F;span&gt;&lt;span&gt;        email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;access_token&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用登录token设置本地存储,使用户保持登录状态
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;addInitScript&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      window.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;localStorage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setItem&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth-token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;添加到购物车并结账&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;HomePage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;CheckoutPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;product2&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addToCart&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navCart&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;proceed1&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;proceed2&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;address&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;123 test street&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;city&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testville&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;country&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;united states&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;postcode&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;12345&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;proceed3&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paymentMethod&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;selectOption&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;2: Cash on Delivery&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accountName&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accountNumber&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;1234124&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finish&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;success&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;fang-fa-2-shi-yong-apirequests-ts-fixture&quot;&gt;方法 2:使用 apiRequests.ts fixture&lt;&#x2F;h3&gt;
&lt;p&gt;这种方法的功劳完全归于&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;yury-s&quot;&gt;Yury Semikhatsky&lt;&#x2F;a&gt;,他在为添加&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright&#x2F;issues&#x2F;23738&quot;&gt;api 接口 baseURL&lt;&#x2F;a&gt;的功能请求提供反馈时提出了这个方法(如果感兴趣,可以去给这个请求点个 👍)。&lt;&#x2F;p&gt;
&lt;p&gt;这种方法与第一种 fixture 方法非常相似,但 Yury 更进一步,不仅创建了一个可以从&lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt;文件导入的&lt;code&gt;apiBaseURL&lt;&#x2F;code&gt; TestOption(与上面的文件相同),还用&lt;code&gt;apiRequest&lt;&#x2F;code&gt;扩展了&lt;code&gt;test&lt;&#x2F;code&gt;,当调用时会默认使用&lt;code&gt;apiBaseURL&lt;&#x2F;code&gt;替换&lt;code&gt;baseURL&lt;&#x2F;code&gt;。这非常巧妙,但如果你与项目中的初级开发人员一起工作,可能不太直观。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;fixtures&#x2F;apiReqeusts.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;APIRequestContext&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span&gt;APIRequestOptions &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiBaseURL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span&gt;APIRequestFixture &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiRequest&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;APIRequestContext,
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这个fixture会在使用时用playwright.config.ts中的apiBaseURL覆盖baseURL
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;  (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;base&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;extend &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;APIRequestOptions&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;amp;
&lt;&#x2F;span&gt;&lt;span&gt;  (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;APIRequestFixture &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      apiBaseURL: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { option: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true &lt;&#x2F;span&gt;&lt;span&gt;}],
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;apiRequest&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;apiBaseURL &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiRequestContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;          baseURL: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiBaseURL&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiRequestContext&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiRequestContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;dispose&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在 spec 中的实现需要你从&lt;code&gt;..&#x2F;lib&#x2F;fixtures&#x2F;apiRequest&lt;&#x2F;code&gt;文件导入&lt;code&gt;test&lt;&#x2F;code&gt;,当使用&lt;code&gt;apiRequest&lt;&#x2F;code&gt;进行 API 调用时,你甚至不需要传入 baseURL,它会自动用&lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt;中的 apiBaseURL 替换 baseURL。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;checkoutWithApiFixture.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;lib&#x2F;fixtures&#x2F;apiRequest&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;CheckoutPage&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;HomePage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;lib&#x2F;pages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用API Fixture的基本UI检查&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;USERNAME &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;PASSWORD &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;apiRequest &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用fixture中的apiBaseURL通过API调用获取登录token,但全部都在fixture中,所以你甚至不需要在测试中添加apiURL
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;apiRequest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;users&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: {
&lt;&#x2F;span&gt;&lt;span&gt;        email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;access_token&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 使用登录token设置本地存储,使用户保持登录状态
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;addInitScript&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      window.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;localStorage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setItem&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth-token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;添加到购物车并结账&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;HomePage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span&gt;CheckoutPage(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;product2&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;addToCart&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;homePage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navCart&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;proceed1&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;proceed2&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;address&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;123 test street&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;city&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testville&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;country&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;united states&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;postcode&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;12345&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;proceed3&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paymentMethod&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;selectOption&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;2: Cash on Delivery&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accountName&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accountNumber&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;1234124&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finish&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkoutPage&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;success&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Yury 的代码库&#x2F;示例可以在这里找到:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;yury-s&#x2F;bug-23738&quot;&gt;GitHub - yury-s&#x2F;bug-23738: bug-23738 bug-23738&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;本文中的所有代码示例都可以在这个仓库中找到:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-practicesoftwaretesting.com&#x2F;tree&#x2F;main&quot;&gt;GitHub - playwrightsolutions&#x2F;playwright-practicesoftwaretesting.com&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;总结一下,有很多方法可以解决这个问题,我相信还有更多我没有涉及到的方法,但希望这些方法中的一种能让你的 Playwright 测试更加简洁和易于维护。如果你希望 Playwright 原生支持这个功能,请为这个功能请求投票!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright&#x2F;issues&#x2F;23738&quot;&gt;[Feature]: 在 playwright.config.ts 中添加 apiEndpoint(类似于 baseUrl) · Issue #23738&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读!如果你觉得这篇文章有帮助,可以在&lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt;上联系我,或者考虑&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;给我买杯咖啡&lt;&#x2F;a&gt;。如果你想接收更多内容,可以在下方订阅,别忘了点个 ❤️ 表示支持。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-do-you-define-an-apiurl-along-with-the-baseurl-in-playwright&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-06-19T12:30:01.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在运行 Playwright 测试时获取底层网络请求的响应值？</title>
        <published>2023-06-12T00:00:00+00:00</published>
        <updated>2023-06-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/get-a-response-value-of-an-underlying-network-request-when-running-a-playwright-test/"/>
        <id>https://playwright.itest.info/blog/get-a-response-value-of-an-underlying-network-request-when-running-a-playwright-test/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/get-a-response-value-of-an-underlying-network-request-when-running-a-playwright-test/">&lt;p&gt;在页面上进行自动化操作时，我经常需要获取刚刚创建的某个对象的 &lt;code&gt;id&lt;&#x2F;code&gt; 或唯一标识符，用于断言或后续操作。通常，这些标识符可以在 UI 层操作的网络响应中找到。今天，我将展示如何抓取网络响应并在断言中使用它。&lt;&#x2F;p&gt;
&lt;p&gt;在这个测试中，我们将使用 &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;&lt;&#x2F;a&gt;，这是一个预订网站。今天要抓取的具体值是登录后的消息通知数，该数值会根据系统中其他操作的不同而产生变化。步骤如下：访问上述网站，找到管理员登录链接，使用用户名: admin &#x2F; 密码: password 并点击登录。你会在页面顶部看到通知框，如果查看 Chrome 开发工具的 Network tab 页，也会看到网络请求。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;06&#x2F;image.png&quot; alt=&quot;Image 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆 消息计数的 UI&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;06&#x2F;image-1.png&quot; alt=&quot;Image 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆 Chrome 开发工具中的网络请求&lt;&#x2F;p&gt;
&lt;p&gt;在我的 Playwright 测试中，我计划使用 &lt;code&gt;page.route&lt;&#x2F;code&gt; 方法拦截流量。如果请求 URL 包含 &lt;code&gt;**&#x2F;message&#x2F;count&lt;&#x2F;code&gt;，则将响应设置为 &lt;code&gt;response&lt;&#x2F;code&gt; 变量，然后从 &lt;code&gt;response&lt;&#x2F;code&gt; 变量获取 json 并保存为名为 message 的变量。&lt;&#x2F;p&gt;
&lt;p&gt;需要注意的是，我在测试级别创建了 &lt;code&gt;message&lt;&#x2F;code&gt; 变量，这样可以在整个测试范围内访问它（而不仅仅是在 page.route 范围内）。如果遇到变量未定义的问题，请确保在正确的级别创建变量。&lt;&#x2F;p&gt;
&lt;p&gt;此外，我使用了 &lt;code&gt;let&lt;&#x2F;code&gt; 声明 &lt;code&gt;message&lt;&#x2F;code&gt; 变量，这样可以根据需要分配&#x2F;重新分配值。&lt;&#x2F;p&gt;
&lt;p&gt;下面是一个具体例子，我在其中拦截了 message&#x2F;count API 请求，并将 message 这个变量的值设置为请求的具体响应值。然后使用该 message 变量对屏幕上的内容进行断言。无论 API 返回什么值，我们将在 DOM 中检查该值。这确实帮助我们解决了测试数据问题，并且不需要用到 api mock。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;admin Checks&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Validate Message Count is correct&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;**&#x2F;message&#x2F;count&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;message &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;continue&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Let me hack!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Admin panel&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-testid=&amp;quot;username&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;admin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-testid=&amp;quot;password&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[data-testid=&amp;quot;submit&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Logout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveText&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Logout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;messageCountSpan &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[href*=&amp;quot;#&#x2F;admin&#x2F;messages&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Wait for the message count to be updated before making an assertion
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForResponse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;**&#x2F;message&#x2F;count&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;messageCountSpan&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个代码可以在 Playwright-Demo 仓库中找到&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;playwright-demo&#x2F;blob&#x2F;master&#x2F;tests&#x2F;ui&#x2F;automationintesting.online&#x2F;messageCountIntercept.spec.ts&quot;&gt;playwright-demo&#x2F;messageCountIntercept.spec.ts&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果你想要一个模拟响应的示例，可以在这里找到&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-do-i-intercept-network-traffic-and-save-specific-values-from-http-requests&#x2F;&quot;&gt;如何在 Playwright 中通过特定响应模拟 HTTP 网络流量&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Tim Deschryver 撰写的精彩文章，包含更多示例，可以在这里找到&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;timdeschryver.dev&#x2F;blog&#x2F;intercepting-http-requests-with-playwright&quot;&gt;使用 Playwright 拦截 HTTP 请求&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;06&#x2F;image-2.png&quot; alt=&quot;Image 9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读！如果你觉得这篇文章有帮助，请在 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; 上与我联系，或者考虑 &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;请我喝杯咖啡&lt;&#x2F;a&gt;。如果你希望更多内容直接发送到你的收件箱，请在下方订阅，并确保留下 ❤️ 以示支持。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;来源网址: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;get-a-response-value-of-an-underlying-network-request-when-running-a-playwright-test&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-06-12T12:30:52.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>一种更好的方法来通过测试名称控制 before and after 钩子函数</title>
        <published>2023-06-05T00:00:00+00:00</published>
        <updated>2023-06-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/a-better-way-to-control-before-and-after-blocks-with-test-titles-in-playwright-test/"/>
        <id>https://playwright.itest.info/blog/a-better-way-to-control-before-and-after-blocks-with-test-titles-in-playwright-test/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/a-better-way-to-control-before-and-after-blocks-with-test-titles-in-playwright-test/">&lt;blockquote&gt;
&lt;p&gt;&quot;没有持续的成长和进步，改进、成就和成功这样的词就毫无意义。&quot; - 本杰明·富兰克林&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;你是否曾经看着几个月前写的代码，开始想：&quot;哇，&#x27;过去的我&#x27;认为这很不错，但实际上只是还凑合&quot;？我喜欢这样的时刻，因为它们证明我正在学习和成长，找到新的方法来解决问题或甚至做出小的改进。&lt;&#x2F;p&gt;
&lt;p&gt;去年，我需要有条件地运行 afterEach 块，解决方案是这个：&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-to-conditionally-use-aftereach&quot;&gt;如何在 Playwright 测试中有条件地使用 afterEach&lt;&#x2F;a&gt; 现在，我为同样的问题找到了另一个解决方案（我个人认为这是一种更好的风格）。&lt;&#x2F;p&gt;
&lt;p&gt;上周，我在编写一组测试时意识到，我需要有条件地运行 beforeEach 块来创建一些测试数据。使用之前用于 afterEach 的相同逻辑似乎不太合适，而且会导致严重依赖测试的顺序。此外，在几个测试中复制相同的代码也不符合 DRY（不要重复自己）原则。&lt;&#x2F;p&gt;
&lt;p&gt;多亏了 &lt;code&gt;testInfo&lt;&#x2F;code&gt; 类，你可以在前后块中获取测试标题（注意：beforeAll 只能访问第一个测试，而 afterAll 只能访问最后一个测试）。我不打算告诉你 &lt;code&gt;testInfo&lt;&#x2F;code&gt; 有多有用，因为这里已经有一篇关于它的文章：&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;untitled-2&quot;&gt;在 Playwright 测试运行时是否可以获取当前测试名称？&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;有了 &lt;code&gt;testInfo&lt;&#x2F;code&gt; 这个武器，我们可以通过测试标题（或其中的关键词）和块本身的简单 if 语句来控制 beforeEach 和 afterEach 块。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;通过测试标题控制前后块&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;testInfo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;testInfo&lt;&#x2F;span&gt;&lt;span&gt;.title.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#runBeforeEach&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;beforeEach() 已执行&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;afterEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;testInfo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;testInfo&lt;&#x2F;span&gt;&lt;span&gt;.title.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#runAfterEach&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;afterEach() 已执行&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;第一个测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;第一个测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;第二个测试 #runBeforeEach&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;第二个测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;第三个测试 #runAfterEach&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;第三个测试&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;05&#x2F;code-2.jpg&quot; alt=&quot;图片 1&quot; &#x2F;&gt;
输出&lt;&#x2F;p&gt;
&lt;p&gt;显然，&lt;code&gt;#runBeforeEach&lt;&#x2F;code&gt; 和 &lt;code&gt;#runAfterEach&lt;&#x2F;code&gt; 可以替换为你想要的任何内容。只要确保代码仍然可读并且有意义 😉&lt;&#x2F;p&gt;
&lt;p&gt;请记住，你可以使用 &lt;code&gt;if&lt;&#x2F;code&gt; 语句不仅用于整个前&#x2F;后块，还可以用于其中的一部分。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;testInfo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;*
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;      需要在每个测试之前运行的一些代码
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;testInfo&lt;&#x2F;span&gt;&lt;span&gt;.title.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#runBeforeEach&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;beforeEach() 已执行&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;a-better-way-to-control-before-and-after-blocks-with-test-titles-in-playwright-test&#x2F;&quot;&gt;URL 来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-06-05&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何使用playwright检查输入框中的内容</title>
        <published>2023-05-22T00:00:00+00:00</published>
        <updated>2023-05-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-do-i-check-the-value-inside-an-input-field-with-playwright/"/>
        <id>https://playwright.itest.info/blog/how-do-i-check-the-value-inside-an-input-field-with-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-do-i-check-the-value-inside-an-input-field-with-playwright/">&lt;p&gt;在使用 Playwright 进行页面测试时，有时我需要验证输入框中是否已经预先填充了文本，以确保我的系统正常运行。&lt;&#x2F;p&gt;
&lt;p&gt;在这种情况下，断言并不那么简单。典型的方法是找到页面上的元素，并查看.text 或.innerText 属性，&lt;em&gt;但问题是输入框通常不属于 DOM 的一部分&lt;&#x2F;em&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;05&#x2F;image-5.png&quot; alt=&quot;图片1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;输入框文本不在 DOM 中&lt;&#x2F;p&gt;
&lt;p&gt;但幸运的是，Playwright 有办法处理这个问题！&lt;&#x2F;p&gt;
&lt;p&gt;在这个例子中，我将使用测试网站&lt;a href=&quot;https:&#x2F;&#x2F;ecommerce-playground.lambdatest.io&#x2F;&quot;&gt;https:&#x2F;&#x2F;ecommerce-playground.lambdatest.io&#x2F;&lt;&#x2F;a&gt;。这个网站是一个虚拟商店，我们可以针对它编写自动化测试。我的测试场景如下：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;使用搜索框搜索产品并点击搜索&lt;&#x2F;li&gt;
&lt;li&gt;验证我搜索的产品名称是否仍在搜索框中&lt;&#x2F;li&gt;
&lt;li&gt;点击进入产品详情页，验证产品名称是否仍在搜索框中&lt;&#x2F;li&gt;
&lt;li&gt;点击“加入购物车”，验证产品名称是否仍在搜索框中&lt;&#x2F;li&gt;
&lt;li&gt;点击结账，验证产品名称是否不再在搜索框中&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;最后一点是否应该如此功能目前我不质疑，但可以作为一个问题提出，这是系统当前的操作方式，因此有必要为其添加自动化检查。如果它发生变化，我们的自动化测试会通知我们。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-tohavevalue-duan-yan-fang-fa&quot;&gt;使用.toHaveValue()断言方法&lt;&#x2F;h2&gt;
&lt;p&gt;使用这个方法非常方便。这个方法可以在任何定位器上使用，被称为 LocatorAssertion。这些是一级公民，需要使用&lt;code&gt;await&lt;&#x2F;code&gt;与 expect 一起使用。下面的例子展示了如何使用这个功能。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;验证产品搜索输入框&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;ecommerce-playground.lambdatest.io&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Palm Treo Pro&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productSearch &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Search For Products&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;searchButton &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Search&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productSearch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;搜索后验证输入框&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;searchButton&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productSearch&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveValue&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;官方 Playwright 文档链接如下：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locatorassertions#locator-assertions-to-have-value&quot;&gt;toHaveValue()&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-jian-tie-ban-jin-xing-duan-yan&quot;&gt;使用剪贴板进行断言&lt;&#x2F;h2&gt;
&lt;p&gt;第二种方法是不使用 toHaveValue()，这在某些棘手的情况下会很有用。可以点击输入框，选择输入内容并将其复制到剪贴板。然后使用 JavaScript 获取剪贴板的内容，并进行断言。示例代码如下。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;验证产品搜索输入框&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;ecommerce-playground.lambdatest.io&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Palm Treo Pro&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productSearch &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Search For Products&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;searchButton &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Search&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productSearch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;搜索后验证输入框（替代方法）&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productSearch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;userAgentInfo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;navigator.userAgent);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;userAgentInfo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Mac OS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;keyboard&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;press&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Meta+A&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;keyboard&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;press&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Meta+C&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;keyboard&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;press&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Control+A&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;keyboard&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;press&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Control+C&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;clipboardText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;navigator.clipboard.readText()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;clipboardText&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在这个例子中，我们首先评估浏览器的 userAgent，以确定需要按下的键盘修饰符，如果是 Mac OS 我们使用 Meta&#x2F;Command 键，如果是 Windows 或 Linux 我们使用 Control 修饰键。我们首先点击 productSearch 定位器，使光标位于输入框中，然后按下选择和复制的快捷键。代码的下一步是获取 clipboardText。&lt;&#x2F;p&gt;
&lt;p&gt;⚠️ 你需要确保在你的 playwright.config.ts 中设置了以下内容：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span&gt;use: {
&lt;&#x2F;span&gt;&lt;span&gt;  permissions: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;clipboard-read&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然后代码将 clipboardText 设置为剪贴板的文本，并进行断言以确保剪贴板中的文本与原始的 productName 变量匹配。&lt;&#x2F;p&gt;
&lt;p&gt;理想情况下你不需要这样做，但这是可行的 :) 完整的规范如下所示，注意我使用 test.steps 添加了不同的场景，这使得调试更加方便。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;验证产品搜索输入框&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;ecommerce-playground.lambdatest.io&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Palm Treo Pro&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productSearch &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;textbox&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Search For Products&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;searchButton &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Search&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productSearch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;搜索后验证输入框&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;searchButton&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productSearch&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveValue&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;搜索后验证输入框（替代方法）&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productSearch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;userAgentInfo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;navigator.userAgent);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;userAgentInfo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Mac OS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;keyboard&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;press&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Meta+A&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;keyboard&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;press&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Meta+C&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;keyboard&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;press&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Control+A&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;keyboard&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;press&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Control+C&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;clipboardText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;navigator.clipboard.readText()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;clipboardText&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;导航到产品页面后验证输入框&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;nth&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productSearch&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveValue&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;加入购物车后验证输入框&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Add to Cart&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productSearch&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveValue&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;访问结账页面后验证输入框&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Checkout &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productSearch&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;not&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveValue&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;productName&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;完整的代码添加可以在&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;playwright-demo&#x2F;pull&#x2F;21&quot;&gt;这里&lt;&#x2F;a&gt;找到。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yin-yong&quot;&gt;引用&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-do-i-check-the-value-inside-an-input-field-with-playwright&#x2F;&quot;&gt;来源网址&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间：2023-05-22&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>The Definitive Guide to API Test Automation With Playwright: Part 5 - Calculating API Endpoint Coverage</title>
        <published>2023-05-15T00:00:00+00:00</published>
        <updated>2023-05-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-5-calculating-endpoint-coverage/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-5-calculating-endpoint-coverage/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-5-calculating-endpoint-coverage/">&lt;p&gt;Now that I have some decent coverage around the &lt;code&gt;auth&lt;&#x2F;code&gt; and &lt;code&gt;bookings&lt;&#x2F;code&gt; endpoints, I have a new challenge. How do I keep up with what areas need coverage added, and how do I know if a a new auth endpoint gets added, without manually checking the docs every day. Today&#x27;s article will show you the solution that I use. Before we get going too far, big shoutout to Joel Black and &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;search&#x2F;results&#x2F;all&#x2F;?fetchDeterministicClustersOnly=true&amp;amp;heroEntityKey=urn:li:fsd_profile:ACoAAC5M5OEBfzodxiEa2m7bKaKlmN3P0YXZ3qQ&amp;amp;keywords=sergei%20gapanovich&amp;amp;origin=RICH_QUERY_TYPEAHEAD_HISTORY&amp;amp;position=0&amp;amp;searchId=5249d9ac-3d77-476d-b6e4-7550c20f59fc&amp;amp;sid=20Q&quot;&gt;Sergei Gapanovich&lt;&#x2F;a&gt; for the inspiration and refinement of this approach.&lt;&#x2F;p&gt;
&lt;p&gt;If you missed the &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;Introduction&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-1-basics-of-api-testing-get&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-2-adding-more-in-depth-checks&#x2F;&quot;&gt;Part 2&lt;&#x2F;a&gt;,  &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-3-debugging-api-tests-with-vs-code&#x2F;&quot;&gt;Part 3&lt;&#x2F;a&gt;, or &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-4-handling-headers-and-authentication&#x2F;&quot;&gt;Part 4&lt;&#x2F;a&gt; I encourage you to check those out to get the context of where we are jumping in.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;high-level-approach-and-plan&quot;&gt;High Level Approach and Plan&lt;&#x2F;h2&gt;
&lt;p&gt;I want to be able to leverage the OpenAPI spec files that are provided and compare them against the API tests we have already created to figure out where our gaps are. This will require:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Tagging our tests with the endpoint(s) that are being covered&lt;&#x2F;li&gt;
&lt;li&gt;Downloading the OpenAPI spec file for the endpoints we want to check&lt;&#x2F;li&gt;
&lt;li&gt;Parsing the OpenAPI spec file and matching them with our tagged tests&lt;&#x2F;li&gt;
&lt;li&gt;Calculating coverage and console.log() the results&lt;&#x2F;li&gt;
&lt;li&gt;This should all happen prior to any tests running&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With this approach I should have a good idea of what areas need coverage, and if there are any new endpoints added to the OpenAPI spec file we are monitoring anytime the tests run.&lt;&#x2F;p&gt;
&lt;p&gt;⚠️&lt;&#x2F;p&gt;
&lt;p&gt;When I mention having test coverage in this article I am simply referring to having at least 1 high level happy path type test against an endpoint we are measuring coverage for. This is a calculation doesn&#x27;t mean much in terms of the depth of our coverage but the breadth of our coverage, across all measured endpoints.&lt;&#x2F;p&gt;
&lt;p&gt;The idea behind a coverage tag is to create a unique identifier that represents the API endpoint. An example of a coverage tag I use is &lt;code&gt;&#x2F;&#x2F;COVERAGE_TAG: POST &#x2F;auth&#x2F;login&lt;&#x2F;code&gt; This includes &lt;code&gt;&#x2F;&#x2F;Coverage_TAG:&lt;&#x2F;code&gt; which allows me to use &lt;a href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;grep&#x2F;manual&#x2F;grep.html#:~:text=grep%20searches%20the%20named%20input,grep%20searches%20the%20working%20directory%20.&quot;&gt;grep&lt;&#x2F;a&gt; to search for the specific line, the &lt;code&gt;POST&lt;&#x2F;code&gt; is the verb&#x2F;action and the &lt;code&gt;&#x2F;auth&#x2F;login&lt;&#x2F;code&gt; is the endpoint I am testing. Depending on your specific system under test you may need to adjust the endpoint url based on what you have accessible in your OpenAPI spec file.&lt;&#x2F;p&gt;
&lt;p&gt;The example provided below can be found in the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;blob&#x2F;main&#x2F;tests&#x2F;booking&#x2F;booking.get.spec.ts&quot;&gt;booking.get.spec.ts&lt;&#x2F;a&gt; file. One thing to note, is in the spec file I cover 3 different endpoints. With the approach on calculating coverage, I filter out any duplicate coverage tags, so if there were other examples with the same endpoints covered they wouldn&#x27;t get counted twice.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;booking&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;booking&#x2F;{id}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;booking&#x2F;summary
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isValidDate &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;helpers&#x2F;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createInvalidHeaders&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F; GET requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;invalidHeader&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I typically will put the coverage tags at the top of the file, though technically you could put it anywhere within the file.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;downloading-and-parsing-an-openapi-spec-file&quot;&gt;Downloading and Parsing an OpenAPI Spec File&lt;&#x2F;h2&gt;
&lt;p&gt;The next step we need to do is to download the OpenAPI spec file. This file will be the source of truth for what endpoints exist. I won&#x27;t go super deep into the &lt;a href=&quot;https:&#x2F;&#x2F;spec.openapis.org&#x2F;oas&#x2F;latest.html&quot;&gt;OpenAPI Specification&lt;&#x2F;a&gt; but a quick overview from the website:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The OpenAPI Specification (OAS) defines a standard, programming language-agnostic interface description for HTTP APIs, which allows both humans and computers to discover and understand the capabilities of a service without requiring access to source code, additional documentation, or inspection of network traffic. When properly defined via OpenAPI, a consumer can understand and interact with the remote service with a minimal amount of implementation logic. Similar to what interface descriptions have done for lower-level programming, the OpenAPI Specification removes guesswork in calling a service.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;05&#x2F;image-4.png&quot; alt=&quot;Image 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For our purposes we will utilize this file to identify the verbs and endpoints that are published in the OpenAPI spec file. It&#x27;s possible your system under test doesn&#x27;t have OpenAPI spec file, but there may still be hope. if there is a swagger doc, there is more than likely some sort of json that can be downloaded that can be parsed to get similar information you&#x27;ll just have to work out the logic for yourself.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;swagger-ui-pages&quot;&gt;Swagger UI Pages&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Auth Swagger UI - &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;auth&#x2F;swagger-ui&#x2F;index.html#&#x2F;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;auth&#x2F;swagger-ui&#x2F;index.html#&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Room Swagger UI - &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;room&#x2F;swagger-ui&#x2F;index.html#&#x2F;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;room&#x2F;swagger-ui&#x2F;index.html#&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Branding Swagger UI - &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;branding&#x2F;swagger-ui&#x2F;index.html#&#x2F;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;branding&#x2F;swagger-ui&#x2F;index.html#&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Report Swagger UI - &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;report&#x2F;swagger-ui&#x2F;index.html#&#x2F;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;report&#x2F;swagger-ui&#x2F;index.html#&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Message Swagger UI - &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;message&#x2F;swagger-ui&#x2F;index.html#&#x2F;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;message&#x2F;swagger-ui&#x2F;index.html#&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For the system we are testing, I&#x27;ve linked all the Swagger API endpoint pages, you&#x27;ll notice when visiting the swagger ui page, there is link to the &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;auth&#x2F;v3&#x2F;api-docs&#x2F;auth-api&quot;&gt;OpenAPI Spec&lt;&#x2F;a&gt;. This is the spec file&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;05&#x2F;image-2.png&quot; alt=&quot;Image 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now we have the urls to the OpenAPI Spec files we can start building out out logic. I&#x27;ll start by creating a file in the &lt;code&gt;lib&#x2F;helpers&lt;&#x2F;code&gt; directory named &lt;code&gt;coverage.ts&lt;&#x2F;code&gt;. I&#x27;ll use this to build out the logic needed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;getting-the-openapi-spec&quot;&gt;Getting the OpenAPI Spec&lt;&#x2F;h3&gt;
&lt;p&gt;The first step I tackled was downloading the OpenAPI spec file. I wanted to do this for multiple files knowing there are multiple endpoints that I want to parse and measure. So I created a function that can be called multiple times. The async function &lt;code&gt;fetchOpenAPI&lt;&#x2F;code&gt; takes a parameter of the &lt;code&gt;resource&lt;&#x2F;code&gt; as a string to indicate which OpenAPI spec we want to download, and uses the built in &lt;code&gt;request&lt;&#x2F;code&gt; method from the playwright library.  Once the api call is made I take the response which is json and write the OpenAPI Spec file to the root directory using the &lt;code&gt;writeFile&lt;&#x2F;code&gt; function (this is more for reference and testing as we don&#x27;t use this file for the moment). The &lt;code&gt;fetchOpenAPI&lt;&#x2F;code&gt; then returns the JSON body which we can use to parse  and get the information we need in the next section.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dotenv&#x2F;config&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseURL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.URL;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resource
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; JSON object of the OpenAPI spec
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;await fetchOpenApi(&amp;quot;messages&amp;quot;); returns JSON object of the OpenAPI spec
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * There is also a ${resource}_spec3.json file created in the root of the project
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * These files are used to get the endpoints and calculate coverage
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fetchOpenApi&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseURL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&#x2F;v3&#x2F;api-docs&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}-api&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    { timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5000 &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;writeFile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}_spec3.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; eslint-disable-next-line
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;writeFile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;writeFileSync&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(&amp;quot;File written successfully&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(&amp;quot;The written file has&amp;quot; + &amp;quot; the following contents:&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(&amp;quot;&amp;quot; + fs.readFileSync(location));
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;catch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;getting-the-individual-endpoints&quot;&gt;Getting the Individual Endpoints&lt;&#x2F;h3&gt;
&lt;p&gt;From the previous step we have the body of the JSON of the OpenAPI spec. An example of that file can be found at the &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;auth&#x2F;v3&#x2F;api-docs&#x2F;auth-api&quot;&gt;Auth API spec&lt;&#x2F;a&gt;. The next thing we need to do is parse this file and get the endpoints out of the file. We will write a function named &lt;code&gt;getEndpoints&lt;&#x2F;code&gt; that takes a json object as a parameter. We first go and grab the methods (verbs GET, PUT, POST, DELETE, etc) and then get the urlPath for each endpoint in the OpenAPI spec file. We then create an array with the verb space path. The output of the auth endpoints are&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;[ &#x27;POST &#x2F;auth&#x2F;validate&#x27;, &#x27;POST &#x2F;auth&#x2F;logout&#x27;, &#x27;POST &#x2F;auth&#x2F;login&#x27; ]&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Below is the code that generates the array.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; JSON object of the OpenAPI spec
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; Array of endpoints with format &amp;quot;VERB PATH&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;getEndpoints(authJson); returns [&amp;quot;POST &#x2F;auth&#x2F;login&amp;quot;, &amp;quot;POST &#x2F;auth&#x2F;logout&amp;quot;, ...]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * This function is used to get the endpoints from the OpenAPI spec
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getEndpoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;spec3 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;methods &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;spec3&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;urlPath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;spec3&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;servers&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;slice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;[] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;property &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;methods&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;verbs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Object&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;keys&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;methods&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;property&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;verb &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;of &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;verbs&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalVerb &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;verb&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;toUpperCase&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalPath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;urlPath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;property&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalArray&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalVerb &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalPath&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalArray&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;💡&lt;&#x2F;p&gt;
&lt;p&gt;It is worth calling out at this point, that if you are attempting to implement this with a different system under test, this code will need to be adjusted. see the example below.&lt;&#x2F;p&gt;
&lt;p&gt;For example if you were writing tests against &lt;a href=&quot;https:&#x2F;&#x2F;api.practicesoftwaretesting.com&#x2F;docs&#x2F;api-docs.json&quot;&gt;https:&#x2F;&#x2F;api.practicesoftwaretesting.com&#x2F;docs&#x2F;api-docs.json&lt;&#x2F;a&gt;, which returns has the entire API in 1 OpenApi spec file, some of the code changes. For starters a full url for the &lt;code&gt;server&lt;&#x2F;code&gt; object, you can edit the file as follows, this will remove the urlPath code. You would also only have to make 1 call to calculate coverage &lt;code&gt;await getEndpointCoverage(&#x27;api&#x27;)&lt;&#x2F;code&gt; rather than making a call for each OpenApi spec.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;08&#x2F;image-30.png&quot; alt=&quot;Image 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;get-endpoint-coverage-from-tests-and-calculate-coverage&quot;&gt;Get Endpoint Coverage From Tests and Calculate Coverage&lt;&#x2F;h3&gt;
&lt;p&gt;Now that we have what endpoints exist within the OpenAPI Spec file, we need to do a comparison with our own codebase to see what endpoints have coverage. To do this  we are going to utilize the coverage tags which were covered earlier in this article example: &lt;code&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;booking&#x2F;&lt;&#x2F;code&gt;.  The function we will use is &lt;code&gt;getCoverage&lt;&#x2F;code&gt; that requires an array, we will use the previous array that we just built). The function iterates through the coverage array, and for each value, runs a grep command searching the files in the &lt;code&gt;&#x2F;tests&#x2F;&lt;&#x2F;code&gt; directory to attempt to match to an existing coverage tag. It does this using the execSync function and will require your base system to have grep installed and accessible from the command line.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;execSync&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;grep -rl tests -e &amp;#39;COVERAGE_TAG: ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;]}$&amp;#39; | cat&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  {
&lt;&#x2F;span&gt;&lt;span&gt;    encoding: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;utf-8&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The command &quot;grep -rl&quot; searches for a specific text pattern recursively in all files and directories under a specified directory and returns the names of the files that contain the pattern. The &quot;-r&quot; option tells grep to search recursively, and the &quot;-l&quot; option tells it to only print the names of the files that contain the pattern, rather than the actual lines that match.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;If there is a match we add a value to coveredEndpoints (which starts from 0) and if there isn&#x27;t a match, we add that value to a nonCoveredEndpoints array, and console.log out a message that this endpoint is missing coverage.&lt;&#x2F;p&gt;
&lt;p&gt;After iterating through all the values, we calculate the coverage with the amount of coveredEndpoints and totalEndpoints and print this out via console messages. The code for this is in the &lt;code&gt;calculateCoverage&lt;&#x2F;code&gt; function below.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;execSync &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;child_process&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;Greps local files getting a list of files with specified coverage tag and calculates coverage
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getCoverage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;totalEndPoints &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span&gt;.length;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coveredEndPoints &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;nonCoveredEndpoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;[] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;Iterates through the coverageArray to grep each file in the test directory looking for matches
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;execSync&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;grep -rl tests -e &amp;#39;COVERAGE_TAG: ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;]}$&amp;#39; | cat&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      {
&lt;&#x2F;span&gt;&lt;span&gt;        encoding: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;utf-8&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(value);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(coverageArray[value]);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(output);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coveredEndPoints &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Endpoint with no coverage: ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;]}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;nonCoveredEndpoints&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Total Endpoints: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;totalEndPoints&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Covered Endpoints: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coveredEndPoints&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; writeFile(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;   &amp;quot;.&#x2F;lib&#x2F;non_covered_endpoints.txt&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;   JSON.stringify(nonCoveredEndpoints, null, &amp;quot;\t&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; );
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;calculateCoverage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coveredEndPoints&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;totalEndPoints&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;calculateCoverage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;coveredEndpoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;totalEndpoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;percentCovered &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coveredEndpoints &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;totalEndpoints&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toFixed&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Coverage: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;percentCovered &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;COVERED_ENDPOINTS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coveredEndpoints&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;TOTAL_ENDPOINTS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;totalEndpoints&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;PERCENT_COVERED &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;percentCovered&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;creating-a-function-that-calls-all-the-other-functions&quot;&gt;Creating a Function That Calls All the Other Functions&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;ll admit my naming hasn&#x27;t been the best, and could be better, along with some of the functions that I&#x27;ve included so far. I&#x27;m sure they could be refactored to be much nicer. This next section I&#x27;ve created a function called &lt;code&gt;getEndpointCoverage&lt;&#x2F;code&gt; and it is the only function I am exporting in the coverage file (our entry point). This function only needs the endpoint name, as I am setting the baseURL from the dotenv config file. This function calls &lt;code&gt;fetchOpenApi&lt;&#x2F;code&gt;, &lt;code&gt;getEndpoints&lt;&#x2F;code&gt;, and &lt;code&gt;getCoverage&lt;&#x2F;code&gt; functions.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dotenv&#x2F;config&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseURL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.URL;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; url path for pulling the OpenAPI spec
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;getEndpointCoverage(&amp;quot;auth&amp;quot;); console logs coverage for auth endpoints
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getEndpointCoverage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;endpoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;=== Coverage for ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;} Endpoints ===&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fetchOpenApi&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpoint&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getEndpoints&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getCoverage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After this function is run you will get nice output in your terminal with details for the endpoints you have coverage for.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;05&#x2F;image-3.png&quot; alt=&quot;Image 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;terminal output showing calculated coverage&lt;&#x2F;p&gt;
&lt;p&gt;The complete &lt;code&gt;coverage.ts&lt;&#x2F;code&gt; file can be found in it&#x27;s entirety below.  We still need to create a way to call this function before all our tests run, which I&#x27;ll cover after the code block.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; lib&#x2F;helpers&#x2F;coverage.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;execSync &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;child_process&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dotenv&#x2F;config&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseURL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.URL;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; url path for pulling the OpenAPI spec
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;getEndpointCoverage(&amp;quot;auth&amp;quot;); console logs coverage for auth endpoints
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getEndpointCoverage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;endpoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;=== Coverage for ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpoint&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;} Endpoints ===&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fetchOpenApi&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;endpoint&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getEndpoints&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getCoverage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resource
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; JSON object of the OpenAPI spec
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;await fetchOpenApi(&amp;quot;messages&amp;quot;); returns JSON object of the OpenAPI spec
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * There is also a ${resource}_spec3.json file created in the root of the project
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * These files are used to get the endpoints and calculate coverage
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fetchOpenApi&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestContext &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestContext&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseURL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&#x2F;v3&#x2F;api-docs&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}-api&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    { timeout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;5000 &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;writeFile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;resource&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}_spec3.json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; JSON object of the OpenAPI spec
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; Array of endpoints with format &amp;quot;VERB PATH&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;getEndpoints(authJson); returns [&amp;quot;POST &#x2F;auth&#x2F;login&amp;quot;, &amp;quot;POST &#x2F;auth&#x2F;logout&amp;quot;, ...]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * This function is used to get the endpoints from the OpenAPI spec
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getEndpoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;spec3 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;methods &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;spec3&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;urlPath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;spec3&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;servers&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;slice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;[] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;property &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;methods&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;verbs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Object&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;keys&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;methods&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;property&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;verb &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;of &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;verbs&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalVerb &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;verb&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;toUpperCase&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalPath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;urlPath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;property&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalArray&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalVerb &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalPath&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;finalArray&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;Greps local files getting a list of files with specified coverage tag and calculates coverage
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getCoverage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;totalEndPoints &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span&gt;.length;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coveredEndPoints &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;nonCoveredEndpoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;[] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;Iterates through the coverageArray to grep each file in the test directory looking for matches
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;execSync&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;grep -rl tests -e &amp;#39;COVERAGE_TAG: ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;]}$&amp;#39; | cat&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      {
&lt;&#x2F;span&gt;&lt;span&gt;        encoding: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;utf-8&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(value);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(coverageArray[value]);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(output);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coveredEndPoints &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Endpoint with no coverage: ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;]}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;nonCoveredEndpoints&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverageArray&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Total Endpoints: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;totalEndPoints&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Covered Endpoints: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coveredEndPoints&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; writeFile(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;   &amp;quot;.&#x2F;lib&#x2F;non_covered_endpoints.txt&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;   JSON.stringify(nonCoveredEndpoints, null, &amp;quot;\t&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; );
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;calculateCoverage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coveredEndPoints&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;totalEndPoints&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;calculateCoverage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;coveredEndpoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;totalEndpoints&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;percentCovered &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coveredEndpoints &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;totalEndpoints&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toFixed&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Coverage: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;percentCovered &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;COVERED_ENDPOINTS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coveredEndpoints&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;TOTAL_ENDPOINTS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;totalEndpoints&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;PERCENT_COVERED &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;percentCovered&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; eslint-disable-next-line
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;writeFile&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;try &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;writeFileSync&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;location&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(&amp;quot;File written successfully&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(&amp;quot;The written file has&amp;quot; + &amp;quot; the following contents:&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(&amp;quot;&amp;quot; + fs.readFileSync(location));
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;catch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;ACoAABg4jOgBK9s1HMX_wTI_XcJgPxTLP42G1xs?lipi=urn%3Ali%3Apage%3Ad_flagship3_feed%3BM%2FuA1chQR2yP5nPgmpVEUQ%3D%3D&quot;&gt;Dmitry Pakhilov&lt;&#x2F;a&gt; for suggestions on how to make the code more readable!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;calculate-coverage-using-defineconfig&quot;&gt;Calculate Coverage Using defineConfig&lt;&#x2F;h3&gt;
&lt;p&gt;We will be using the &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-configuration&quot;&gt;defineConfig&lt;&#x2F;a&gt; function that allows us to utilize the test runner and setup and create order to our tests. Below is an example of a file I created named &lt;code&gt;coverage.setup.ts&lt;&#x2F;code&gt; Notice, this file isn&#x27;t using the &lt;code&gt;spec&lt;&#x2F;code&gt; in the name but rather setup, this will allow us to separate our setup versus spec files.&lt;&#x2F;p&gt;
&lt;p&gt;notice we are also renaming &lt;code&gt;test as coverage&lt;&#x2F;code&gt; This naming convention is really just so we can better describe our actions. We will uset the same syntax as when writing tests with the async block and will call &lt;code&gt;getEndpointCoverage()&lt;&#x2F;code&gt; function for each of our API endpoints.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; tests&#x2F;coverage.setup.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getEndpointCoverage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;lib&#x2F;helpers&#x2F;coverage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;coverage &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;coverage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;calculate coverage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getEndpointCoverage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getEndpointCoverage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getEndpointCoverage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;room&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getEndpointCoverage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;branding&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getEndpointCoverage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;report&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getEndpointCoverage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Below is the &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; file. You can see in the projects section there are 2 objects in the array, the first is named &lt;code&gt;setup&lt;&#x2F;code&gt; and the 2nd is named &lt;code&gt;api-checks&lt;&#x2F;code&gt;. For the &lt;code&gt;setup&lt;&#x2F;code&gt; project I create a testMatch that will only run the &lt;code&gt;coverage.setup.ts&lt;&#x2F;code&gt; file. The api-checks is using the &lt;code&gt;dependencies&lt;&#x2F;code&gt; which requires &lt;code&gt;setup&lt;&#x2F;code&gt; to complete before running.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; playwright.config.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dotenv&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  projects: [
&lt;&#x2F;span&gt;&lt;span&gt;    { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, testMatch: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;coverage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api-checks&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      dependencies: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  use: {
&lt;&#x2F;span&gt;&lt;span&gt;    extraHTTPHeaders: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-solutions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    baseURL: process.env.URL,
&lt;&#x2F;span&gt;&lt;span&gt;    ignoreHTTPSErrors: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    trace: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  retries: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  reporter: [[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;list&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;so-why-is-this-important-again&quot;&gt;So Why Is This Important Again?&lt;&#x2F;h3&gt;
&lt;p&gt;As you can see now when you run your tests, now you should have some really nice output for your API endpoint coverage. Now at this point (or probably way sooner) you may be asking why... Why should I do this? Well for me there are a few reasons. When first starting off a project I want to be able to track progress. Though my checks I am writing may still be mainly covering high level breadth of the endpoints (not going super deep on assertions), I want to be able to track my progress.&lt;&#x2F;p&gt;
&lt;p&gt;Having this tooling built into my framework also allows me to not just track what I&#x27;ve done today, but when I get coverage to 100%, and a new endpoint gets added, I&#x27;ll know about it right away if I am checking the logs, if not I can easily take any new endpoints and alert a slack channel or have some other form of notification to alert me when new endpoints have been added. This has been a big help for me in my day job keeping up with new endpoints released. We have a goal to add coverage to any new endpoints added within a week.&lt;&#x2F;p&gt;
&lt;p&gt;The full codebase can be found below where you can download and run for yourself, or visit the GitHub pages link to view the results from the latest test run.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&quot;&gt;GitHub&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you made it this far you are amazing. This is me celebrating together with you!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;05&#x2F;dance.gif&quot; alt=&quot;Image 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks for reading! If you found this helpful, reach out and let me know on &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; or consider &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;buying me a cup of coffee&lt;&#x2F;a&gt;. If you want more content delivered to you in your inbox subscribe below.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-5-calculating-endpoint-coverage&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-05-15T12:30:29.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>The Definitive Guide to API Test Automation With Playwright: Part 4 - Handling Headers and Authentication</title>
        <published>2023-05-08T00:00:00+00:00</published>
        <updated>2023-05-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-4-handling-headers-and-authentication/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-4-handling-headers-and-authentication/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-4-handling-headers-and-authentication/">&lt;p&gt;One of the first areas your likely to run into issues while writing automation for apis is around headers and authentication. I hope to share a few ways I&#x27;ve handled authentication and header management from my experiences, though there are many solutions to this problem. If you missed the &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;Introduction&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-1-basics-of-api-testing-get&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-2-adding-more-in-depth-checks&#x2F;&quot;&gt;Part 2&lt;&#x2F;a&gt;, or &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-3-debugging-api-tests-with-vs-code&#x2F;&quot;&gt;Part 3&lt;&#x2F;a&gt; I encourage you to check those out to get the context of where we are jumping in.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;creating-an-auth-ts-datafactory&quot;&gt;Creating an auth.ts datafactory&lt;&#x2F;h2&gt;
&lt;p&gt;My approach for the application we are testing &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;&lt;&#x2F;a&gt; will likely differ from other sites, as they are all different. When dealing with the &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;auth&#x2F;swagger-ui&#x2F;index.html&quot;&gt;auth endpoints&lt;&#x2F;a&gt;, there are only 3 endpoints, an auth where you pass username and password, a validate where you pass in a token to ensure you are still logged in, and a logout endpoint to invalidate your session. With this site only having 1 login UN &lt;code&gt;admin&lt;&#x2F;code&gt; PW &lt;code&gt;password&lt;&#x2F;code&gt; we will take a simple approach.&lt;&#x2F;p&gt;
&lt;p&gt;I will start by creating an &lt;code&gt;auth.ts&lt;&#x2F;code&gt; datafactory file. I previously had created the &lt;code&gt;auth&lt;&#x2F;code&gt; async function that would return cookies to that would be added to the headers on each test. I&#x27;ve updates the functions name to &lt;code&gt;createCookies&lt;&#x2F;code&gt; and added jsdoc so that when using the function you can hover with some nice helper text of what the function does along with an example of how to use it. I&#x27;ve found that super helpful when working within the context of a team.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;05&#x2F;image-1.png&quot; alt=&quot;Image 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As I also added a new async function named &lt;code&gt;createToken&lt;&#x2F;code&gt;. This function is very similar to create cookies in that it makes the same POST auth&#x2F;login call but rather than returning all the cookies I only return the token. I needed this for some of the auth tests I&#x27;ve added during this round of updates.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.URL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * Returns valid cookies for the given username and password.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * If a username and password aren&amp;#39;t provided &amp;quot;admin&amp;quot; and &amp;quot;password&amp;quot; will be used
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * import { createCookies } from &amp;quot;..&#x2F;datafactory&#x2F;auth&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * const cookies = createCookies(&amp;quot;Happy&amp;quot;, &amp;quot;Mcpassword&amp;quot;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * const response = await request.put(`booking&#x2F;${bookingId}`, {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;      headers: { cookie: cookies },
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;      data: body,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    });
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createCookies&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;admin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;contextRequest &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;contextRequest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    data: {
&lt;&#x2F;span&gt;&lt;span&gt;      username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;set-cookie&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * Returns valid token for the given username and password.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * If a username and password aren&amp;#39;t provided &amp;quot;admin&amp;quot; and &amp;quot;password&amp;quot; will be used
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * import { createToken } from &amp;quot;..&#x2F;datafactory&#x2F;auth&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * const token = createToken(&amp;quot;Happy&amp;quot;, &amp;quot;Mcpassword&amp;quot;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   * const response = await request.post(&amp;quot;auth&#x2F;validate&amp;quot;, {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;      data: { token: token },
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    });
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;   *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;admin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;contextRequest &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;newContext&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;contextRequest&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    data: {
&lt;&#x2F;span&gt;&lt;span&gt;      username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tokenString &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;set-cookie&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;split&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tokenString&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;split&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With the easy way to create cookies or a valid token we can start on abstracting away how we create headers. For this I created a &lt;code&gt;createHeaders.ts&lt;&#x2F;code&gt; helper. I chose to keep this as a helper as I am not interacting with the API directly within this file. Within this helper file I have 2 async functions &lt;code&gt;createHeaders()&lt;&#x2F;code&gt; and &lt;code&gt;createInvalidHeaders()&lt;&#x2F;code&gt;. These are basic examples, but this pattern can be used to create different types of headers based on what your system needs. For example if you needed to include an account id or api key in your headers you could build logic out in this file keeping your code &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Don%27t_repeat_yourself&quot;&gt;DRY&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createCookies &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;datafactory&#x2F;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_NAME&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;process.env.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;ADMIN_PASSWORD&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@param &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; a valid token to be used in the request if one is not provided cookies will be created from default username and password
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; a header object with the token set as a cookie
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * import { createHeaders } from &amp;quot;..&#x2F;lib&#x2F;helpers&#x2F;createHeaders&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * const headers = await createHeaders(token);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *     const response = await request.delete(`booking&#x2F;${bookingId}`, {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;      headers: headers,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    });
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestHeaders&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestHeaders &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;token=${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Authenticate and get cookies
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createCookies&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestHeaders &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestHeaders&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@returns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; a header object with an invalid cookie used to test negative scenarios
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;@example
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * import { createInvalidHeaders } from &amp;quot;..&#x2F;lib&#x2F;helpers&#x2F;createHeaders&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; * const invalidHeader = await createInvalidHeaders();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *     const response = await request.delete(`booking&#x2F;${bookingId}`, {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;      headers: invalidHeader,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;    });
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createInvalidHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestHeaders &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;cookie=invalid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestHeaders&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;ve refactored all the areas in the spec files to include these methods in order to create the headers. To see the refactored files take a look at &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;pull&#x2F;5&#x2F;files&quot;&gt;the pull request&lt;&#x2F;a&gt;.  Below is the GET examples that were updated. One thing to note is in may different tests creating the headers in the beforeAll() is typically ok, but it may make sense to create them in a beforeEach(). This will be determined by your tests, so don&#x27;t blindly copy me, think through your needs.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;booking&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;booking&#x2F;{id}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;COVERAGE_TAG: GET &#x2F;booking&#x2F;summary
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isValidDate &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;helpers&#x2F;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createInvalidHeaders&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;helpers&#x2F;createHeaders&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F; GET requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;invalidHeader&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;invalidHeader &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createInvalidHeaders&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET booking summary with specific room id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;summary?roomid=1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThanOrEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET booking summary with specific room id that doesn&amp;#39;t exist&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  }) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;summary?roomid=999999&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET booking summary with specific room id that is empty&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  }) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;summary?roomid=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;500&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;timestamp&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.status).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;500&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Internal Server Error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;booking&#x2F;summary&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET all bookings with details&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThanOrEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;James&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Dean&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET all bookings with details with no authentication&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  }) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;invalidHeader&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;403&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET booking by id with details&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;James&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Dean&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET booking by id that doesn&amp;#39;t exist&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;999999&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;404&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET booking by id without authentication&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;403&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;using-storagestate&quot;&gt;Using storageState&lt;&#x2F;h2&gt;
&lt;p&gt;One thing I didn&#x27;t implement is the built in &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;auth#basic-shared-account-in-all-tests&quot;&gt;storageState&lt;&#x2F;a&gt;. I did create an &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;blob&#x2F;ced7d621f05a02b5cff4c9e1a31e1da7a405866f&#x2F;tests&#x2F;auth.setup.ts&quot;&gt;auth.setup.ts&lt;&#x2F;a&gt; file that utilized this but found that it didn&#x27;t suite my needs. I left the file in there, and just commented out using this as the setup project. The reason it didn&#x27;t work for me was because I include tests with invalid headers, and the storageState when setting up via &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; makes all requests authenticated. This could be used with a bit more complexity importing different storageStates for each file&#x2F;test, but I decided since I am creating headers just to handle that logic on my own.&lt;&#x2F;p&gt;
&lt;p&gt;Playwright also has the option to pass default http headers with every request using the extraHTTPHeaders within the &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; file. An example below shows passing in &lt;code&gt;&quot;playwright-solutions&quot;: &quot;true&quot;&lt;&#x2F;code&gt; as a header on every request. This can be really useful if you have a test header or default header you can pass in to bypass security rules in services as an example a CloudFlare WAF.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dotenv&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  projects: [
&lt;&#x2F;span&gt;&lt;span&gt;    { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, testMatch: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\o&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;verage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;\.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api-checks&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      dependencies: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  use: {
&lt;&#x2F;span&gt;&lt;span&gt;    extraHTTPHeaders: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-solutions&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    baseURL: process.env.URL,
&lt;&#x2F;span&gt;&lt;span&gt;    ignoreHTTPSErrors: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    trace: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  retries: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  reporter: [[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;list&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;creating-auth-api-checks&quot;&gt;Creating &#x2F;auth API checks&lt;&#x2F;h2&gt;
&lt;p&gt;With the creation of these new helpers I&#x27;ve gone ahead and added api test coverage for the &lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;auth&#x2F;swagger-ui&#x2F;index.html#&#x2F;&quot;&gt;auth endpoints&lt;&#x2F;a&gt;. The checks can be found in the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&#x2F;tree&#x2F;main&#x2F;tests&#x2F;auth&quot;&gt;main repo under the tests&#x2F;auth&#x2F;&lt;&#x2F;a&gt; directory. With this area of the system covered I now have high confidence as things change, logging in, logging out, and validating a token are working as expected. One thing to note with these examples, there was only 1 valid login I was testing with, typically when it comes to authorization I would also want to try test any different types of logins that are provided. For a more complex system I would recommend more checks.&lt;&#x2F;p&gt;
&lt;p&gt;Please do check out the repo below with all the code examples&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&quot;&gt;GitHub - playwrightsolutions&#x2F;playwright-api-test-demo&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks for reading! If you found this helpful, reach out and let me know on &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; or consider &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;buying me a cup of coffee&lt;&#x2F;a&gt;. If you want more content delivered to you in your inbox subscribe below.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-4-handling-headers-and-authentication&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-05-08T04:22:16.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>The Definitive Guide to API Test Automation With Playwright: Part 3 - Debugging API Tests</title>
        <published>2023-04-17T00:00:00+00:00</published>
        <updated>2023-04-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-3-debugging-api-tests-with-vs-code/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-3-debugging-api-tests-with-vs-code/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-3-debugging-api-tests-with-vs-code/">&lt;p&gt;A big part of writing automated checks is testing and debugging the code you write, ideally ensuring there aren&#x27;t any bugs in test code. For part 3 I will be covering how I run my tests, debug my tests, and how I get feedback before the code is merged into the code branch. If you missed the &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-do-api-testing-with-playwright-the-definitive&#x2F;&quot;&gt;Introduction&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-1-basics-of-api-testing-get&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt;, or &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-2-adding-more-in-depth-checks&#x2F;&quot;&gt;Part 2&lt;&#x2F;a&gt;, I encourage you to check those out to get the context of where we are jumping in.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-ll-cover&quot;&gt;What I&#x27;ll cover&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;How I use  VS Code to debug my tests&lt;&#x2F;li&gt;
&lt;li&gt;Using UI mode to debug&lt;&#x2F;li&gt;
&lt;li&gt;Using Playwright Test Trace Files via Show-Report&lt;&#x2F;li&gt;
&lt;li&gt;Using console.log()&#x2F;console.table()&lt;&#x2F;li&gt;
&lt;li&gt;How I get feedback via GitHub Action before merging code&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;vs-code-to-debug-tests&quot;&gt;VS Code to Debug Tests&lt;&#x2F;h2&gt;
&lt;p&gt;When I am writing or debugging Playwright tests I typically use the &lt;a href=&quot;https:&#x2F;&#x2F;marketplace.visualstudio.com&#x2F;items?itemName=ms-playwright.playwright&quot;&gt;Playwright Test for VS Code extension&lt;&#x2F;a&gt;. Once installed, you can select the Testing (beaker icon) and view, debug, or run the tests that are written.  What&#x27;s nice about this, is you can run individual tests with a button click.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;04&#x2F;image-3.png&quot; alt=&quot;Image 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Playwright Test VS Code Extension&lt;&#x2F;p&gt;
&lt;p&gt;With the extension installed you also will see green play buttons on each of your test and describe blocks where you can run the checks from the &lt;code&gt;spec.ts&lt;&#x2F;code&gt; file directly.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;04&#x2F;image-4.png&quot; alt=&quot;Image 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Run test buttons in-line with VS Code&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll demonstrate using running a test in debug mode below. Before I run the test, I will add a &lt;code&gt;breakpoint&lt;&#x2F;code&gt; where I want the test to pause and open the debugger menu. From here I am able to see information about the tests that I typically wouldn&#x27;t see. This is helpful in trying to determine what information variables are assigned to. In my example I will add a debug point after I assign the body variable to response.json().&lt;&#x2F;p&gt;
&lt;p&gt;From the above example, you can see I was able to validate the shape of the data that is returned &lt;code&gt;{ bookings: [ { bookingDates: { checkin: &quot;Date&quot;, checkout: &quot;Date&quot;} } ] }&lt;&#x2F;code&gt;, along with any specific data.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-playwright-ui-mode&quot;&gt;Using Playwright UI mode&lt;&#x2F;h2&gt;
&lt;p&gt;To use UI mode you will need to have Playwright version 1.32 in your project. To upgrade your version it&#x27;s as easy as visiting your &lt;code&gt;package.json&lt;&#x2F;code&gt; folder, finding the devDependencies &amp;gt; @playwright&#x2F;test, and updating the value to 1.32 or higher.&lt;&#x2F;p&gt;
&lt;p&gt;If you are not sure what the latest version is you can find it on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright&#x2F;releases&quot;&gt;GitHub Releases page&lt;&#x2F;a&gt;. You can also follow this guide to set up slack notification when a new version of Playwright is released.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-can-i-get-slack-notifications&#x2F;&quot;&gt;How can I get Slack notifications&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;On to Playwright UI mode, you may be thinking, why do I need to use UI mode, when we are running API tests? Well you don&#x27;t have to but it&#x27;s a great way to run and debug tests and view the network requests as they are occurring versus adding console.log or using the VSCode debugger.&lt;&#x2F;p&gt;
&lt;p&gt;To run Playwright in UI mode add &lt;code&gt;-- ui&lt;&#x2F;code&gt; to the end of your playwright cli command. To make this easier I went ahead and added a npm scripts entry to the &lt;code&gt;package.json&lt;&#x2F;code&gt; file.  If you are following along I create a key named &lt;code&gt;ui&lt;&#x2F;code&gt; and add the command &lt;code&gt;npx playwright test --ui&lt;&#x2F;code&gt; what this allows me to do is run &lt;code&gt;npm run ui&lt;&#x2F;code&gt; and the command &lt;code&gt;npx playwright test --ui&lt;&#x2F;code&gt; will get executed from the command line. I find creating certain npm scripts really useful in taking long cli commands and reducing them to something you can quickly type.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-api-test-demo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;1.0.0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;This repository will serve as a place where I add API test Automation checks for articles written at &amp;lt;https:&#x2F;&#x2F;playwrightsolutions.com&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;index.js&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test --ui&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#f92649;color:#f8f8f0;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once &lt;code&gt;npm run ui&lt;&#x2F;code&gt; is run, you will notice a new window that pops up, after a few seconds all the tests in your suite should also show up on the left hand column. This is where we will run or debug our tests.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;04&#x2F;image-5.png&quot; alt=&quot;Image 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To run a test in ui mode select either a file, a group of tests &lt;code&gt;test.describe()&lt;&#x2F;code&gt; or an individual test &lt;code&gt;test()&lt;&#x2F;code&gt;. You can run the test or group of tests using the green play button. Once run you will be able to see the history of each test in the &lt;code&gt;Actions&lt;&#x2F;code&gt; panel, see the test code under source, console logs under console, http network requests under network, and test logs under logs. For debugging API, typically you will want to find specific network requests that were made and look at the different request and response + headers and body. All of that is viewable on each http request that is made from the Playwright Test you run. An example can be found below.&lt;&#x2F;p&gt;
&lt;p&gt;Though we aren&#x27;t utilizing all the features with UI (as we aren&#x27;t opening a browser), there is a ton of value in being able to run tests with all the details of the http network requests at our fingertips. Before the ui feature existed I found myself running the tests with traces always on, in order to view the test report to view the http requests via the trace file viewer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-playwright-test-trace-files-via-show-report&quot;&gt;Using Playwright Test Trace Files via Show-Report&lt;&#x2F;h2&gt;
&lt;p&gt;This was my go to way to debug tests prior to the &lt;code&gt;ui&lt;&#x2F;code&gt; mode being released. This method requires you to update your &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; to set the trace: &quot;on&quot;. This will make it where the trace files are recorded for each test, and will be available when viewing the Playwright Test Report.  An example can be found below, this can also be set when running from the command line overwriting whatever is in the &lt;code&gt;playwright.confg.ts&lt;&#x2F;code&gt; file by running &lt;code&gt;npx playwright test --trace on&lt;&#x2F;code&gt; &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-cli&quot;&gt;docs for command line&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;playwright.config.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  use: {
&lt;&#x2F;span&gt;&lt;span&gt;    baseURL: process.env.URL,
&lt;&#x2F;span&gt;&lt;span&gt;    ignoreHTTPSErrors: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    trace: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  retries: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  reporter: [[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;list&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once the tests run and are complete, it&#x27;s as simple as running &lt;code&gt;npx playwright show-report&lt;&#x2F;code&gt; which will bring up the Playwright Test Report, and because we set trace to &quot;on&quot; we will have all network requests in the report! Below is an example of the Test Report in action.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;console-log-console-table&quot;&gt;Console.log &#x2F; Console.table&lt;&#x2F;h2&gt;
&lt;p&gt;The below example I am running the command &lt;code&gt;npx playwright test tests&#x2F;booking&#x2F;booking.get.spec.ts:48&lt;&#x2F;code&gt; and adding the console.log() commands&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;04&#x2F;image-9.png&quot; alt=&quot;Image 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Another common method to debug your code is to add console.log statements within the codebase. Depending on the shape of your data, you could run into mixed results just using the console.log(body), if there are additional objects like the bookingdates listed below, you will notice they are displayed as [Object] in the console.log output.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;04&#x2F;image-6.png&quot; alt=&quot;Image 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When you want to see all the data, you can use the &lt;code&gt;JSON.stringify()&lt;&#x2F;code&gt; method to transform the json to a string. This is helpful if you want to see all the data, but isn&#x27;t very useful within your tests, as you no longer have a javascript object that you can use the dot notation to get info from.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;04&#x2F;image-8.png&quot; alt=&quot;Image 8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Another useful tip that &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;author&#x2F;joel&#x2F;&quot;&gt;Joel Black&lt;&#x2F;a&gt; taught me about is console.table(), you can pass an array of objects and get really nice formatting out of it. Note, I am passing in &lt;code&gt;body.bookings&lt;&#x2F;code&gt;. Viewing data this way can make it easier to see differences in what is returned for easy comparisons.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;04&#x2F;image-7.png&quot; alt=&quot;Image 9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;running-only-changed-tests-on-a-pull-request&quot;&gt;Running only changed tests on a pull request&lt;&#x2F;h2&gt;
&lt;p&gt;I recently covered how I do this in a recent post. It made enough sense for me to be a stand alone post. Implanting this can allow you to get quick feedback from your tests running in CI. What&#x27;s really nice is if you enable capturing the trace files on failures and using the article below, you will have access to the test artifacts after the test run.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-run-only-playwright-tests-that-changed-on-a&#x2F;&quot;&gt;Is it possible to run only Playwright Tests that changed in GitHub actions on a pull request?&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As you can see there are quite a few different ways to debug your tests as you are writing and maintaining them. I&#x27;m sure there are more, if you have any please reach and and share them with me on &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks for reading! If you found this helpful, reach out and let me know on &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; or consider &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;buying me a cup of coffee&lt;&#x2F;a&gt;. If you want more content delivered to you in your inbox subscribe below.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-3-debugging-api-tests-with-vs-code&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-04-17T12:30:56.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>GitHub Actions 中是否可以只运行 pull request 中更改的 Playwright 测试?</title>
        <published>2023-04-10T00:00:00+00:00</published>
        <updated>2023-04-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/is-it-possible-to-run-only-playwright-tests-that-changed-on-a/"/>
        <id>https://playwright.itest.info/blog/is-it-possible-to-run-only-playwright-tests-that-changed-on-a/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/is-it-possible-to-run-only-playwright-tests-that-changed-on-a/">&lt;p&gt;你是否在 review 同事的 GitHub pull request 时,曾问自己这个 Playwright 测试是否能通过?我就有过这样的经历,这也是促使我写这篇文章的原因。在我的工作环境中,我希望防止失败的测试进入自动化测试的主干分支,但同时也不想运行每一个 spec 文件而导致反馈延迟,所以我用以下方法解决了这个问题。&lt;&#x2F;p&gt;
&lt;p&gt;在我的情况下,我使用专门的代码库进行自动化测试,主要用于回归测试。我有一个&lt;code&gt;main&lt;&#x2F;code&gt;代码分支,任何新添加到代码库的代码都将通过&lt;code&gt;pull request&lt;&#x2F;code&gt;合并进来。这要求开发人员从&lt;code&gt;main&lt;&#x2F;code&gt;分支创建一个新分支,编写、编辑或删除代码,然后创建一个指向 main 分支的&lt;code&gt;pull request&lt;&#x2F;code&gt;。我们将在这个&lt;code&gt;pull request&lt;&#x2F;code&gt;事件触发时做一些事情。&lt;&#x2F;p&gt;
&lt;p&gt;对我来说,这样做的好处包括:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;当我最初创建 pull request 时,我可以确信我在本地编写和运行的测试也能在 CI 中通过。&lt;&#x2F;li&gt;
&lt;li&gt;我请求对 pull request 进行代码 review 的同事可以快速看到我修改的测试已运行并通过。&lt;&#x2F;li&gt;
&lt;li&gt;对于包含数百或上千个测试的测试套件,在 pull request 时只运行更改的文件可以提供更快的反馈,而不是等待所有测试运行完毕。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这个自动化测试逻辑主要做以下几件事：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;找出变化的文件：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;git diff&lt;&#x2F;code&gt;命令比较&lt;code&gt;main&lt;&#x2F;code&gt;分支和新建的&lt;code&gt;my_new_branch&lt;&#x2F;code&gt;分支。&lt;&#x2F;li&gt;
&lt;li&gt;这样可以得到两个分支之间所有变化的文件列表。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;筛选测试文件：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;从变化的文件中，只保留文件名包含&quot;.spec.ts&quot;的文件。&lt;&#x2F;li&gt;
&lt;li&gt;这些通常是 Playwright 的测试文件。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;保存文件列表：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;将筛选出的文件名列表（用空格分隔）保存到一个叫&lt;code&gt;CHANGED&lt;&#x2F;code&gt;的环境变量中。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;设置正确的 Git 历史深度：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;在 GitHub Actions 中，需要设置&lt;code&gt;actions&#x2F;checkout&lt;&#x2F;code&gt;的&lt;code&gt;fetch-depth&lt;&#x2F;code&gt;为 0。&lt;&#x2F;li&gt;
&lt;li&gt;这样做是为了确保 GitHub Actions 可以访问所有分支的完整历史。&lt;&#x2F;li&gt;
&lt;li&gt;如果不这样设置，使用默认值 1 可能会导致&lt;code&gt;git diff&lt;&#x2F;code&gt;命令失败，错误信息为&quot;fatal: Invalid revision range&quot;。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;关于历史深度的说明：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;随着项目变大，你可以根据需要调整&lt;code&gt;fetch-depth&lt;&#x2F;code&gt;的值。&lt;&#x2F;li&gt;
&lt;li&gt;但要记住，设置得太小可能会导致某些操作失败。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;04&#x2F;image-2.png&quot; alt=&quot;图片 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;GitHub Actions 日志 👆&lt;&#x2F;p&gt;
&lt;p&gt;以下是一个 GitHub actions .yml 文件的示例,展示了如何实现这一功能。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# .github-ci.yml
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright API Checks
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pull_request&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;workflow_dispatch&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;inputs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;base_url&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;URL, to run tests against&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;required&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;playwright-automation-checks&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;timeout-minutes&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;10
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;env&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;BASE_URL&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ github.event.inputs.base_url }}
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;CHANGED&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;checkout@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;fetch-depth&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;setup-node@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;node-version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;16
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Install node_modules and Playwright
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm ci --ignore-scripts
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright install --with-deps
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright install-deps
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Set BASE_URL if not passed in
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;env.BASE_URL == null
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;BASE_URL=https:&#x2F;&#x2F;automationintesting.online&#x2F;&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Create Test List if pull_request
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;github.event.pull_request
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;Creating a list of tests that have changed&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} | grep &amp;quot;.spec.ts&amp;quot; | tr &amp;#39;\n&amp;#39; &amp;#39; &amp;#39;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;CHANGED=$FILES&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run Playwright tests
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;CHANGED = ${{ env.CHANGED }}&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;The github event is: ${{ github.event_name }}&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          URL=${{ env.BASE_URL}} npx playwright test --workers=1 ${{ env.CHANGED }}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;需要注意的是,如果没有找到匹配&lt;code&gt;spec.ts&lt;&#x2F;code&gt;的文件,在运行&lt;code&gt;npx playwright test --workers=1 ${{ env.CHANGED}}&lt;&#x2F;code&gt;命令时,&lt;code&gt;CHANGED&lt;&#x2F;code&gt;变量将为空,所有测试都会运行(这对我来说是可以接受的)。&lt;&#x2F;p&gt;
&lt;p&gt;这种方法存在一个潜在的问题：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;分支同步问题：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;假设你正在一个特性分支上工作，准备创建 pull request。&lt;&#x2F;li&gt;
&lt;li&gt;如果在此期间，main 分支有了新的更新，而你的特性分支还没有合并这些更新。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;可能的后果：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;git diff&lt;&#x2F;code&gt;命令时，它会显示出所有的差异。&lt;&#x2F;li&gt;
&lt;li&gt;这包括 main 分支上的新更改，即使这些更改与你的工作无关。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;影响：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;这可能导致运行一些不必要的测试，因为系统会认为这些文件也被修改了。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;替代方案：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;我的第一个解决方案使用了 GitHub API。&lt;&#x2F;li&gt;
&lt;li&gt;使用 GitHub API 可以更精确地获取只在当前 pull request 中更改的文件。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;适用场景：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;对于小团队来说，这个问题可能影响不大。&lt;&#x2F;li&gt;
&lt;li&gt;但对于大型团队，特别是 main 分支频繁更新的项目：
&lt;ul&gt;
&lt;li&gt;使用 GitHub API 的方法可能更合适。&lt;&#x2F;li&gt;
&lt;li&gt;它可以更准确地识别实际更改的文件，避免运行不必要的测试。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;总的来说，这个问题在小团队中可能不太明显，但在大型、快节奏的开发环境中，使用更精确的 GitHub API 方法可能会带来显著的效率提升。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;04&#x2F;image.png&quot; alt=&quot;图片 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wo-de-di-yi-ge-jie-jue-fang-an&quot;&gt;我的第一个解决方案&lt;&#x2F;h3&gt;
&lt;p&gt;当我第一次实现这个功能时,我的 Create Test List if pull_request 部分看起来很不一样。下面是代码,我使用了 GitHub API,发送 curl 请求获取完整的更改文件列表。这需要我在 GitHub 中创建一个个人访问令牌,并将其作为密钥添加到 GitHub Actions 中。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Create Test List if pull_request
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;github.event.pull_request
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    echo &amp;quot;Creating a list of tests that have changed&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    FILES=$(curl --header &amp;quot;Authorization: Bearer ${{ secrets.REPO_ACCESS_TOKEN }}&amp;quot; --request GET &amp;#39;https:&#x2F;&#x2F;api.github.com&#x2F;repos&#x2F;${{ github.repository }}&#x2F;pulls&#x2F;${{ github.event.pull_request.number }}&#x2F;files&amp;#39; | jq -r &amp;#39;.[] | .filename&amp;#39; | grep &amp;quot;spec.ts&amp;quot; |  tr &amp;#39;\n&amp;#39; &amp;#39; &amp;#39;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;    echo &amp;quot;CHANGED=$FILES&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;感谢阅读!如果你觉得这篇文章有帮助,请在&lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt;上联系我,或考虑&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;为我买杯咖啡&lt;&#x2F;a&gt;。如果你想在收件箱中收到更多内容,请在下方订阅。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-run-only-playwright-tests-that-changed-on-a&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-04-10T12:30:50.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在Playwright UI测试中找到在Chrome中不断消失元素的定位器</title>
        <published>2023-04-07T00:00:00+00:00</published>
        <updated>2023-04-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-do-i-find-a-locator-that-keeps-disappearing-in-chrome-for-a-playwright-ui-test/"/>
        <id>https://playwright.itest.info/blog/how-do-i-find-a-locator-that-keeps-disappearing-in-chrome-for-a-playwright-ui-test/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-do-i-find-a-locator-that-keeps-disappearing-in-chrome-for-a-playwright-ui-test/">&lt;p&gt;我知道我之前遇到过很多次这个问题。感觉就像是在比赛中选择元素并快速查看控制台以实际获取元素名称。&lt;&#x2F;p&gt;
&lt;p&gt;这个简单的技巧可能会帮助你在交互时保持下拉菜单或弹出窗口的可见性。&lt;&#x2F;p&gt;
&lt;p&gt;我们将使用 Chrome 开发者工具的“模拟聚焦页面”功能。在 Mac 上打开 Chrome 开发者工具，使用[⌘ 或 Ctrl]+[P]命令菜单，搜索&lt;code&gt;&amp;gt;emulate a focused page&lt;&#x2F;code&gt;并按 Enter。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2022&#x2F;09&#x2F;image.png&quot; alt=&quot;Image 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这将使某些元素保持聚焦状态，以便它们可以被选择。&lt;&#x2F;p&gt;
&lt;p&gt;要亲自试试这个功能，你可以访问&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;&quot;&gt;MDN 文档&lt;&#x2F;a&gt;并尝试检查搜索框中的一个搜索结果。&lt;&#x2F;p&gt;
&lt;p&gt;感谢&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;sulco&#x2F;status&#x2F;1305841873945272321&quot;&gt;@sulco&lt;&#x2F;a&gt;提供的灵感。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>在 VSCode 中启用 pw:api 以增强playwright的调试功能</title>
        <published>2023-04-03T00:00:00+00:00</published>
        <updated>2023-04-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/enabling-pw-api-in-playwright-test-for-visual-studio-code/"/>
        <id>https://playwright.itest.info/blog/enabling-pw-api-in-playwright-test-for-visual-studio-code/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/enabling-pw-api-in-playwright-test-for-visual-studio-code/">&lt;p&gt;刚开始使用 Playwright 时，你可能运行了你的第一个测试命令，&lt;code&gt;npx playwright test&lt;&#x2F;code&gt;。真棒！用已故绝地大师欧比-旺-克诺比的话来说，&quot;&lt;strong&gt;你已经迈出了进入更大世界的第一步。&lt;&#x2F;strong&gt;&quot;&lt;&#x2F;p&gt;
&lt;p&gt;深入挖掘，你可能已经学会了通过传递环境变量来在运行测试时在控制台中显示调试信息。&lt;code&gt;DEBUG=pw:api npx playwright test&lt;&#x2F;code&gt; 你甚至可能已经将其永久设置为系统环境变量，这本身就很酷！&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;pw-api.png&quot; alt=&quot;图 1: DEBUG=pw:api npx playwright test 输出&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;看看这些美丽、详细的调试信息！&lt;&#x2F;p&gt;
&lt;h3 id=&quot;xian-zai-shi-jian-guo-qu-liao-yi-duan&quot;&gt;现在，时间过去了一段。&lt;&#x2F;h3&gt;
&lt;p&gt;你探索了更多的 Playwright 生态系统，设置了 Visual Studio Code 并安装了 Playwright Test for VSCode 插件！现在你手中有了如此强大的工具！你打开了测试资源管理器，启动了一个测试，当你查看测试输出时，现在你看到的只是...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;vscode-no-debug.png&quot; alt=&quot;图 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;所有优雅的调试信息去哪了？:(&lt;&#x2F;p&gt;
&lt;p&gt;如果你喜欢优雅的调试信息，并通过 Playwright Test for VSCode 运行测试，请继续阅读！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rang-wo-men-kuai-su-xiu-fu-zhe-ge-wen-ti&quot;&gt;让我们快速修复这个问题！&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;在 VS Code 中打开 &lt;code&gt;扩展&lt;&#x2F;code&gt;。&lt;&#x2F;li&gt;
&lt;li&gt;找到 Playwright Test for VS Code 并点击 &lt;code&gt;齿轮&lt;&#x2F;code&gt;图标&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;vscode-click-the-gear.png&quot; alt=&quot;图 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;
&lt;p&gt;然后点击 &lt;code&gt;扩展设置&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;现在，一个名为 &lt;code&gt;设置&lt;&#x2F;code&gt; 的窗口会出现在你打开的文件的主选项卡中。注意 &lt;code&gt;Playwright: Env&lt;&#x2F;code&gt; 部分。在这里，我们可以保存 Playwright 测试在通过 VS Code 执行时使用的环境变量。
点击 &lt;code&gt;在 settings.json 中编辑&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;vscode-edit-in-settingsjson.png&quot; alt=&quot;图 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;一个名为 &lt;code&gt;settings.json&lt;&#x2F;code&gt; 的文件现在将在你的编辑器中打开，光标位于 &lt;code&gt;&quot;playwright.env&quot;&lt;&#x2F;code&gt; 部分内。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;仔细在 &lt;code&gt;&quot;playwright.env&quot;&lt;&#x2F;code&gt; 的花括号内添加 &lt;code&gt;&quot;DEBUG&quot;: &quot;pw:api&quot;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;vscode-add-env.png&quot; alt=&quot;图 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;
&lt;p&gt;保存并关闭 &lt;code&gt;settings.json&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;运行你的测试并打开 &lt;code&gt;查看测试输出&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;ta-you-chu-xian-liao&quot;&gt;它又出现了！&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;vscode-api-debugging-to-vscode-pw-1.png&quot; alt=&quot;图 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;哦，优雅的调试信息，我多么想念你。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读！如果你觉得这篇文章有帮助，请通过 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; 联系我，或者考虑 &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;请我喝一杯咖啡&lt;&#x2F;a&gt;。如果你想要更多内容发送到你的收件箱，请在下方订阅。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;enabling-pw-api-in-playwright-test-for-visual-studio-code&#x2F;&quot;&gt;网址来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-04-03&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright API 测试权威指南：第2部分 - 更多用例</title>
        <published>2023-03-27T00:00:00+00:00</published>
        <updated>2023-03-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-2-adding-more-in-depth-checks/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-2-adding-more-in-depth-checks/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-2-adding-more-in-depth-checks/">&lt;p&gt;让我们来测试 post 请求。我不会在这组测试中深入太多细节,因为本文还有 PUT 和 DELETE 请求要讨论。下面的断言要花一些时间来重构,因为需要创建一些辅助函数和数据工厂函数,以使 POST spec 更简单。我不会详细介绍这些函数,但简单概括如下:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;createRandomBookingBody&lt;&#x2F;code&gt; - 需要房间 ID、入住日期和退房日期,并创建一个可用于 POST 到 &lt;code&gt;booking&#x2F;&lt;&#x2F;code&gt; api 接口的请求体。💡 我们也可以用它来断言响应体!!!&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;futureOpenCheckinDate&lt;&#x2F;code&gt; - 需要一个房间 ID,并返回一个日期字符串 &lt;code&gt;2023-03-31T00:00:00.000Z&lt;&#x2F;code&gt; (这使用了获取预订 API 并进行一些快速计算)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;stringDateByDays&lt;&#x2F;code&gt; - 需要一个日期字符串和一个可选的数字。它会根据今天的日期添加或减去天数,并返回一个日期字符串 &lt;code&gt;2023-03-24&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createRandomBookingBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureOpenCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;datafactory&#x2F;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;stringDateByDays &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;helpers&#x2F;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F; POST requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;futureOpenCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkInString &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toISOString&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;split&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOutString &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createRandomBookingBody&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkInString&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkOutString
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST new booking with full body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;201&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;通过在 beforeEach 块中使用这些函数,我能够添加额外的测试并为每个想要编写的测试获得新的请求体数据和可用的入住日期。实际的 POST 请求测试将使用完整的请求体创建一个新预订(包括必需和可选参数),注意这个 API 调用不需要身份验证,所以没有传递头部信息,我传入了通过 &lt;code&gt;createRandomDataBody()&lt;&#x2F;code&gt; 函数生成的 &lt;code&gt;requestBody&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;我们以这种方式创建请求体的好处是(不是硬编码),现在我可以使用我们为 postBody 设置的变量和数据,并在我们的断言中使用它。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; POST booking 响应体
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Testy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;McTesterSon&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;2023-05-10&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;2023-05-11&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;下面的断言顺序也与响应体相同,这是故意的,目的是保持组织性!注意在这个场景中,我取了 &lt;code&gt;body&lt;&#x2F;code&gt;,也就是完整的 JSON 响应,创建了一个新变量 &lt;code&gt;booking&lt;&#x2F;code&gt; 并用它进行断言。我喜欢这样做,因为它帮助我快速可视化我正在对预订对象进行断言,在那之下是 &lt;code&gt;bookingdates&lt;&#x2F;code&gt; 对象。对于小型 API 响应来说可能不是那么重要,但当你有一个包含 20-50 个项目的 JSON 对象的响应体,或者你想遍历对象数组时,你会非常高兴你遵循了这种模式。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;requestBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我们可以进一步添加测试,比如:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;只使用必需输入的 POST 预订&lt;&#x2F;li&gt;
&lt;li&gt;缺少必需输入的 POST 预订&lt;&#x2F;li&gt;
&lt;li&gt;使用错误类型的 POST 预订(&quot;true&quot; 而不是 true 或 &quot;1&quot; 而不是 1)&lt;&#x2F;li&gt;
&lt;li&gt;使用身份验证的 POST 预订,尽管不需要&lt;&#x2F;li&gt;
&lt;li&gt;与现有预订日期重叠的 POST 预订&lt;&#x2F;li&gt;
&lt;li&gt;使用过去日期的 POST 预订&lt;&#x2F;li&gt;
&lt;li&gt;等等...&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;但我们已经验证了具有这组参数的 api 正在按预期工作,所以让我们继续前进!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;delete-qing-qiu-jian-cha&quot;&gt;DELETE 请求检查&lt;&#x2F;h3&gt;
&lt;p&gt;对于这组检查,我再次使用了我创建的数据工厂函数 &lt;code&gt;createFutureBooking&lt;&#x2F;code&gt; 和 &lt;code&gt;auth&lt;&#x2F;code&gt; 来设置测试。注意我在 describe 块中创建了 let 变量,有些已经设置了值,有些将在 beforeAll (如果不改变)或 beforeEach 块中获得它们的值,beforeEach 块将在每次测试运行前为变量分配一个新值。我在这个测试中将 roomId 硬编码为 1,因为我知道系统会有它可用,但这是一个风险,因为有人可能会删除 roomId 1,在这种情况下,我需要创建一个房间,并为该房间创建一个预订来执行我们的删除预订测试。我包含的 3 个测试是:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;DELETE 特定房间 ID 的预订&lt;&#x2F;li&gt;
&lt;li&gt;DELETE 不存在 ID 的预订&lt;&#x2F;li&gt;
&lt;li&gt;无身份验证的 DELETE 预订 ID&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;auth &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;datafactory&#x2F;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingSummary&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createFutureBooking&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;datafactory&#x2F;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;{id} DELETE requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;auth&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;admin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureBooking &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createFutureBooking&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureBooking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;DELETE booking with specific room id:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;delete&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;202&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBooking &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getBookingSummary&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBooking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;DELETE booking with an id that doesn&amp;#39;t exist&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;delete&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;999999&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;404&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;DELETE booking id without authentication&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;delete&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;403&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在第一个测试中,你可以看到我们首先在 beforeEach 块中创建一个预订,我们将传入删除方法的预订 ID 与头部一起使用,因为我们需要授权。这是我一直使用的另一种模式,我计划进行主要断言的任何 api 接口,总是将 &lt;code&gt;response&lt;&#x2F;code&gt; 变量分配给它。这为我们将来继续构建测试提供了优势。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;const response = await request.delete(`booking&#x2F;${bookingId}`, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: cookies },
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;对于这第一个测试,我进行了两个断言,一个是对 response.status() 的断言,另一个是期望 response.text() 为空字符串 &quot;&quot;。对我来说,我想进一步验证预订是否真的被删除了,所以我没有使用测试中的 playwright &lt;code&gt;request&lt;&#x2F;code&gt; 方法,而是创建了另一个数据工厂 &lt;code&gt;getBookingSummary(bookingId)&lt;&#x2F;code&gt;，它返回 GET booking&#x2F;summary?roomid=${bookingId} 的响应体,我可以对其进行断言。在这种情况下,我想确保 bookings.length 为 0。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;put-qing-qiu-jian-cha&quot;&gt;PUT 请求检查&lt;&#x2F;h3&gt;
&lt;p&gt;在下一节中,我保留了一些冗长的内容,即在每个测试部分中创建一个新的 putBody,有很多方法可以组织代码。我本可以像在发布新预订 api 接口上创建请求体那样将其抽象出来。在这种情况下,我希望非常清楚地传递了哪些信息,因为在我的一个测试中,我尝试在没有名字的情况下进行 PUT 请求。&lt;&#x2F;p&gt;
&lt;p&gt;我遵循的一个模式是,我在 describe 块中创建所有变量。这允许我在测试和测试步骤中访问这些变量以进行断言。目前我也硬编码了一些变量。我可以使用像 faker 这样的工具来使数据唯一,如果我这样做,我会在 beforeEach() 中设置变量,这样每个测试都会得到一个新名字。它看起来像这样:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;{id} PUT requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;faker&lt;&#x2F;span&gt;&lt;span&gt;.name.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;firstName&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我正在进行的检查是:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;PUT 特定房间 ID 的预订 + 验证预订已更新&lt;&#x2F;li&gt;
&lt;li&gt;在 putBody 中没有 firstname 的 PUT 预订&lt;&#x2F;li&gt;
&lt;li&gt;PUT 不存在 ID 的预订&lt;&#x2F;li&gt;
&lt;li&gt;PUT ID 为文本的预订&lt;&#x2F;li&gt;
&lt;li&gt;使用无效身份验证的 PUT 预订 ID&lt;&#x2F;li&gt;
&lt;li&gt;无身份验证的 PUT 预订 ID&lt;&#x2F;li&gt;
&lt;li&gt;没有 put body 的 PUT 预订 ID&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;在我看来,这是一个更有趣的 api 接口,因为有很多不同的组合可以测试。&lt;&#x2F;p&gt;
&lt;p&gt;我想特别指出的一个地方是在第一个测试 &lt;code&gt;PUT booking with specific room id&lt;&#x2F;code&gt; 中,我使用 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-test#test-step&quot;&gt;test.step()&lt;&#x2F;a&gt; 方法来帮助拆分测试。在我的 test.step 中,我实际上使用了 &lt;code&gt;getBookingById()&lt;&#x2F;code&gt;,这是我创建的一个数据工厂方法,用于返回发送的预订 ID 的当前主体。💡 重要提示:如果你使用 test.step,确保在 test.step 前面使用 &lt;code&gt;await&lt;&#x2F;code&gt;,如果你像我第一次编写测试时那样错过了这一点,你会在试图弄清楚发生了什么时敲键盘。&lt;&#x2F;p&gt;
&lt;p&gt;对我来说,我发现我错过了 await 是因为我试图让 test.step 中的一个断言失败...我在测试中做不到,回到 Playwright 文档中发现了这个问题。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Verify booking was updated&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getBookingById&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingDates &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;当我第一次为这些测试编写自动化时,我遇到了很多来自被测应用程序的 &lt;a href=&quot;https:&#x2F;&#x2F;http.cat&#x2F;409&quot;&gt;409 错误消息&lt;&#x2F;a&gt;。代码代表冲突。具体的冲突是围绕已经在使用的入住和退房日期,这导致我创建了使我们编写这些测试更容易的数据工厂方法。没有这些函数,我会在故障排除时花费大量时间手动更新日期,我们会有硬编码的数据,这最终会导致更多的 409 错误和测试的不一致性。&lt;&#x2F;p&gt;
&lt;p&gt;这些数据工厂方法允许我让测试独立于任何其他测试数据或测试创建它需要的数据。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;你不应该依赖测试 1 来设置测试 2 的数据。这是一个陷阱!!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;我特别为 &lt;code&gt;futureOpenCheckinDate()&lt;&#x2F;code&gt; 和 &lt;code&gt;createFutureBooking()&lt;&#x2F;code&gt; 感到自豪。我在下面包含了我为每个创建的 &lt;a href=&quot;https:&#x2F;&#x2F;jsdoc.app&#x2F;&quot;&gt;js
doc&lt;&#x2F;a&gt; 描述。(我刚刚了解到 jsdoc,我非常喜欢它!!!)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;image-11.png&quot; alt=&quot;图片 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;futureOpenCheckinDate() jsdoc&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;image-12.png&quot; alt=&quot;图片 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;createFutureBooking() jsdoc&lt;&#x2F;p&gt;
&lt;p&gt;所有的 PUT 请求测试可以在下面找到。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;auth &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;datafactory&#x2F;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingById&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureOpenCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;createFutureBooking&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;datafactory&#x2F;booking&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;stringDateByDays &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&#x2F;..&#x2F;lib&#x2F;helpers&#x2F;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;{id} PUT requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Happy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;McPathy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;testy@mcpathyson.com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;phone &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;5555555555555&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureBooking&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;auth&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;admin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureBooking &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;createFutureBooking&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureBooking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;futureOpenCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT booking with specific room id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      bookingid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      roomid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      firstname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      lastname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      depositpaid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      phone: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;phone&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      bookingdates: {
&lt;&#x2F;span&gt;&lt;span&gt;        checkin: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;        checkout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;booking&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Verify booking was updated&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getBookingById&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingDates &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;getBookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT booking without firstname in putBody&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      bookingid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      roomid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      lastname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      depositpaid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      phone: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;phone&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      bookingdates: {
&lt;&#x2F;span&gt;&lt;span&gt;        checkin: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;        checkout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;BAD_REQUEST&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errorCode&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;errorMessage&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Validation failed for argument [0] in public org.springframework.http.ResponseEntity&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fieldErrors&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Firstname should not be blank&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT booking with an id that doesn&amp;#39;t exist&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      bookingid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      roomid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      firstname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      lastname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      depositpaid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      phone: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;phone&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      bookingdates: {
&lt;&#x2F;span&gt;&lt;span&gt;        checkin: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;        checkout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;delete&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;999999&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;404&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT booking id that is text&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      bookingid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      roomid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      firstname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      lastname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      depositpaid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      phone: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;phone&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      bookingdates: {
&lt;&#x2F;span&gt;&lt;span&gt;        checkin: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;        checkout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;asdf&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;404&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;timestamp&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.status).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;404&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Not Found&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;booking&#x2F;asdf&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT booking id with invalid authentication&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      bookingid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      roomid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      firstname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      lastname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      depositpaid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      phone: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;phone&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      bookingdates: {
&lt;&#x2F;span&gt;&lt;span&gt;        checkin: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;        checkout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;403&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT booking id without authentication&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      bookingid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      roomid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      firstname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      lastname: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      depositpaid: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      email: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      phone: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;phone&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      bookingdates: {
&lt;&#x2F;span&gt;&lt;span&gt;        checkin: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;        checkout: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;stringDateByDays&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;futureCheckinDate&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;putBody&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;403&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;PUT booking id without put body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;timestamp&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.status).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;400&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Bad Request&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;booking&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我们总是可以添加更多的断言和检查,但现在我的信心已经提高,我觉得如果预订 api 接口引入任何重大变化,我们的自动化应该会提醒我们,这样我们就可以进行调查并探索更大的系统会受到怎样的影响。&lt;&#x2F;p&gt;
&lt;p&gt;总的来说,我们有 19 个检查,在我的本地机器上使用 4 个工作线程运行了 15.9 秒&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;image-13.png&quot; alt=&quot;图片 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果你读到这里,给自己一个 ⭐️,你应得的!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读!如果你觉得这很有帮助,请在 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; 上联系我并让我知道,或考虑 &lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;给我买杯咖啡&lt;&#x2F;a&gt;。如果你想在收件箱中收到更多内容,请在下面订阅。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-2-adding-more-in-depth-checks&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2023-03-27T12:30:37.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何通过 Playwright 模拟特定响应的 HTTP 网络请求</title>
        <published>2023-03-24T00:00:00+00:00</published>
        <updated>2023-03-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-do-i-intercept-network-traffic-and-save-specific-values-from-http-requests/"/>
        <id>https://playwright.itest.info/blog/how-do-i-intercept-network-traffic-and-save-specific-values-from-http-requests/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-do-i-intercept-network-traffic-and-save-specific-values-from-http-requests/">&lt;p&gt;我最近遇到一个场景，我想检查一个 UI 元素，但 UI 元素中的数据是一个计数器，而我套件中的其他测试会影响这个计数。我看到有两种方法可以解决这个问题。一种是拦截网络流量并抓取这个值，然后对这个值进行断言，另一种方法是拦截并 mock(自己设置 1 个假的值) 一个我传入的值作为响应。&lt;&#x2F;p&gt;
&lt;p&gt;在本文中，我将演示一个简单的拦截和模拟网络请求的场景。&lt;&#x2F;p&gt;
&lt;p&gt;首先，我们创建一个名为&lt;code&gt;count&lt;&#x2F;code&gt;的变量，并将其设置为 100。这个值可以是任何数字，因为它将被传入测试名称、mockMessageCount 函数，并在断言中使用。&lt;&#x2F;p&gt;
&lt;p&gt;在这个测试中，我在 beforeEach 方法中有很多操作，理想情况下，我会将很多这些操作抽象到一个页面对象中，使测试更加简洁。这个测试假设在&lt;code&gt;test&lt;&#x2F;code&gt;部分开始时你已经登录。在 beforeEach 中，我还调用了&lt;code&gt;mockMessageCount(page, count)&lt;&#x2F;code&gt;方法。此代码调用了测试规范底部列出的异步函数。这个函数可以很容易地存在于不同的文件中，通过从它自己的文件中导出函数并在其他测试用例中导入。下面的代码使用 route 类，对于以&quot;&#x2F;message&#x2F;count&quot;结尾的任何 URL，我们将直接 mock 这个接口的返回值。我通过这个接口&lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;message&#x2F;count&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;message&#x2F;count&lt;&#x2F;a&gt; mock 返回值。这是不需要身份验证的，所以你可以点击并查看当前的计数。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mockMessageCount&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;messageCount&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;**&#x2F;message&#x2F;count&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fulfill&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;      status: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      body: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;({ count: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;messageCount &lt;&#x2F;span&gt;&lt;span&gt;}),
&lt;&#x2F;span&gt;&lt;span&gt;    })
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;由于我们在 mockMessageCount 中传入了 100 作为计数，因此在 Playwright 测试用例中 mock 了&lt;code&gt;100&lt;&#x2F;code&gt; 这个返回。完整的测试如下所示。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;selectors &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;admin 检查&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;count &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;selectors&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;setTestIdAttribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;data-testid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这调用了一个在此页面底部存在的异步函数，它接受页面实例和一个数字
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mockMessageCount&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Let me hack!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Admin panel&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;admin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByTestId&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;submit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;验证消息计数为${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Logout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveText&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Logout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;messageCountSpan &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;[href*=&amp;quot;#&#x2F;admin&#x2F;messages&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;messageCountSpan&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 这个函数使用route类，拦截服务器发送的内容，并用我们提供的响应进行满足（模拟！）
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export async &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;mockMessageCount&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;messageCount&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;**&#x2F;message&#x2F;count&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fulfill&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;      status: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      body: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;({ count: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;messageCount &lt;&#x2F;span&gt;&lt;span&gt;}),
&lt;&#x2F;span&gt;&lt;span&gt;    })
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;playwright-wen-dang-lian-jie&quot;&gt;Playwright 文档链接&lt;&#x2F;h3&gt;
&lt;p&gt;查看文档将帮助你更深入地了解 page.route 类的所有可能性。下面是一个简要总结。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page#page-route&quot;&gt;页面类 - page.route()&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-route&quot;&gt;Route 类&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;code&gt;abort()&lt;&#x2F;code&gt; 终止 route 的请求。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;continue()&lt;&#x2F;code&gt; 继续 route 的请求，并可选择性地进行覆盖。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;fallback()&lt;&#x2F;code&gt; 当多个路由匹配给定模式时，它们按照注册顺序的相反顺序运行。这样，最后注册的路由总是可以覆盖所有先前的路由。在下面的示例中，请求将首先由最底层的处理程序处理，然后它会回退到上一个，最后由第一个注册的路由终止。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;fetch()&lt;&#x2F;code&gt; 执行请求并获取结果，而不进行满足，以便可以修改响应然后进行满足。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;fulfill()&lt;&#x2F;code&gt; 使用给定的响应满足 route 的请求。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;示例中的代码可以在这个仓库中找到。&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;playwright-demo&#x2F;blob&#x2F;master&#x2F;tests&#x2F;ui&#x2F;automationintesting.online&#x2F;messageCount.spec.ts&quot;&gt;代码示例&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-do-i-intercept-network-traffic-and-save-specific-values-from-http-requests&#x2F;&quot;&gt;URL Source&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-04-24&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright API 测试权威指南：第1部分 - API 测试基础之 GET 请求（包括需要登录和不需要登录的接口）</title>
        <published>2023-03-13T00:00:00+00:00</published>
        <updated>2023-03-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-1-basics-of-api-testing-get/"/>
        <id>https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-1-basics-of-api-testing-get/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/the-definitive-guide-to-api-test-automation-with-playwright-part-1-basics-of-api-testing-get/">&lt;p&gt;现在让我们开始构建 Playwright 的 API 测试。首先,我们需要一个可以测试的网站。我有一个不断更新的优秀网站列表。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;awesome-sites-to-test-on&quot;&gt;GitHub - BMayhew&#x2F;awesome-sites-to-test-on&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;从这个列表中,我选择了一个我知道有前后端分离,而且在可预见的未来不会挂掉的网站。&lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这个网站是 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;markwinteringham&#x2F;&quot;&gt;Mark Winteringham&lt;&#x2F;a&gt; 和 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;friendlytester&#x2F;&quot;&gt;Richard Bradshaw&lt;&#x2F;a&gt; 的&lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.com&#x2F;&quot;&gt;测试自动化研讨会&lt;&#x2F;a&gt;的配套网站,同时也是《Testing Web APIs》一书中的主要测试系统(这是一本我强烈推荐的好书)。该书使用 Java 提供了 API 自动化的示例,而我将使用 Playwright 构建相同的示例以及书中的其他示例。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.amazon.com&#x2F;Testing-Web-APIs-Mark-Winteringham&#x2F;dp&#x2F;1617299537?crid=2Y3N4STRLVSBA&amp;amp;keywords=api+test+book&amp;amp;qid=1678342103&amp;amp;sprefix=api+test+book%2Caps%2C120&amp;amp;sr=8-9&amp;amp;ufe=app_do%3Aamzn1.fos.006c50ae-5d4c-4777-9bc0-4513d670b6bc&amp;amp;linkCode=ll1&amp;amp;tag=amazonid54611-20&amp;amp;linkId=e32a5140f8d1ddd80cabbc7b471a4d42&amp;amp;language=en_US&amp;amp;ref_=as_li_ss_tl&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;image.png&quot; alt=&quot;图片 3&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆 Testing Web APIs 书籍封面&lt;&#x2F;p&gt;
&lt;p&gt;我不会详细介绍这个网站的功能，但我建议你花些时间用浏览器的开发者工具中的 Network 标签页来探索这个网站。这样做可以帮助你很好地了解不同 API 接口的作用及其在网站 UI 中的使用方式。需要注意的是，我们今天要自动化测试的 API 接口只在网站的管理员页面使用（页面底部有一个链接，用户名：admin | 密码：password）。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;yu-ding-api-jie-kou&quot;&gt;预订 api 接口&lt;&#x2F;h3&gt;
&lt;p&gt;我们首先关注 restful booker 平台的预订 api 接口。幸运的是,有一个 Swagger 文档(&lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;booking&#x2F;swagger-ui&#x2F;index.html&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;booking&#x2F;swagger-ui&#x2F;index.html&lt;&#x2F;a&gt;)可以查看可用的 api 接口。&lt;&#x2F;p&gt;
&lt;p&gt;在开始编写代码之前,我总是先通过像 Postman 这样的工具把 api 接口调通。这次我尝试使用 &lt;a href=&quot;https:&#x2F;&#x2F;www.thunderclient.com&#x2F;&quot;&gt;Thunder Client&lt;&#x2F;a&gt;,这是一个可以在 VS Code 中使用的扩展。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;image-1.png&quot; alt=&quot;图片 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我首先注意到的是一些 api 接口需要 token&#x2F;鉴权&#x2F;登录。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;booking&#x2F;summary?roomid=1&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;booking&#x2F;summary?roomid=1&lt;&#x2F;a&gt; (不需要 token)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;booking&#x2F;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;booking&#x2F;&lt;&#x2F;a&gt; (需要 token)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;经过一番探索，我发现有一个 API 接口（&lt;a href=&quot;https:&#x2F;&#x2F;automationintesting.online&#x2F;auth&#x2F;swagger-ui&#x2F;index.html#&#x2F;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;auth&#x2F;swagger-ui&#x2F;index.html#&#x2F;&lt;&#x2F;a&gt;），可以用来生成令牌。我可以将这个令牌作为 HTTP header 传递到上面的 &#x2F;booking&#x2F; 调用中，格式为 &lt;code&gt;cookie: token={token-goes-here}&lt;&#x2F;code&gt;。API 认证有很多不同的方式，这是构建 API 自动化时首先需要弄清楚的事情之一。这里有一篇不错的文章，介绍了认证中使用的一些不同技术：&lt;a href=&quot;https:&#x2F;&#x2F;dev.to&#x2F;abbeyperini&#x2F;beginners-guide-to-http-part-5-authentication-3p2p&quot;&gt;HTTP 初学者指南第 5 部分 认证&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tan-suo-bei-ce-xi-tong&quot;&gt;探索被测系统!!!&lt;&#x2F;h3&gt;
&lt;p&gt;这一步至关重要。如果你对正在测试的系统没有深入了解，请先停下来做这件事。对我来说，我先在 Thunder Client 中建立了一个包含所有 API 接口的集合。在这个过程中，我了解了哪些 API 接口需要认证，哪些 API 接口需要参数，还了解了 JSON 主体未记录的限制（例如：电话号码至少需要 11 个字符，并且在请求主体中需要一个字符串）。下面是我录制的一个交互所有 API 接口的演示会话。我将令牌参数化为一个环境变量，因为我发现必须在每个请求中更新它。这让我意识到，随着测试套件的增长，我肯定会将其保存为 API 自动化中的一个变量！&lt;&#x2F;p&gt;
&lt;p&gt;当我第一次探索应用并开始思考如何自动化所有内容时，一个问题是我们将如何管理测试数据。我注意到一个非常好的事情是，大约每 10 分钟，创建的任何数据都会从数据库中清除，并重新填充一些静态数据（特别是 James Dean 的一个预订，入住日期在过去，即 &lt;code&gt;2022-02-01&lt;&#x2F;code&gt;）。在本教程中，我们将基于这些数据创建大部分断言，因为我们假设它们将始终可用。如果不是这样，我们每次都需要创建数据来进行断言（我们将在后面的教程中讲到）。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;rang-wo-men-bian-xie-di-yi-ge-yong-li&quot;&gt;让我们编写第一个用例!&lt;&#x2F;h3&gt;
&lt;p&gt;创建一个目录来存放你的测试套件。如果你是第一次做这件事情，你可以创建一个新文件夹。假设你已经安装了 node,进入空文件夹所在的目录,让我们运行&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; init playwright@latest
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;运行这条命令之后，工具会问你一些问题,我的回答是:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Typescript&lt;&#x2F;li&gt;
&lt;li&gt;tests&lt;&#x2F;li&gt;
&lt;li&gt;n (我们现在还不需要 GitHub actions 文件。)&lt;&#x2F;li&gt;
&lt;li&gt;n (我们不需要浏览器,我们在测试 API!)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;image-2.png&quot; alt=&quot;图片 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;命令完成后,你的主目录中应该有一个 &lt;code&gt;tests&lt;&#x2F;code&gt; 和 &lt;code&gt;tests-examples&lt;&#x2F;code&gt; 文件夹,以及 &lt;code&gt;package.json&lt;&#x2F;code&gt; 和 &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;首先,我们将 &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; 改动一下&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;devices &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dotenv&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;defineConfig&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  use: {
&lt;&#x2F;span&gt;&lt;span&gt;    baseURL: process.env.URL,
&lt;&#x2F;span&gt;&lt;span&gt;    ignoreHTTPSErrors: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    trace: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;retain-on-failure&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  retries: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  reporter: [[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;list&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;], [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;安装 &lt;code&gt;dotenv&lt;&#x2F;code&gt;,这将允许我们在项目根目录使用 .env 文件来存储环境变量。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span&gt; install dotenv&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --save
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;接下来删除 &lt;code&gt;&#x2F;tests-examples&#x2F;&lt;&#x2F;code&gt; 目录&lt;&#x2F;p&gt;
&lt;p&gt;然后我们将修改 &lt;code&gt;example.spec.ts&lt;&#x2F;code&gt; 来实现一个最简单的 API GET 请求。官方 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-api-testing#writing-api-test&quot;&gt;API 测试文档&lt;&#x2F;a&gt;描述了两种发起 API 调用的方法,内置的 &lt;code&gt;request&lt;&#x2F;code&gt; fixture(我们将在下面使用)或使用 &lt;code&gt;request context&lt;&#x2F;code&gt;。我们将重点使用 &lt;code&gt;request&lt;&#x2F;code&gt; fixture 进行测试,它可以在用例内部使用。当我们需要在测试用例外进行 API 调用时(从测试块外的另一个文件中的函数),我们将使用 &lt;code&gt;request context&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET booking summary&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;booking&#x2F;summary?roomid=1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个测试将对 发送 1 个不需要鉴权的 GET 请求，地址是 summary?roomid=1。我目前将响应保存到 &lt;code&gt;response&lt;&#x2F;code&gt; 变量中,它代表 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-apiresponse&quot;&gt;APIResponse&lt;&#x2F;a&gt; 类。这使我们可以访问响应体对象、JSON 格式的响应体、文本格式的响应体、响应头、状态码、状态文本、URL,以及一个名为 &lt;code&gt;.ok()&lt;&#x2F;code&gt; 的方法,如果状态码在 200-299 之间,它将返回 true。&lt;&#x2F;p&gt;
&lt;p&gt;对于我们的第一个测试,我只对 &lt;code&gt;response.status()&lt;&#x2F;code&gt; 进行断言,期望它为 200。我还展示了如何判断 JSON 返回值里的内容,因为我们将希望对它进行一些断言。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;image-3.png&quot; alt=&quot;图片 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;简单 GET 请求的输出 👆&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;image-6.png&quot; alt=&quot;图片 7&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;耶!我们做到了 👆&lt;&#x2F;p&gt;
&lt;h3 id=&quot;rang-wo-men-zheng-li-yi-xia-bing-tian-jia-yi-xie-geng-hao-de-duan-yan&quot;&gt;让我们整理一下并添加一些更好的断言&lt;&#x2F;h3&gt;
&lt;p&gt;首先在根目录创建一个 &lt;code&gt;.env&lt;&#x2F;code&gt; 文件，然后加入这一行。这一行的作用是设置了 URL 个环境变量作为接口测试的 BaseURL&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;URL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;automationintesting.online&#x2F;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;现在我们可以重构我们的 spec 文件了。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;我想按 api 接口组织我的 spec,所以我要在测试目录中创建一个 &lt;code&gt;&#x2F;booking&#x2F;&lt;&#x2F;code&gt; 文件夹。&lt;&#x2F;li&gt;
&lt;li&gt;将 &lt;code&gt;example.spec.ts&lt;&#x2F;code&gt; 重命名为 &lt;code&gt;booking.get.spec.ts&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;更新 spec,添加一个 describe 块,一个更好的测试名称,以及一些额外的断言。&lt;&#x2F;li&gt;
&lt;li&gt;最后再添加一个辅助函数 isValidDate() 来验证返回的入住和退房日期是否为真实日期。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;summary?roomid={id}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET booking summary with specific room id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;summary?roomid=1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThanOrEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span&gt;)) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;shi-xian-geng-duo-get-api-jie-kou-bing-shi-xian-zi-dong-jian-cha&quot;&gt;实现更多 GET api 接口并实现自动检查&lt;&#x2F;h3&gt;
&lt;p&gt;接下来的两个 GET api 接口需要通过保存为 cookie 的 token 进行认证,所以我们必须将 cookie 放到 header 里才能成功发起调用。剩下的两个接口是 &lt;code&gt;GET &#x2F;booking&lt;&#x2F;code&gt; 和 &lt;code&gt;GET &#x2F;booking&#x2F;{id}&lt;&#x2F;code&gt;。我回到 thunder client 把这两个接口调通了,这也可以通过用例本身完成。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;image-7.png&quot; alt=&quot;图片 8&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;⬆️ 对 booking&#x2F; 进行 GET 调用时 Thunder Client 的响应&lt;&#x2F;p&gt;
&lt;p&gt;修改现有的代码，我首先在第 4 行的 &lt;code&gt;describe&lt;&#x2F;code&gt; 块内添加了一个新变量 &lt;code&gt;savedToken&lt;&#x2F;code&gt;，这是一个我们将在下一步中以编程方式设置的值，但为了测试，我先硬编码了一个值。你可以看到我们的 GET 请求现在有了一个额外的 header 选项，我们在其中传递了一个 cookie，值为 &lt;code&gt;token=${savedToken}&lt;&#x2F;code&gt;。在 JavaScript 中，当使用 ` 定义字符串时，js 允许你在 ${} 内添加代码，这称为插值，在编写自动化测试时非常方便。我们还将对我们期望存在的数据进行断言，另外请注意，我们对返回的 response 中的每个值都进行了断言。如果我们假设所有数据都应该返回，这通常是一个好的做法。&lt;&#x2F;p&gt;
&lt;p&gt;在此过程中，我还发现了一个应该报告给开发人员的 bug，在 &lt;code&gt;booking&#x2F;summary&lt;&#x2F;code&gt; 调用中，&lt;code&gt;bookingDates&lt;&#x2F;code&gt; 是驼峰式的，而在 &lt;code&gt;booking&#x2F;&lt;&#x2F;code&gt; 的响应中，&lt;code&gt;bookingdates&lt;&#x2F;code&gt; 对象是全小写的。这虽然是个小问题，但通过自动化这部分内容，很容易注意到这些差异。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F; GET requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;savedToken &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;r2dBKvt8rCo5p74s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET booking summary with specific room id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;summary?roomid=1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThanOrEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET all bookings with details&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;token=${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;savedToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;` &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThanOrEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;James&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Dean&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;booking&#x2F;{id}
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span&gt;)) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;下一步，我们将为带详细信息的 GET booking by id 接口添加自动化用例，即 GET booking&#x2F;1。为此，我复制了前面的测试并开始修改，以匹配我在 Thunder Client 中看到的内容。首先，这里没有 bookings 数组，所以我从每个断言中删除了所有这些，并移动了 toBeGreaterThanOrEqual() 断言。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F; GET requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;savedToken &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;r2dBKvt8rCo5p74s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET booking summary with specific room id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;summary?roomid=1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThanOrEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingDates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET all bookings with details&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;token=${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;savedToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;` &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;.length).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeGreaterThanOrEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;James&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Dean&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookings&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET booking by id with details&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;token=${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;savedToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;` &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;roomid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;firstname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;James&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;lastname&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Dean&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;depositpaid&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkin&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;bookingdates&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;checkout&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isValidDate&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span&gt;)) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;现在我们有了一组很好的用例,测试了预订 API 下三个 GET 调用的正常路径场景。接下来,我们在 beforeAll() 钩子里增加 1 个请求，然后保存 鉴权要用到的 cookie 的值。&lt;&#x2F;p&gt;
&lt;p&gt;我首先创建了一个 post 请求,将用户名和密码传入 data,并检查 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-apiresponse&quot;&gt;APIResponse&lt;&#x2F;a&gt; 返回的响应。我使用 VS Code 的 Playwright 调试器来做这件事,它在编写代码和深入了解 Playwright 和 JavaScript 的工作原理时非常有用。&lt;&#x2F;p&gt;
&lt;p&gt;通过对 response header 的研究,我决定直接使用 &lt;code&gt;response.headers()&lt;&#x2F;code&gt; 来拿到响应返回的 header，然后进一步拿到 cookie 的值。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F; GET requests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;auth&#x2F;login&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      data: {
&lt;&#x2F;span&gt;&lt;span&gt;        username: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;admin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;set-cookie&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如你所见，我在 &lt;code&gt;describe&lt;&#x2F;code&gt; 块内创建了变量，然后在 &lt;code&gt;beforeAll&lt;&#x2F;code&gt; 块中对其进行设置。这是我和团队一直遵循的最佳实践，因为这样可以在所有测试中重复使用这些变量。请注意，我使用了 &lt;code&gt;let&lt;&#x2F;code&gt; 关键字声明变量，这样可以让变量在 &lt;code&gt;beforeAll&lt;&#x2F;code&gt; 块中被修改或设置。&lt;&#x2F;p&gt;
&lt;p&gt;现在我们有了 cookies 值，里面的信息很多，但我们只关注 token,我们可以重构我们的代码来实现这一点。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; from
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET all bookings with details&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;token=${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;savedToken&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;` &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; to
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;GET all bookings with details&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;booking&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;      headers: { cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cookies &lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;为确保我们的代码不会出现偶发性故障,我将运行 &lt;code&gt;npx playwright test --repeat-each=10&lt;&#x2F;code&gt;命令,这将使每个测试运行 10 次,然后很幸运 💥 它们全都通过了!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;03&#x2F;image-8.png&quot; alt=&quot;图片 9&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;测试结果 30 个通过 👆&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;可以在&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;playwrightsolutions&#x2F;playwright-api-test-demo&quot;&gt;这里&lt;&#x2F;a&gt;找到代码的仓库和分支(api-part1)。&lt;&#x2F;p&gt;
&lt;p&gt;在下一部分(第 2 部分)中,我们将继续处理这个示例代码,为 GET booking api 接口添加更多断言,并覆盖其他的预订 api 接口。我们还将重构一些代码,将可重用的方法放在代码库的单独区域,使一切整洁有序。&lt;&#x2F;p&gt;
&lt;p&gt;非常感谢 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;joel-black-1344a267&#x2F;&quot;&gt;Joel Black&lt;&#x2F;a&gt; 和 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;sgapanovich&#x2F;&quot;&gt;Sergei Gapanovich&lt;&#x2F;a&gt;,没有他们的影响、反馈和代码审查,这些例子会糟糕得多 😅。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读!如果你觉得这篇文章有帮助,请在 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; 上联系我,或考虑&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;给我买杯咖啡&lt;&#x2F;a&gt;。如果你想在收件箱中收到更多内容,请在下方订阅。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;the-definitive-guide-to-api-test-automation-with-playwright-part-1-basics-of-api-testing-get&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-03-13&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何playwright去检查按钮或者控件是否被禁用</title>
        <published>2023-02-27T00:00:00+00:00</published>
        <updated>2023-02-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/utilizing-the-tohaveattribute-method-when-making-assertions-in-playwright-to-check-if-a-button-is-enabled-or-disabled/"/>
        <id>https://playwright.itest.info/blog/utilizing-the-tohaveattribute-method-when-making-assertions-in-playwright-to-check-if-a-button-is-enabled-or-disabled/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/utilizing-the-tohaveattribute-method-when-making-assertions-in-playwright-to-check-if-a-button-is-enabled-or-disabled/">&lt;p&gt;简单来说很多情况下，我们需要在用例里判断某个按钮是否处于被禁用(灰掉)的状态。&lt;&#x2F;p&gt;
&lt;p&gt;在 UI 层进行测试时，您可能希望根据用户的操作验证按钮或元素是否处于特定状态。我经常遇到的一个常见场景是登录、填写地址信息或输入信用卡详细信息。您不希望在必填字段中没有值时，用户就有机会点击提交按钮，通常这些按钮会被禁用。&lt;&#x2F;p&gt;
&lt;p&gt;在这些情况下，我发现构建测试以验证某些按钮是否正确处于禁用（不可点击状态）是很有帮助的。&lt;&#x2F;p&gt;
&lt;p&gt;下面是一个通过 CodePen 的基本示例，我们将针对其编写自动化测试。&lt;&#x2F;p&gt;
&lt;p&gt;展示禁用按钮直到输入文本的 CodePen 示例。&lt;&#x2F;p&gt;
&lt;p&gt;对于断言，我们将使用 toHaveAttribute() 方法。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;expect({locator}).toHaveAttribute(&quot;{name}&quot;, &quot;{value}&quot;)&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在我们的示例中，定位器将是我们要检查是否禁用的按钮，属性名称将是 &quot;disabled&quot;，值将是 &quot;true&quot;。根据您正在测试的站点中禁用属性的实现方式，可能禁用只是一个没有值的属性，如果是这种情况，值实际上是一个空字符串。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果 DOM 看起来像这样
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;disabled&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Click Me&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveAttribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;disabled&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 如果 DOM 看起来像这样
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;disabled&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;Click Me&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveAttribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;disabled&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;现在让我们看看完整的脚本。请注意，由于我们使用 CodePen，为了与生成的 HTML 元素交互，我们需要查看名为 &quot;CodePen&quot; 的 iFrame，所有元素都在这里。你还会注意到，在我的示例中，我为我们要交互的元素创建了变量，如果我们想创建多种场景，这些变量可以很容易地添加到页面对象中。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;codepen.io&#x2F;bmayhew&#x2F;pen&#x2F;eYLdwVg&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 创建我们将要交互的元素的变量
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;codePenFrame &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;frameLocator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;iframe[name=&amp;quot;CodePen&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;textInput &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;codePenFrame&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByPlaceholder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;fill me&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;codePenFrame&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Click Me&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;codePenFrame&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;id=result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Disabled 属性是活动的
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveAttribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;disabled&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;textInput&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Testing 1234&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;keyboard&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;press&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Tab&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Disabled 属性不再活动
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;not&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveAttribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;disabled&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;You clicked the button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;以下是关于定位器断言的文档链接，因为还有许多其他方法可以验证 DOM 中的元素。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-locatorassertions#locator-assertions-to-have-attribute&quot;&gt;LocatorAssertions&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;来源链接&lt;&#x2F;strong&gt;: &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;utilizing-the-tohaveattribute-method-when-making-assertions-in-playwright-to-check-if-a-button-is-enabled-or-disabled&#x2F;&quot;&gt;Utilizing the toHaveAttribute() method when making assertions in Playwright to check if a button is enabled or disabled&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;发布时间&lt;&#x2F;strong&gt;: 2023-02-27&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何使用 Playwright 定位元素的父级?</title>
        <published>2023-02-13T00:00:00+00:00</published>
        <updated>2023-02-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-do-you-locate-the-parent-of-an-element-with-playwright/"/>
        <id>https://playwright.itest.info/blog/how-do-you-locate-the-parent-of-an-element-with-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-do-you-locate-the-parent-of-an-element-with-playwright/">&lt;p&gt;本文将介绍 5 种定位父级元素的方法!&lt;&#x2F;p&gt;
&lt;p&gt;UI 自动化测试中的一个常见挑战是,如何访问或控制网页上没有唯一标识符的元素。以下面的&lt;a href=&quot;https:&#x2F;&#x2F;the-internet.herokuapp.com&#x2F;challenging_dom&quot;&gt;Challenging DOM&lt;&#x2F;a&gt;页面为例,定位编辑按钮并不是一件轻松的任务。页面上有至少 10 个不同的编辑按钮,我们不能仅仅简单的点击带有&quot;edit&quot;文本的链接。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;02&#x2F;image-4.png&quot; alt=&quot;图片 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;首先,我们需要在页面上找到一个可以交互的、在 DOM 中保持一致的唯一标识。在典型的 UI 测试中,当你需要验证表格或网格中的数据时,其父元素通常会有某种唯一标识符。在本例中,我将使用&quot;Adipisci5&quot;作为唯一标识符。我将演示几种不同的方法来与包含&quot;Adipisci5&quot;的行中的 edit 按钮进行交互。以下是页面上 HTML 的截图。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;02&#x2F;image-5.png&quot; alt=&quot;图片 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我们的目标是找到包含文本&quot;Adipisci5&quot;的数据单元格&lt;td&gt;,然后找到完整的行(如上图高亮显示),最后点击编辑链接。接下来,我将展示几种不同的方法来获取父级行&lt;tr&gt;。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;shi-yong-playwright-locator-de-has-xuan-xiang&quot;&gt;使用 Playwright locator 的 has 选项&lt;&#x2F;h2&gt;
&lt;p&gt;第一个例子使用了 locator 类的 &lt;code&gt;has&lt;&#x2F;code&gt; 选项。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { has: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=&amp;quot;Adipisci5&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) });
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page#page-locator&quot;&gt;locator&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;css-tong-guo-wen-ben-pi-pei&quot;&gt;CSS: 通过文本匹配&lt;&#x2F;h2&gt;
&lt;p&gt;这个例子利用了 Playwright 内置的 CSS 文本匹配功能。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;css=tr:has-text(&amp;quot;Adipisci5&amp;quot;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;other-locators#css-matching-by-text&quot;&gt;Other Locators&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-yong-getbyrole-he-accessible-name&quot;&gt;使用 getByRole 和 accessible name&lt;&#x2F;h3&gt;
&lt;p&gt;这个例子使用了新发布的 getByRole 方法,通过 name 选项来查找唯一标识符。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Adipisci5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page#page-get-by-role&quot;&gt;getByRole&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-getbyrole-he-filter-fang-fa&quot;&gt;使用 getByRole 和 filter() 方法&lt;&#x2F;h2&gt;
&lt;p&gt;这是一种类似上面的方法,但我们使用了 .filter() 方法,它非常强大,因为你还可以在 hasText 部分使用正则表达式(参见第二个例子)。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;({ hasText: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Adipisci5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;({ hasText: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Adi&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ci5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators#filtering-locators&quot;&gt;Filter&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-xpath-locator&quot;&gt;使用 xpath locator(&quot;..&quot;)&lt;&#x2F;h2&gt;
&lt;p&gt;这种方法可行,而且是获取父元素的一种快速方法。总的来说,我不太喜欢在代码中使用 xpath,但这是我唯一允许使用的 xpath 场景。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=Adipisci5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;xpath=..&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;02&#x2F;xpath.png&quot; alt=&quot;图片 11&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;other-locators#parent-element-locator&quot;&gt;other locators&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;下面是一个完整的例子,展示了使用不同方法访问父元素,然后用这些父元素来点击选定行中的编辑链接,并通过对 URL 来断言来确认编辑按钮被点击。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Challenging DOM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;beforeEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;the-internet.herokuapp.com&#x2F;challenging_dom&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用 Playwright locator 的 has 选项查找父元素&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { has: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=&amp;quot;Adipisci5&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) })
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用 has-text 查找父元素&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;css=tr:has-text(&amp;quot;Adipisci5&amp;quot;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用 getByRole locator 和 accessible name 查找父元素&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  }) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Adipisci5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用 getByRole locator 和带正则表达式的 filter 查找父元素&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  }) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;({ hasText: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Ad&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;sci5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用 xpath 查找父元素&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=Adipisci5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;xpath=&amp;#39;text=edit&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用分步骤的 xpath 查找父元素&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cell &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=Adipisci5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cell&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;editLink &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;editLink&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#edit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如你所见,Playwright 在定位和处理父元素时提供了极大的灵活性。如果我遗漏了什么,请在 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; 上联系我并告诉我。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读!如果你觉得这篇文章有帮助,请在 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; 上联系我,或考虑&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;给我买杯咖啡&lt;&#x2F;a&gt;。如果你想收到更多内容直接发送到你的收件箱,请在下方订阅。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-do-you-locate-the-parent-of-an-element-with-playwright&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-02-13&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何使用 Playwright 测试含页面重定向网页的网站?</title>
        <published>2023-02-06T00:00:00+00:00</published>
        <updated>2023-02-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-do-i-test-a-website-that-has-a-page-redirect-with-playwright/"/>
        <id>https://playwright.itest.info/blog/how-do-i-test-a-website-that-has-a-page-redirect-with-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-do-i-test-a-website-that-has-a-page-redirect-with-playwright/">&lt;p&gt;在测试各种 Web 应用时,你不可避免的会遇到链接点击或页面操作导致重定向的情况。这种重定向可能通过 JavaScript 或 HTTP 请求实现。如果你想了解更多关于重定向实现方式的详细信息,可以参考&lt;a href=&quot;https:&#x2F;&#x2F;www.semrush.com&#x2F;blog&#x2F;redirects&#x2F;&quot;&gt;这篇指南&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dai-zhong-ding-xiang-gong-neng-de-shi-li-wang-zhan&quot;&gt;带重定向功能的示例网站&lt;&#x2F;h2&gt;
&lt;p&gt;在这个演示中,我创建了一个简单的 HTML 页面,其中包含一个&quot;重定向&quot;按钮。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;02&#x2F;image.png&quot; alt=&quot;图片 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆 简单的带重定向按钮的网页&lt;&#x2F;p&gt;
&lt;p&gt;点击该按钮后,页面会添加一段文本,开始重定向过程,并添加一个点击后会弹出提示框的&quot;订阅&quot;按钮。以下是实现这个功能的代码。你可以在这个 GitHub 仓库的 public 文件夹中找到完整代码:&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;new-playwright-init&#x2F;tree&#x2F;master&#x2F;public&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;new-playwright-init&#x2F;tree&#x2F;master&#x2F;public&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;redirect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 重定向前显示消息
&lt;&#x2F;span&gt;&lt;span&gt;  document.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;4秒后将重定向到新的URL...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 4秒后重定向到新页面
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    window.location &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;4000&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;document.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;createElement&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;innerText &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;订阅&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;addEventListener&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;alert&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;你点击了订阅按钮!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  document.body.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;appendChild&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;yi-ge-shi-bai-de-playwright-ce-shi-yong-li&quot;&gt;一个失败的 Playwright 测试用例&lt;&#x2F;h2&gt;
&lt;p&gt;为了使这个练习更有趣,我在重定向页面上也添加了一个&quot;订阅&quot;按钮。现在,我们编写一个测试用例:访问主页,点击重定向按钮,等待 4 秒,然后页面重定向到 PlaywrightSolutions.com 主页。之后,我们要点击&quot;订阅&quot;按钮,并开始填写订阅信息。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;访问主页并完成订阅&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;127.0.0.1:3000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=重定向&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=订阅&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;frameLocator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;iframe[title=&amp;quot;portal-popup&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;id=input-name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=注册&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=输入你的邮箱地址&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然而,当我运行这段代码时,测试并没有按预期进行。它点击了重定向之前的页面上的&quot;订阅&quot;按钮,而不是 PlaywrightSolutions.com 页面上也就是重定向之后的页面上的&quot;订阅&quot;按钮。因此,脚本在执行 &lt;code&gt;await popup.locator(&quot;id=input-name&quot;).fill(&quot;test&quot;);&lt;&#x2F;code&gt; 这一步时失败了。
&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;02&#x2F;image-1.png&quot; alt=&quot;图片 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ce-shi-chao-shi-cuo-wu-xiang-qing&quot;&gt;测试超时错误详情&lt;&#x2F;h2&gt;
&lt;p&gt;出现这个问题的原因是,一旦点击&quot;重定向&quot;按钮,重定向页面上就出现了一个匹配&quot;订阅&quot;文本的元素。测试脚本立即点击了这个元素,触发了一个警告框。当重定向最终完成时,已经没有可以输入名字的弹出窗口了。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;page-waitforurl-jie-jue-fang-an-tui-jian-zuo-fa&quot;&gt;page.waitForURL 解决方案(推荐做法)&lt;&#x2F;h2&gt;
&lt;p&gt;解决这个问题有几种方法,但最简单有效的是使用 page 对象的 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-page#page-wait-for-url&quot;&gt;waitForURL 方法&lt;&#x2F;a&gt;。我们只需在点击重定向按钮之后,点击订阅按钮之前添加这个等待。waitForURL 命令可以使用完整 URL、glob 模式或正则表达式。更多细节可以查阅上面链接的文档。这个解决方案在使用 &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;in-playwright-test-is-there-an-easy-way-to-loop-the-test-multiple-times-to-check-for-flakiness&#x2F;&quot;&gt;&lt;code&gt;--repeat-each=10&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;in-playwright-test-is-there-an-easy-way-to-loop-the-test-multiple-times-to-check-for-flakiness&#x2F;&quot;&gt;参数&lt;&#x2F;a&gt;多次运行时表现稳定。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;访问主页并完成订阅 - 使用 waitForURL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;127.0.0.1:3000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=重定向&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=订阅&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;frameLocator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;iframe[title=&amp;quot;portal-popup&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;id=input-name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=注册&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=输入你的邮箱地址&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;shi-yong-page-waitforurl-he-javascript-promise&quot;&gt;使用 page.waitForURL 和 JavaScript Promise&lt;&#x2F;h2&gt;
&lt;p&gt;虽然代码稍长,但在遇到复杂情况时,你可以使用下面这种基于 Promise 的方法,这也是&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;navigations#multiple-navigations&quot;&gt;官方文档&lt;&#x2F;a&gt;中提到的。需要注意的是,曾经有一个名为 &lt;code&gt;waitForNavigation()&lt;&#x2F;code&gt; 的方法,但由于存在竞态条件问题,现已被弃用。使用下面的 Promise 方法时,我发现还需要在点击&quot;订阅&quot;按钮前添加 &lt;code&gt;page.waitForLoadState()&lt;&#x2F;code&gt;。相比之下,单独使用 &lt;code&gt;await page.waitForURL()&lt;&#x2F;code&gt; 的优势在于它内置了导航等待,默认会等到 &lt;code&gt;load&lt;&#x2F;code&gt; 事件触发才认为操作完成。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;访问主页并完成订阅 - 使用 Promise&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;127.0.0.1:3000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 在点击之前开始等待导航,注意这里没有 await
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navigationPromise &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 触发导航的操作
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=重定向&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;navigationPromise&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForLoadState&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;networkidle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=订阅&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;frameLocator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;iframe[title=&amp;quot;portal-popup&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;id=input-name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=注册&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=输入你的邮箱地址&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;另一种使用 &lt;code&gt;Promise.all([])&lt;&#x2F;code&gt; 的方法,它会等待数组中所有的 Promise 都 resolve 后才继续:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;访问主页并完成订阅 - 使用 Promise.all&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;127.0.0.1:3000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Promise&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;([
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 重要:在点击之前设置等待导航
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 触发导航的操作
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=重定向&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;  ]);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForLoadState&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;networkidle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=订阅&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;frameLocator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;iframe[title=&amp;quot;portal-popup&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;id=input-name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=注册&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=输入你的邮箱地址&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;02&#x2F;image-2.png&quot; alt=&quot;图片 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆 梗图，开个玩笑&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-expect-topass-de-fan-mo-shi-zuo-fa&quot;&gt;使用 expect().toPass() 的反模式做法&lt;&#x2F;h2&gt;
&lt;p&gt;首先要强调,请不要在实际项目中这样做。我展示这种方法只是为了说明它的可能性。在最近的一个版本中,Playwright 引入了 .toPass() 方法,允许在特定的断言块内进行重试。你可以利用这个特性来等待 page.url() 变为期望的值。为了减少这种方法的不稳定性,我不得不添加 &lt;code&gt;await page.waitForLoadState(&quot;networkidle&quot;);&lt;&#x2F;code&gt; 这一行,它会等待网络活动至少停止 500ms 才继续。但这种方法无法保证在继续之前 DOM 和所有网络请求都已完成。再次强调,这种方法不推荐使用,仅作为技术探讨!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;访问主页并完成订阅 - 反模式&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;http:&#x2F;&#x2F;127.0.0.1:3000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=重定向&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toPass&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForLoadState&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;networkidle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=订阅&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;frameLocator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;iframe[title=&amp;quot;portal-popup&amp;quot;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;id=input-name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;fill&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=注册&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;popup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;text=输入你的邮箱地址&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我希望这些方法能帮助你更轻松地处理网页重定向测试。如果你已经读到这里,那真是太棒了! 🙌 不过,我还是建议你重点关注 &lt;code&gt;page.waitForURL 解决方案&lt;&#x2F;code&gt; 部分的第一种方法,这才是最佳实践。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢你的阅读!如果你觉得这篇文章有帮助,欢迎在 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; 上与我联系,或者考虑&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;请我喝杯咖啡&lt;&#x2F;a&gt;。如果你想接收更多类似的内容,可以在下方订阅我们的通讯。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-do-i-test-a-website-that-has-a-page-redirect-with-playwright&#x2F;&quot;&gt;URL 来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间:2023 年 2 月 6 日 13:30:22&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何获取 Playwright 测试运行后所有通过或失败测试的详细信息</title>
        <published>2023-01-23T00:00:00+00:00</published>
        <updated>2023-01-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/is-it-possible-to-get-a-list-of-all-passed-or-failed-tests-after-a-playwright-test-run/"/>
        <id>https://playwright.itest.info/blog/is-it-possible-to-get-a-list-of-all-passed-or-failed-tests-after-a-playwright-test-run/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/is-it-possible-to-get-a-list-of-all-passed-or-failed-tests-after-a-playwright-test-run/">&lt;p&gt;在我刚开始使用 Playwright 时,我曾尝试回答这个问题,但一直找不到合适的解决方案。幸运的是,Playwright 团队开发了 reporter 功能,让我们可以轻松创建&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-reporters#custom-reporters&quot;&gt;自定义报告&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;这促使我开发了一个自定义 reporter, 它可以输出我需要的特定信息,并以可在测试运行后用 json 展示，json 文件是可以解析的，这为其他系统的集成提供了便利。我的目标是创建一个 JSON 文件,包含运行时长、最终状态(通过&#x2F;失败),以及通过、失败和跳过的测试列表。有了这些信息,我就可以轻松地以编程方式重试任何失败的测试,并将失败的测试列表作为 Slack 通知集成的一部分。&lt;&#x2F;p&gt;
&lt;p&gt;这让我兴奋地宣布:我创建了一个 NPM 包,可以为你完成这项工作!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;playwright-json-summary-reporter&quot;&gt;playwright-json-summary-reporter&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在你的 Playwright 项目中使用这个包非常简单。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-zhuang&quot;&gt;安装&lt;&#x2F;h2&gt;
&lt;p&gt;在你的 Playwright 目录下运行以下命令来安装这个包。这会将依赖项添加到你的 &lt;code&gt;package.json&lt;&#x2F;code&gt; 文件中。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; npm install playwright-json-summary-reporter&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --save-dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;shi-yong-fang-fa&quot;&gt;使用方法&lt;&#x2F;h2&gt;
&lt;p&gt;现在修改你的 &lt;code&gt;playwright.config.ts&lt;&#x2F;code&gt; 文件,加入这个自定义报告器:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span&gt;  reporter: [
&lt;&#x2F;span&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-json-summary-reporter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 其他reporter
&lt;&#x2F;span&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;dot&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;现在当你运行测试时,应该会在目录根目录下生成一个新的 &lt;code&gt;summary.json&lt;&#x2F;code&gt; 文件。下面是该文件的一个示例。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; summary.json
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;durationInMS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;41565&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;passed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&#x2F;create-user-account.spec.ts:34:3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&#x2F;create-user-account.spec.ts:55:3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&#x2F;delete-user-account.spec.ts:47:3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&#x2F;login.spec.ts:12:3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&#x2F;login.spec.ts:37:3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&#x2F;login.spec.ts:61:3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;api&#x2F;login.spec.ts:84:3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&#x2F;codepen&#x2F;copyToClipboard.spec.ts:5:1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&#x2F;codepen&#x2F;copyToClipboard.spec.ts:17:1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&#x2F;internet.app&#x2F;selectPresentElement.spec.ts:10:1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&#x2F;internet.app&#x2F;selectPresentElement.spec.ts:35:1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&#x2F;grep.spec.ts:4:3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&#x2F;grep.spec.ts:9:3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&#x2F;loginUser.spec.ts:22:3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&#x2F;loginUserNoImages.spec.ts:23:3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&#x2F;loginUser.spec.ts:37:3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&#x2F;loginUserNoImages.spec.ts:40:3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ui&#x2F;registerUser.spec.ts:17:1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;skipped&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [],
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;failed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [],
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;warned&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [],
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;timedOut&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [],
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;passed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;startedAt&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;1674431153277
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;有了这个文件,我现在可以使用命令行工具如 &lt;code&gt;cat&lt;&#x2F;code&gt; 和 &lt;code&gt;jq&lt;&#x2F;code&gt; 来解析文件并从命令行获取数据。下面的命令会获取失败数组中的所有值,并将换行符替换为空格,这样你就有了一个可以用来重新运行失败测试的文件列表。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;failed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;cat&lt;&#x2F;span&gt;&lt;span&gt; summary.json &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;jq&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -r &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;.failed[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39; &amp;#39; &amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;`
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这也让我们可以把 Playwright 的执行结果集成道其他应用，比如 slack 通知!&lt;&#x2F;p&gt;
&lt;p&gt;如果我提供的方法不够有用,你只是想要一个包含测试运行详情的纯文本文件,你可以看看 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;stephenkilbourn&#x2F;&quot;&gt;Stephen Kilbourn&lt;&#x2F;a&gt; 的报告摘要包。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;@skilbourn&#x2F;playwright-report-summary&quot;&gt;@skilbourn&#x2F;playwright-report-summary&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读!如果你觉得这篇文章有帮助,可以在 &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;mynetwork&#x2F;discovery-see-all&#x2F;?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=butchmayhew&quot;&gt;LinkedIn&lt;&#x2F;a&gt; 上联系我,或者考虑&lt;a href=&quot;https:&#x2F;&#x2F;ko-fi.com&#x2F;butchmayhew&quot;&gt;给我买杯咖啡&lt;&#x2F;a&gt;。如果你想获得更多内容直接发送到你的收件箱,请在下方订阅。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-get-a-list-of-all-passed-or-failed-tests-after-a-playwright-test-run&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2023-01-23T13:30:50.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何使用 playwright 访问浏览器的剪切板</title>
        <published>2023-01-09T00:00:00+00:00</published>
        <updated>2023-01-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-do-i-access-the-browser-clipboard-with-playwright/"/>
        <id>https://playwright.itest.info/blog/how-do-i-access-the-browser-clipboard-with-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-do-i-access-the-browser-clipboard-with-playwright/">&lt;p&gt;最近我遇到了一个问题，我想与我正在测试的系统中的一个具有“复制到剪贴板”功能的部分交互。这具体是一个我可以生成然后分享的链接。这个部分是我们客户入职流程的一部分，我们认为需要对此进行自动化检查。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;01&#x2F;image-1.png&quot; alt=&quot;图片1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我们将使用 codepen.io 示例进行测试&lt;&#x2F;p&gt;
&lt;p&gt;我们要做的第一件事是导航到网站：&lt;a href=&quot;https:&#x2F;&#x2F;codepen.io&#x2F;shaikmaqsood&#x2F;pen&#x2F;XmydxJ&quot;&gt;https:&#x2F;&#x2F;codepen.io&#x2F;shaikmaqsood&#x2F;pen&#x2F;XmydxJ&lt;&#x2F;a&gt;，并点击一个链接或按钮，将某个项目复制到浏览器剪贴板。接下来，我们将实际与浏览器交互以获取复制的值。为了与剪贴板交互，我们将使用在页面类&lt;code&gt;page.evaluate()&lt;&#x2F;code&gt;上调用的 evaluate 方法。代码如下所示。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;copyToClipboard.spec.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;验证复制到剪贴板功能1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;codepen.io&#x2F;shaikmaqsood&#x2F;pen&#x2F;XmydxJ&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;frameLocator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;#result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Copy TEXT 1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;clipboardText1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;navigator.clipboard.readText()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;clipboardText1&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toContain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Hello, I&amp;#39;m TEXT 1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;playwright-demo&#x2F;blob&#x2F;master&#x2F;tests&#x2F;ui&#x2F;codepen&#x2F;copyToClipboard.spec.ts&quot;&gt;Github 代码示例&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;为了在无人干预的情况下运行此代码，还有另一个步骤，即授予浏览器访问剪贴板的权限。具体来说，需要在&lt;code&gt;use:{}&lt;&#x2F;code&gt;块中添加&lt;code&gt;permissions: [&quot;clipboard-read&quot;]&lt;&#x2F;code&gt;。没有这个选项，你将在 chrome 中看到这个弹窗，你必须手动允许。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2023&#x2F;01&#x2F;image.png&quot; alt=&quot;图片2：codepen.io请求查看复制到剪贴板的文本和图片（阻止&#x2F;允许按钮）&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;通过添加以下权限，你将不再收到此由 playwright 控制的浏览器中的弹出窗口。值得注意的是，为了在 headless: true 模式下与 codepen.io 网站交互，我必须传递一个自定义的 userAgent 来绕过 Cloudflare 的“你是人类吗”检查。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;playwright.config.ts
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;PlaywrightTestConfig &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;PlaywrightTestConfig &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  use: {
&lt;&#x2F;span&gt;&lt;span&gt;    browserName: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;chromium&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    headless: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    permissions: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;clipboard-read&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    userAgent: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;some custom ua&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;yin-yong&quot;&gt;引用&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-do-i-access-the-browser-clipboard-with-playwright&#x2F;&quot;&gt;来源网址&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间：2023-01-09&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Playwright 中如何根据元素可见性来条件性点击?</title>
        <published>2022-12-16T00:00:00+00:00</published>
        <updated>2022-12-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/is-it-possible-in-playwright-to-conditionally-click-an-element-depending-on-which-one-is-present/"/>
        <id>https://playwright.itest.info/blog/is-it-possible-in-playwright-to-conditionally-click-an-element-depending-on-which-one-is-present/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/is-it-possible-in-playwright-to-conditionally-click-an-element-depending-on-which-one-is-present/">&lt;p&gt;最近在 Playwright 的 Slack 频道中有人询问是否可以在两个元素中点击可见的那个。有一个回答让我深入了解了 JavaScript 中的 Promise。&lt;&#x2F;p&gt;
&lt;p&gt;解决方案使用了 Promise.race() 方法,该方法接受一个 Promise 数组,并返回最先完成的那个 Promise。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Promise.race() 方法是 Promise 并发方法之一。当你想要第一个完成的异步任务,但不关心它最终的状态(成功或失败)时,这个方法很有用。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;这对我们来说很棒,因为只要我们要交互的定位器有一个是存在的,测试就不会失败。我发现 the-internet.herokuapp.com 网站有一个消失元素的页面可以验证这一点。刷新页面时,&quot;Portfolio&quot;元素总是存在,而&quot;Gallery&quot;元素只是偶尔出现。&lt;&#x2F;p&gt;
&lt;p&gt;下面的代码片段的注释解释了发生的事情,还有一些可用于调试的 console.log 注释。这种实现的优点是,如果被测系统有多个需要考虑的状态,你可以随意向数组中添加任意数量的定位器。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;使用 promise.race 点击数组中的一个元素&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;the-internet.herokuapp.com&#x2F;disappearing_elements&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; 构建一个可以传入 Promise 数组的 promise
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForLocator &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Locator&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span&gt;Promise&amp;lt;Locator&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitFor&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;returnedLocator &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Promise&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;race&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; Promise&#x2F;定位器数组
&lt;&#x2F;span&gt;&lt;span&gt;    [
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForLocator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Gallery&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})),
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;waitForLocator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Portfolio&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;})),
&lt;&#x2F;span&gt;&lt;span&gt;    ]
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(await returnedLocator.innerText());
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;returnedLocator&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F; console.log(page.url());
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;gallery&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;portfolio&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;经过这次尝试后,我开始思考是否有更简单直接的方法来处理这种条件,于是我开始用 if 语句构思解决方案。几分钟后,我就有了一个不错的可行方案,更容易理解。在下面的代码中,我们检查 locator.isVisible(),如果为 true 则继续,如果为 false 则转到下一个代码块。我倾向于使用这种解决方案,因为它能完成任务,而且更易于理解。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;在两个元素中点击可见的那个&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;the-internet.herokuapp.com&#x2F;disappearing_elements&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;gallery &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Gallery&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;portfolio &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Portfolio&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;gallery&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isVisible&lt;&#x2F;span&gt;&lt;span&gt;()) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;gallery&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;portfolio&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isVisible&lt;&#x2F;span&gt;&lt;span&gt;()) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;portfolio&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toHaveURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;gallery&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;portfolio&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;2024 年来自乙醇的注释 👀：我更倾向于第 1 种方案，第 1 种方案更有 javascript 的味道一点，而且支持超过 2 个的 locator。然而，在 2024 年的今天，上面两种方案都不推荐。比较好的实现是 playwright 自带的&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;locators#matching-one-of-the-two-alternative-locators&quot;&gt;locator.or&lt;&#x2F;a&gt;方案，代码如下&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newEmail &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;New&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dialog &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByText&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Confirm security settings&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newEmail&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;or&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dialog&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;first&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBeVisible&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;dialog&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;isVisible&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;getByRole&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, { name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Dismiss&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;newEmail&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;感谢阅读!如果你觉得这篇文章有帮助,可以在 LinkedIn 上联系我,或考虑给我买杯咖啡。如果你想获得更多内容直接发送到你的收件箱,请在下方订阅。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源：https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-in-playwright-to-conditionally-click-an-element-depending-on-which-one-is-present&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间：2022 年 12 月 26 日 13:30:38&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在GitHub Actions中缓存 Playwright 浏览器二进制文件</title>
        <published>2022-11-16T00:00:00+00:00</published>
        <updated>2022-11-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/playwright-github-action-to-cache-the-browser-binaries/"/>
        <id>https://playwright.itest.info/blog/playwright-github-action-to-cache-the-browser-binaries/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/playwright-github-action-to-cache-the-browser-binaries/">&lt;p&gt;我最近于 2022 年 11 月 16 日更新了这篇文章!&lt;&#x2F;p&gt;
&lt;p&gt;如果你发现自己每天多次在 GitHub Actions 中运行 Playwright 测试,你可以通过缓存下载的二进制文件来节省时间和成本。&lt;&#x2F;p&gt;
&lt;p&gt;要了解 Playwright 如何安装和管理浏览器,最好的方法是查看&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;browsers#managing-browser-binaries&quot;&gt;官方文档&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;文档中提到,每种浏览器的默认安装位置如下:&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Playwright 将 Chromium、WebKit 和 Firefox 浏览器下载到特定操作系统的缓存文件夹中:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;%USERPROFILE%\AppData\Local\ms-playwright&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;MacOS: &lt;code&gt;~&#x2F;Library&#x2F;Caches&#x2F;ms-playwright&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Linux: &lt;code&gt;~&#x2F;.cache&#x2F;ms-playwright&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;下面的 GitHub Actions YAML 文件包含了一个创建缓存并存储 Playwright 二进制文件的部分,这样 GitHub Actions 就不必每次运行时都重新下载。这里的关键是确保 &#x27;key&#x27; 部分(目前设置为 &lt;code&gt;Linux-playwright-1.27.1&lt;&#x2F;code&gt;)在每次更新 Playwright 版本时都要更新。这将使缓存失效和自动下载最新的 Playwright 版本,并在新的 key 下缓存它。&lt;&#x2F;p&gt;
&lt;p&gt;例如,如果发布了 Playwright 1.28.0 版本,当我更新 package.json 文件以获取 1.28.0 版本时,缓存 key 将自动更新为 &lt;code&gt;Linux-playwright-1.28.0&lt;&#x2F;code&gt;,这将下载最新版本的 Playwright 并在该 key 值下缓存它。&lt;&#x2F;p&gt;
&lt;p&gt;另外需要注意的是,以下代码行很重要,它用于检查缓存是否仍然有效,或者是否应该重建:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;steps.playwright-cache.outputs.cache-hit != &amp;#39;true&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright Tests
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;master&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;pull_request&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;master&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;timeout-minutes&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;60
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;checkout@v3
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;setup-node@v2
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;node-version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;16.x&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Get installed Playwright version
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-version
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;echo &amp;quot;PLAYWRIGHT_VERSION=$(node -e &amp;quot;console.log(require(&amp;#39;.&#x2F;package-lock.json&amp;#39;).dependencies[&amp;#39;@playwright&#x2F;test&amp;#39;].version)&amp;quot;)&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Cache playwright binaries
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;cache@v3
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-cache
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;            ~&#x2F;.cache&#x2F;ms-playwright
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npm ci
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright install --with-deps
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;steps.playwright-cache.outputs.cache-hit != &amp;#39;true&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright install-deps
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;steps.playwright-cache.outputs.cache-hit != &amp;#39;true&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Run Playwright tests
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;npx playwright test
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;upload-artifact@v2
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;always()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;playwright-test-results
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;test-results&#x2F;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;以下是 GitHub Actions 实际运行时的一些截图。示例仓库可以在&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;new-playwright-init&#x2F;actions&quot;&gt;这里&lt;&#x2F;a&gt;找到。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2022&#x2F;11&#x2F;image-6.png&quot; alt=&quot;图片1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆GitHub Actions 运行创建缓存的示例&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2022&#x2F;11&#x2F;image-7.png&quot; alt=&quot;图片2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;GitHub Actions 运行使用缓存的示例 👆&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2022&#x2F;11&#x2F;image-8.png&quot; alt=&quot;图片3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;GitHub Actions 页面中可见的缓存 ⬆️&lt;&#x2F;p&gt;
&lt;p&gt;根据我的测试,这可以为你的 Playwright 测试运行节省不少时间(至少 3 分钟)。不过我确实遇到过有几天 Playwright 安装过程耗时超过 8 分钟的情况。正是这个问题促使我深入研究这个方案。&lt;&#x2F;p&gt;
&lt;p&gt;如果你想出了任何创新的缓存使用方法,或者觉得这篇文章对你有帮助,欢迎在 Twitter 上联系我 &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;ButchMayhew&quot;&gt;@butchmayhew&lt;&#x2F;a&gt;,或者考虑给我买杯咖啡。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;playwright-github-action-to-cache-the-browser-binaries&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2022-11-16T09:30:00.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何使用 GitHub Action 触发另一个代码库中的 Playwright 测试?</title>
        <published>2022-11-06T00:00:00+00:00</published>
        <updated>2022-11-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-do-i-trigger-playwright-tests-to-run-across-repositories-using-github-actions/"/>
        <id>https://playwright.itest.info/blog/how-do-i-trigger-playwright-tests-to-run-across-repositories-using-github-actions/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-do-i-trigger-playwright-tests-to-run-across-repositories-using-github-actions/">&lt;p&gt;如果你发现 Playwright 测试自动化代码与被测服务或应用程序位于不同的代码库中,别担心!本文将介绍如何通过 GitHub Actions 跨代码库触发测试运行。&lt;&#x2F;p&gt;
&lt;p&gt;虽然有多种方法可以实现这一目标,但最简单的方式是利用 GitHub Actions,特别是 &lt;code&gt;peter-evans&#x2F;repository-dispatch@v2&lt;&#x2F;code&gt; &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;peter-evans&#x2F;repository-dispatch&quot;&gt;action&lt;&#x2F;a&gt;。这个 GitHub Action 可以让你轻松使用 &lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;actions&#x2F;using-workflows&#x2F;events-that-trigger-workflows#repository_dispatch&quot;&gt;Repository Dispatch API&lt;&#x2F;a&gt; 来触发其他代码库中的 GitHub Actions。&lt;&#x2F;p&gt;
&lt;p&gt;唯一的前提条件是你需要创建一个具有 &lt;code&gt;repos&lt;&#x2F;code&gt; 权限的个人访问 token(PAT)。&lt;&#x2F;p&gt;
&lt;p&gt;创建个人访问 token 的步骤可以在&lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;authentication&#x2F;keeping-your-account-and-data-secure&#x2F;creating-a-personal-access-token&quot;&gt;这里&lt;&#x2F;a&gt;找到。需要允许的权限范围如下图所示。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2022&#x2F;11&#x2F;image.png&quot; alt=&quot;图片 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;保存后,你需要将 PAT 保存为应用程序代码库中的 GitHub Action Secret。在设置页面左侧,展开 Secrets 并查看 Actions 页面&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2022&#x2F;11&#x2F;image-2.png&quot; alt=&quot;图片 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆 代码库 &amp;gt; 设置 &amp;gt; Secrets &amp;gt; Actions&lt;&#x2F;p&gt;
&lt;p&gt;添加名为 &lt;code&gt;REPO_ACCESS_TOKEN&lt;&#x2F;code&gt; 的 PAT&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2022&#x2F;11&#x2F;image-3.png&quot; alt=&quot;图片 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;👆 将 PAT 添加到 Action secrets&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bian-xie-github-action-yml-wen-jian&quot;&gt;编写 GitHub Action yml 文件&lt;&#x2F;h2&gt;
&lt;p&gt;完成上述步骤后,你只需要编写 .yml 文件来启动 Repository Dispatch,并在测试代码库中设置 &lt;code&gt;on&lt;&#x2F;code&gt; repository_dispatch 来运行测试。以下是服务&#x2F;应用程序代码库(你想要从中启动测试运行的地方)和接收代码库(运行测试的地方)的 GitHub Action yml 文件示例。&lt;&#x2F;p&gt;
&lt;p&gt;第一部分包括服务或应用程序的 yml 示例,以及一个可以查看实际运行情况的 GitHub 代码库链接。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;my-service 代码库中的 build-deploy.yml&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;My Service Build and Deploy
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;branches&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;develop
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;deploy_staging&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Staging Deploy
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;记录 Staging 发布
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;echo &amp;quot;这次运行是 staging 部署&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;pull代码
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;actions&#x2F;checkout@v2
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;设置要传递给测试的 URL
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;ENVIRONMENT=http:&#x2F;&#x2F;my-site.com&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;构建和部署
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;构建和部署&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;触发 Playwright 测试作业
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;uses&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;peter-evans&#x2F;repository-dispatch@v2
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ secrets.REPO_ACCESS_TOKEN }}
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;repository&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;bmayhew&#x2F;my-tests
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;event-type&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;run-my-tests
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;client-payload&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;{&amp;quot;github&amp;quot;: ${{ toJson(github) }}}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;my-service&#x2F;actions&#x2F;runs&#x2F;3397953921&#x2F;jobs&#x2F;5650587912&quot;&gt;GitHub Action 运行示例&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;my-service&quot;&gt;GitHub my-site 代码库&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;关于这行 &lt;code&gt;client-payload: &#x27;{&quot;github&quot;: ${{ toJson(github) }}}&#x27;&lt;&#x2F;code&gt; 的快速说明: 这允许你传递当前 GitHub 运行的所有详细信息(状态、环境变量、你想要设置或从作业的前面步骤传递到测试作业的任何内容)。我使用这个的方式包括发送预览环境 URL、GitHub 评论 ID、设置我正在进行的反馈&#x2F;测试类型(快乐路径或完整运行)等。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;这部分是 Playwright 测试代码库中应包含内容的示例。需要特别注意的行包括何时运行 GitHub Action &lt;code&gt;on&lt;&#x2F;code&gt; 以及创建与服务&#x2F;应用程序 GitHub Action 中发送的 &lt;code&gt;event-type&lt;&#x2F;code&gt; 名称相匹配的类型。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;repository_dispatch&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;types&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;run-my-tests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;em&gt;my-tests 代码库中的 run-tests.yml&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Playwright 自动化测试
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;workflow_dispatch&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;inputs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;要运行测试的 URL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;required&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;my-site.com
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;repository_dispatch&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;types&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;run-my-tests&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;run-my-tests-2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;jobs&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run_automation_tests&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;timeout-minutes&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;60
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;runs-on&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;ubuntu-latest
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;env&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;ENVIRONMENT&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;${{ github.event.inputs.environment }}${{ github.event.client_payload.github.event.inputs.environment }}
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;steps&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;运行 Playwright 测试
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;环境: $ENVIRONMENT&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;GitHub 事件是: ${{ github.event_name }}&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;在这里运行测试&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;          echo &amp;quot;Action 中的以下步骤提供了你可以在脚本中使用的内容&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;输出 GitHub 上下文
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;github_context_step
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 以下内容会打印出所有可用于任何报告或从其他代码库运行测试的信息
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;echo &amp;#39;${{ toJSON(github) }}&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;my-tests&#x2F;actions&#x2F;runs&#x2F;3397955085&#x2F;jobs&#x2F;5650589659&quot;&gt;GitHub Action 运行示例&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BMayhew&#x2F;my-tests&quot;&gt;GitHub my-test 代码库&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;要访问 &lt;code&gt;client_payload&lt;&#x2F;code&gt; 中发送的任何详细信息,你必须调用:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;${{ github.event.client_payload.github.你想要的信息 }}&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果你不确定想要什么,运行命令:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;echo &#x27;${{ toJSON(github) }}&#x27;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这将在 GitHub Action 中输出一个 JSON 对象,包含你在 GitHub Action 中可以访问的所有不同键值对。&lt;&#x2F;p&gt;
&lt;p&gt;如果你想更进一步,可以继续使用 Repository Dispatch 功能向原始服务&#x2F;应用程序代码库回传状态,根据测试是否通过执行不同的自动化操作。例如,你可以为特定的拉取请求添加标签,或创建一个操作来向拉取请求添加评论,说明测试的状态(你需要从 client_payload 中保留原始拉取请求 ID)。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;关于跨代码库使用这种功能的可能性是无限的。希望你觉得这篇文章有帮助!如果你想出了使用 Repository Dispatch 的任何创意方法,或者觉得这篇文章有用,欢迎在 Twitter 上联系我 &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;ButchMayhew&quot;&gt;@butchmayhew&lt;&#x2F;a&gt;,或者考虑给我买杯咖啡。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-do-i-trigger-playwright-tests-to-run-across-repositories-using-github-actions&#x2F;&quot;&gt;URL 来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2022-11-06T04:32:48.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>是否可以使用 Playwright 进行无障碍测试？</title>
        <published>2022-10-16T00:00:00+00:00</published>
        <updated>2022-10-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/is-it-possible-to-use-playwright-to-do-accessibility-testing/"/>
        <id>https://playwright.itest.info/blog/is-it-possible-to-use-playwright-to-do-accessibility-testing/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/is-it-possible-to-use-playwright-to-do-accessibility-testing/">&lt;p&gt;Markdown 内容：&lt;&#x2F;p&gt;
&lt;p&gt;当然可以！Deque 实验室提供了一个名为 axe 的工具来协助这项工作。对于 Playwright，他们专门提供了 axe-core&#x2F;playwright 包，使用起来比以往更加简单。&lt;&#x2F;p&gt;
&lt;p&gt;以下代码片段展示了如何使用 axe-core&#x2F;playwright 包中的 &lt;code&gt;AxeBuilder&lt;&#x2F;code&gt; 功能。调用 &lt;code&gt;analyze()&lt;&#x2F;code&gt; 将返回一个 promise，可以通过等待获取扫描的所有详细信息。我还列举了几种处理结果的方法：在控制台打印结果、将违规情况附加到使用 Playwright 测试运行器生成的测试报告中，以及使用 expect 语句在不符合规范时使测试失败。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;AxeBuilder &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@axe-core&#x2F;playwright&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Visit home page and run an axe test @axe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;testInfo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;broken-workshop.dequelabs.com&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;使用 axe 分析页面
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessibilityScanResults &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await new &lt;&#x2F;span&gt;&lt;span&gt;AxeBuilder({ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;analyze&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;将违规情况附加到测试报告
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;testInfo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;attach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;accessibility-scan-results&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    body: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessibilityScanResults&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;violations&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    contentType: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;application&#x2F;json&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;在控制台输出违规情况
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;violation &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessibilityScanResults&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;violations&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;violation&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;forEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;entry&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;entry&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;impact &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot; &amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;entry&lt;&#x2F;span&gt;&lt;span&gt;.description);
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;期望违规列表为空
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessibilityScanResults&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;violations&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toEqual&lt;&#x2F;span&gt;&lt;span&gt;([]);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;上面的示例虽然简单，但查看 Playwright 文档，你会发现更多功能，例如：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;配置 axe 扫描页面的特定部分&lt;&#x2F;li&gt;
&lt;li&gt;从扫描中排除单个元素&lt;&#x2F;li&gt;
&lt;li&gt;禁用单个扫描规则（完整的 Axe 规则列表请参见此处）&lt;&#x2F;li&gt;
&lt;li&gt;使用快照允许特定已知问题&lt;&#x2F;li&gt;
&lt;li&gt;为 AxeBuilder 创建 fixture 以简化代码&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;你还可以根据想要运行的测试类型，使用特定标签调用 AxeBuilder 进行检查。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;使用 axe 分析页面
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;accessibilityScanResults &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await new &lt;&#x2F;span&gt;&lt;span&gt;AxeBuilder({ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;withTags&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;wcag2a&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;wcag2aa&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;wcag21a&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;wcag21aa&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;analyze&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Axe 是一个在多种自动化解决方案中使用的工具，如果你想尝试它的 Chrome 扩展版本，可以在这里安装。&lt;&#x2F;p&gt;
&lt;p&gt;我越是使用和探索 Playwright，就越被它的功能所折服。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;accessibility-testing&quot;&gt;无障碍测试&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;网址来源：https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;is-it-possible-to-use-playwright-to-do-accessibility-testing&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间：2022-10-16&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在 Playwright 测试中根据条件不同使用 afterEach</title>
        <published>2022-07-06T00:00:00+00:00</published>
        <updated>2022-07-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-to-conditionally-use-aftereach/"/>
        <id>https://playwright.itest.info/blog/how-to-conditionally-use-aftereach/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-to-conditionally-use-aftereach/">&lt;p&gt;在某些情况下，你可能希望在测试中使用 afterEach 来清理数据，但并非所有情况下都需要清理数据。我的具体例子是我在测试一个 POST 接口时，我们有正面和负面的测试。当我成功创建一个 POST 时，我希望在 afterEach 中删除刚刚创建的内容，但当我进行负面测试时，POST 并没有创建新的项目，我不需要运行 afterEach 来清理数据，因为它会因为试图删除不存在的 id 而失败。&lt;&#x2F;p&gt;
&lt;p&gt;解决方案是使 afterEach 有条件的被调用。请看下面的代码示例，我们创建了一个新变量&lt;code&gt;triggerAfterEach&lt;&#x2F;code&gt;，并设置为&lt;code&gt;true&lt;&#x2F;code&gt;来触发 afterEach。如果在测试中没有分配&lt;code&gt;triggerAfterEach&lt;&#x2F;code&gt;，afterEach 代码将不会执行。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;测试组&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;triggerAfterEach&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;boolean&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;afterEach&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;triggerAfterEach&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;&#x2F;&#x2F;在这里执行after代码
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;有afterEach的测试场景&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;triggerAfterEach &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;没有afterEach的测试场景&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;triggerAfterEach &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff8942;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-to-conditionally-use-aftereach&#x2F;&quot;&gt;URL Source&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2022-07-06&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>playwright如何屏蔽页面上的各种弹出广告</title>
        <published>2022-04-16T00:00:00+00:00</published>
        <updated>2022-04-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/network-abort-request-playwright-tutorial-part-59/"/>
        <id>https://playwright.itest.info/blog/network-abort-request-playwright-tutorial-part-59/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/network-abort-request-playwright-tutorial-part-59/">&lt;p&gt;最近，我的一个被测网站添加了谷歌广告，这导致我之前编写的所有测试都失败了。一个从底部弹出的广告窗口覆盖在了自动化程序要点击的输入框上。&lt;&#x2F;p&gt;
&lt;p&gt;为了解决这个问题，我转向使用&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-route&quot;&gt;route 方法&lt;&#x2F;a&gt;。这个方法允许拦截网络请求。&lt;code&gt;route&lt;&#x2F;code&gt;对象允许以下操作：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;abort - 中止路由的请求&lt;&#x2F;li&gt;
&lt;li&gt;continue - 继续路由的请求，可选择重写部分内容（如重写头部）&lt;&#x2F;li&gt;
&lt;li&gt;fulfill - 改写响应内容（如发送不同的状态码、内容类型或正文）&lt;&#x2F;li&gt;
&lt;li&gt;request - 允许你以多种方式与&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-request&quot;&gt;request 对象&lt;&#x2F;a&gt;交互。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;对于这个特定的例子，我想编写代码来查找所有 URL 以&lt;code&gt;https:&#x2F;&#x2F;googleads.*&lt;&#x2F;code&gt;开头的请求并拦截它们，同时继续处理其余的请求。&lt;&#x2F;p&gt;
&lt;p&gt;以下代码块使用当前会话&#x2F;测试的上下文，创建一个路由拦截器来监视所有请求。代码的第 2 行获取请求 URL 作为字符串，并使用&lt;code&gt;startsWith()&lt;&#x2F;code&gt;函数返回一个布尔值。如果为 true，则执行第 3 行（中止），否则执行第 4 行（继续）。然后返回响应。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;**&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;startsWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;googleads.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;? &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;abort&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;continue&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果你想获取页面上发出的所有的异步请求列表，这段代码也很有用，把结果打印到控制台就可以了。这里的一个重要部分是要对请求做些处理，在下面的例子中我选择继续执行。如果你不使用&lt;code&gt;abort&lt;&#x2F;code&gt;、&lt;code&gt;continue&lt;&#x2F;code&gt;或&lt;code&gt;fulfill&lt;&#x2F;code&gt;，请求很可能会超时。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;**&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;continue&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;尽情使用吧！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;network-abort-request-playwright-tutorial-part-59&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2022 年 4 月 16 日 05:04:36&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在Playwright中一次运行多个标签的测试用例?</title>
        <published>2022-03-14T00:00:00+00:00</published>
        <updated>2022-03-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/how-can-i/"/>
        <id>https://playwright.itest.info/blog/how-can-i/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/how-can-i/">&lt;p&gt;要在 Playwright Test 中运行带有多个标签的测试，可以使用 &lt;code&gt;--grep&lt;&#x2F;code&gt; 选项。下面是具体的方法和命令：&lt;&#x2F;p&gt;
&lt;p&gt;假设你有三个标签：&lt;code&gt;@tag1&lt;&#x2F;code&gt;、&lt;code&gt;@tag2&lt;&#x2F;code&gt; 和 &lt;code&gt;@tag3&lt;&#x2F;code&gt;，你希望将 &lt;code&gt;@tag1&lt;&#x2F;code&gt; 和 &lt;code&gt;@tag2&lt;&#x2F;code&gt; 的测试分组在一起运行。你可以使用以下命令来实现：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 运行带有 @tag1 的测试
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; npx playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --grep&lt;&#x2F;span&gt;&lt;span&gt; @tag1
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 运行带有 @tag2 的测试
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; npx playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --grep&lt;&#x2F;span&gt;&lt;span&gt; @tag2
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 运行带有 @tag1 和 @tag2 的测试
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; npx playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --grep&lt;&#x2F;span&gt;&lt;span&gt; @tag1&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; --grep&lt;&#x2F;span&gt;&lt;span&gt; @tag2
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6d6d6d;&quot;&gt;# 简化命令，运行带有 @tag1 或 @tag2 的测试
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; npx playwright test&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt; -g&lt;&#x2F;span&gt;&lt;span&gt; tag1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;tag2
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在运行命令时，&lt;code&gt;-g tag1|tag2&lt;&#x2F;code&gt; 会运行所有带有 &lt;code&gt;@tag1&lt;&#x2F;code&gt; 或 &lt;code&gt;@tag2&lt;&#x2F;code&gt; 的测试。使用这个方法，你可以方便地按照标签来组织和运行测试。&lt;&#x2F;p&gt;
&lt;p&gt;如果你觉得这个解决方案有帮助，请给予支持！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL Source: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;how-can-i&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Published Time: 2022-03-14&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>在 Playwright Test 中有简便的方法重复执行测试多次来检查用例的稳定性吗？</title>
        <published>2022-03-14T00:00:00+00:00</published>
        <updated>2022-03-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/in-playwright-test-is-there-an-easy-way-to-loop-the-test-multiple-times-to-check-for-flakiness/"/>
        <id>https://playwright.itest.info/blog/in-playwright-test-is-there-an-easy-way-to-loop-the-test-multiple-times-to-check-for-flakiness/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/in-playwright-test-is-there-an-easy-way-to-loop-the-test-multiple-times-to-check-for-flakiness/">&lt;p&gt;在 Playwright Test 中有简便的方法重复执行测试多次来检查用例到底稳定不稳定性吗？&lt;&#x2F;p&gt;
&lt;p&gt;我发现在合并新代码到测试集的主干分支之前，在 pull request 上多次运行新的自动化测试或对现有测试的进行修改是一个很好的做法，这样可以检查测试的不稳定性。要实现这一点，你可以构建 CI 流水线来运行命令 &lt;code&gt;npx playwright test --repeat-each=5&lt;&#x2F;code&gt;。关于命令行运行的文档可以在&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-cli&quot;&gt;这里&lt;&#x2F;a&gt;找到。&lt;&#x2F;p&gt;
&lt;p&gt;你还可以更进一步，通过 git 比较查看哪些目录发生了变化，并将目录作为参数传递给命令。下面这个命令将会运行 todo-page 和 landing-page 目录下的所有测试，每个测试重复 5 次：&lt;code&gt;npx playwright test tests&#x2F;todo-page&#x2F; tests&#x2F;landing-page&#x2F; --repeat-each=5&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果这个解决方案对你有帮助，欢迎点赞支持！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;URL 来源：https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;in-playwright-test-is-there-an-easy-way-to-loop-the-test-multiple-times-to-check-for-flakiness&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间：2022 年 3 月 14 日 21:12:45&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>如何在playwright中定位表格的行(table row)</title>
        <published>2022-03-13T00:00:00+00:00</published>
        <updated>2022-03-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/access-a-table-row-with-a-unique-id-in-playwright/"/>
        <id>https://playwright.itest.info/blog/access-a-table-row-with-a-unique-id-in-playwright/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/access-a-table-row-with-a-unique-id-in-playwright/">&lt;p&gt;有许多不同的方法可以与表格进行交互，这是我们在 Playwright 测试中提取&#x2F;确认数据是否存在的众多示例中的第一个。在这个示例中，我们将使用 https:&#x2F;&#x2F;letcode.in&#x2F;table 上的表格。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2022&#x2F;03&#x2F;image.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在这个挑战中，我想获取包含 Cupcake 值的整行。Playwright 有一个非常好的定位器函数，它允许我们指定一个高级元素 tr 并找到包含文本 Cupcake 的表格行。这将返回表格行的定位器，以便进行断言或以其他方式与整行进行交互。&lt;&#x2F;p&gt;
&lt;p&gt;TODO: 下面的代码应该已经失效了&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;test &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;从单元格中的文本获取表格的整行&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;page &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;https:&#x2F;&#x2F;letcode.in&#x2F;table&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;locator&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;tr:has-text(&amp;quot;Cupcake&amp;quot;)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果你 &lt;code&gt;console.log(row.innerText())&lt;&#x2F;code&gt;，你会得到如下返回值。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;Cupcake
&lt;&#x2F;span&gt;&lt;span&gt;305
&lt;&#x2F;span&gt;&lt;span&gt;4
&lt;&#x2F;span&gt;&lt;span&gt;67
&lt;&#x2F;span&gt;&lt;span&gt;4
&lt;&#x2F;span&gt;&lt;span&gt;50
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;access-a-table-row-with-a-unique-id-in-playwright&#x2F;&quot;&gt;网址来源&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2022-03-13&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>playwright里如何快速确定该使用多少个worker运行用例</title>
        <published>2022-03-13T00:00:00+00:00</published>
        <updated>2022-03-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/whats-an-easy-way-to-tell-how-many-workers/"/>
        <id>https://playwright.itest.info/blog/whats-an-easy-way-to-tell-how-many-workers/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/whats-an-easy-way-to-tell-how-many-workers/">&lt;p&gt;好消息是，默认情况下，Playwright 根据您的计算机规格尝试并行运行测试。更好的消息是，通过增加或减少工作线程数，您可以在运行 Playwright 测试时获得更快的反馈。&lt;&#x2F;p&gt;
&lt;p&gt;您可以通过阅读&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;test-parallel&quot;&gt;并行和分片&lt;&#x2F;a&gt;的文档来了解更多信息，但如果您想快速简便地查看哪种工作线程数量对您的计算机或云中的计算机最优，只需运行以下命令即可。&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#191919;color:#f8f8f2;&quot;&gt;&lt;code&gt;&lt;span&gt;for i in `seq &amp;lt;min&amp;gt; &amp;lt;max&amp;gt;`; do time npx playwright test --reporter=line --workers=$i; done
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;将 &lt;code&gt;&amp;lt;min&amp;gt;&lt;&#x2F;code&gt; 修改为您的 CPU 数量除以 2，将 &lt;code&gt;&amp;lt;max&amp;gt;&lt;&#x2F;code&gt; 修改为 CPU 数量乘以 2。这个命令将使用不同的工作线程数启动 Playwright 测试套件（通过最小和最大值进行迭代）。输出将通过命令行返回。请注意，根据您的最小和最大输入以及测试数量，此过程可能需要一些时间。因此，请启动并冲泡一些 ☕️ 或 🫖。运行完成后，您应该会得到类似以下的输出。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;content&#x2F;images&#x2F;2022&#x2F;03&#x2F;image-2.png&quot; alt=&quot;Image 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;3、5 和 6 次运行的测试都是最短的运行时间。关键是找到最快的运行方式，而不会引入任何不稳定的测试。我的默认值是 &lt;code&gt;4&lt;&#x2F;code&gt;，看了结果后，我可能会继续使用这个值。&lt;&#x2F;p&gt;
&lt;p&gt;当尝试在 AWS EC2 实例或云中的其他服务器上运行 Playwright 测试时，我可以看到这个脚本非常实用。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;来源链接&lt;&#x2F;strong&gt;: &lt;a href=&quot;https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;whats-an-easy-way-to-tell-how-many-workers&#x2F;&quot;&gt;What&#x27;s an easy way to tell how many workers?&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;发布时间&lt;&#x2F;strong&gt;: 2022-03-13&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>使用 Playwright API 的 POST 请求上传 Content-Type 为 multipart&#x2F;form-data 的文件</title>
        <published>2022-03-12T00:00:00+00:00</published>
        <updated>2022-03-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            乙醇
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/blog/making-a-post/"/>
        <id>https://playwright.itest.info/blog/making-a-post/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/blog/making-a-post/">&lt;p&gt;在为我的 API 编写自动化脚本时，我遇到了一个问题：如何使用 Playwright API 发起 POST 请求来上传文件。最后，我在一个 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.slack.com&#x2F;archives&#x2F;CSUHZPVLM&#x2F;p1638175756183800?thread_ts=1638175752.183700&amp;amp;cid=CSUHZPVLM&quot;&gt;Slack 对话里&lt;&#x2F;a&gt; 找到了答案，于是决定在这里分享。&lt;&#x2F;p&gt;
&lt;p&gt;我尝试进行的 API 调用是向文件接口发送 POST 请求以上传文件，下面的例子中是一个 .png 文件。接口要求请求类型必须为 &lt;code&gt;multipart&#x2F;form-data&lt;&#x2F;code&gt;。首先，我查看了 &lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-apirequestcontext#api-request-context-post&quot;&gt;Playwright 文档&lt;&#x2F;a&gt; 中关于 apiRequestContext.post() 的部分，发现可以传递的一个选项是 &lt;code&gt;multipart&lt;&#x2F;code&gt;（而不是通常用于发布 JSON 数据的 data）。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;multipart&lt;&#x2F;code&gt; &amp;lt;&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Reference&#x2F;Global_Objects&#x2F;Object&quot;&gt;Object&lt;&#x2F;a&gt;&amp;lt;&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Data_structures#String_type&quot;&gt;string&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Data_structures#String_type&quot;&gt;string&lt;&#x2F;a&gt;|&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Data_structures#Number_type&quot;&gt;number&lt;&#x2F;a&gt;|&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Data_structures#Boolean_type&quot;&gt;boolean&lt;&#x2F;a&gt;|[ReadStream]|&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Reference&#x2F;Global_Objects&#x2F;Object&quot;&gt;Object&lt;&#x2F;a&gt;&amp;gt;&amp;gt; 提供一个对象，该对象将使用 &lt;code&gt;multipart&#x2F;form-data&lt;&#x2F;code&gt; 编码作为 HTML 表单发送，并作为请求体。如果指定了此参数，则 &lt;code&gt;content-type&lt;&#x2F;code&gt; 头将被设置为 &lt;code&gt;multipart&#x2F;form-data&lt;&#x2F;code&gt;，除非显式提供。文件值可以传递为 &lt;a href=&quot;https:&#x2F;&#x2F;nodejs.org&#x2F;api&#x2F;fs.html#fs_class_fs_readstream&quot;&gt;&lt;code&gt;fs.ReadStream&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; 或包含文件名、mime 类型及其内容的类文件对象。&lt;a href=&quot;https:&#x2F;&#x2F;playwright.dev&#x2F;docs&#x2F;api&#x2F;class-apirequestcontext#api-request-context-post-option-multipart&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;name&lt;&#x2F;code&gt; &amp;lt;&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Data_structures#String_type&quot;&gt;string&lt;&#x2F;a&gt;&amp;gt; 文件名&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;mimeType&lt;&#x2F;code&gt; &amp;lt;&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Data_structures#String_type&quot;&gt;string&lt;&#x2F;a&gt;&amp;gt; 文件类型&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;buffer&lt;&#x2F;code&gt; &amp;lt;&lt;a href=&quot;https:&#x2F;&#x2F;nodejs.org&#x2F;api&#x2F;buffer.html#buffer_class_buffer&quot;&gt;Buffer&lt;&#x2F;a&gt;&amp;gt; 文件内容&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;在查看文档和 Slack 对话后，我意识到还需要进一步了解如何创建一个 &lt;code&gt;buffer&lt;&#x2F;code&gt; 并将其传递到 POST 请求中。之后，我成功地构建了一个运行正常的 Playwright 测试，如下例所示。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;typescript&quot; style=&quot;background-color:#191919;color:#f8f8f2;&quot; class=&quot;language-typescript &quot;&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;@playwright&#x2F;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;path &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;POST: Upload a file&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;async &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fc9354;&quot;&gt;baseURL &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;file &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;lib&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;logo.png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;image &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;fs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;readFileSync&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;baseURL &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;&#x2F;files&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {
&lt;&#x2F;span&gt;&lt;span&gt;    headers: {
&lt;&#x2F;span&gt;&lt;span&gt;      Accept: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;*&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      ContentType: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;multipart&#x2F;form-data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    multipart: {
&lt;&#x2F;span&gt;&lt;span&gt;      file: {
&lt;&#x2F;span&gt;&lt;span&gt;        name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        mimeType: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;image&#x2F;png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        buffer: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      },
&lt;&#x2F;span&gt;&lt;span&gt;      title: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Logo of Business&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  });
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#fbdfb5;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ecfdb9;&quot;&gt;JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6699cc;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff5e5e;&quot;&gt;await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fdb082;&quot;&gt;201&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.title).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;Logo of Business&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#e9fdac;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span&gt;.type).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8cdaff;&quot;&gt;toBe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fbe3bf;&quot;&gt;png&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffffff;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;更多关于 Http POST 方法不同请求类型的详细信息可以在 &lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Methods&#x2F;POST&quot;&gt;MDN Web Docs&lt;&#x2F;a&gt; 上找到。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lai-yuan&quot;&gt;来源&lt;&#x2F;h2&gt;
&lt;p&gt;来源网址: https:&#x2F;&#x2F;playwrightsolutions.com&#x2F;making-a-post&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;发布时间: 2022-03-12T06:12:26.000Z&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Terms</title>
        <published>2020-01-08T00:00:00+00:00</published>
        <updated>2020-01-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            Thomas Weitzel
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/pages/terms/"/>
        <id>https://playwright.itest.info/pages/terms/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/pages/terms/">&lt;p&gt;&lt;em&gt;These terms have been last updated on January 8, 2020&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;All content on this web site is for informational purposes only.&lt;&#x2F;p&gt;
&lt;p&gt;As the owner of this web site, I ...&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;make no representations as to the accuracy or completeness of any information on this site or by following any link.&lt;&#x2F;li&gt;
&lt;li&gt;will not be liable for any errors or omissions in this information, nor for the availability of this information.&lt;&#x2F;li&gt;
&lt;li&gt;will not be liable for any losses, injuries, or damages from the display or use of this information.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These terms are subject to change at any time and without notice.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="zh-CN">
        <title>Privacy Policy</title>
        <published>2019-03-11T00:00:00+00:00</published>
        <updated>2019-03-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            Thomas Weitzel
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://playwright.itest.info/pages/privacy/"/>
        <id>https://playwright.itest.info/pages/privacy/</id>
        
        <content type="html" xml:base="https://playwright.itest.info/pages/privacy/">&lt;h3 id=&quot;data-protection&quot;&gt;Data protection&lt;&#x2F;h3&gt;
&lt;p&gt;The use of my website is possible without providing personal data.
If personal data can be entered (for example: name, address, or email addresses), this is on a voluntary basis.
These data will not be disclosed to third parties without your explicit consent.&lt;&#x2F;p&gt;
&lt;p&gt;Data transmission in the Internet (e.g.
in the communication by email) can have security gaps.
A complete protection of the data from access by third parties is not possible.
The use of contact data published in the imprint by third parties for sending me unsolicited advertising and information materials is hereby prohibited.
I reserve the right to take legal action in the event of unsolicited promotional information, such as spam emails.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hosting&quot;&gt;Hosting&lt;&#x2F;h3&gt;
&lt;p&gt;I host my website at netcup. The provider is netcup GmbH, Daimlerstr. 25, 76185 Karlsruhe, referred to as &quot;netcup&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Details on netcup&#x27;s privacy policy can be found on their homepage under &lt;a href=&quot;https:&#x2F;&#x2F;www.netcup.eu&#x2F;kontakt&#x2F;datenschutzerklaerung.php&quot;&gt;privacy policy&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The use of netcup is based on Article 6(1)(f) GDPR.
I have a legitimate interest that my website is available to visitors as reliably as possible.
If consent was requested, data processing takes place exclusively based on Article 6(1)(a) GDPR.
Consent can be revoked at any time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;data-processing&quot;&gt;Data processing&lt;&#x2F;h3&gt;
&lt;p&gt;I have signed a data processing contract with netcup.
This contract is required by privacy law and guarantees, that data of visitors of my website will be processed in compliance with the GDPR.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;responsible-controller&quot;&gt;Responsible controller&lt;&#x2F;h3&gt;
&lt;p&gt;The person responsible for data processing on this website is the person named in the [imprint]({{&amp;lt; relref &quot;imprint&quot; &amp;gt;}}).
The &quot;responsible controller&quot; means the natural or legal person who decides on the processing of data (for example: name, address, or email addresses).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;information-cancellation-deletion&quot;&gt;Information, cancellation, deletion&lt;&#x2F;h3&gt;
&lt;p&gt;At any time you have the right to free information about your stored personal data, its origins, recipients, and the purpose of the data processing, as well as a right to correction or deletion of this data.
You can contact me at any time via the address given in the imprint for further questions on the subject of your personal data.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;log-files-on-the-server&quot;&gt;Log files on the server&lt;&#x2F;h3&gt;
&lt;p&gt;The web server stores information in its log files, some of which your browser transmits to me.
These are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;the IP address of your computer&lt;&#x2F;li&gt;
&lt;li&gt;the time of the request&lt;&#x2F;li&gt;
&lt;li&gt;the name of the requested page&lt;&#x2F;li&gt;
&lt;li&gt;a status code (if the request was successful or not)&lt;&#x2F;li&gt;
&lt;li&gt;the referrer URL&lt;&#x2F;li&gt;
&lt;li&gt;the browser type, browser version and other information sent by your browser&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This data can not be used to identify a specific person.
I will not merge this data with other data sources.
I reserve the right to check this data retrospectively, if I become aware of illegal use of my website.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ssl-encryption&quot;&gt;SSL encryption&lt;&#x2F;h3&gt;
&lt;p&gt;This site uses SSL encryption for security reasons and to protect the transmission of sensitive content, such as the requests you send to me.
An encrypted connection can be recognized by the fact that the address in the address bar of the browser starts with &lt;code&gt;https:&#x2F;&#x2F;&lt;&#x2F;code&gt; and a lock symbol nearby.
If SSL encryption is enabled, the data you submit to me can not be read by third parties.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cookies&quot;&gt;Cookies&lt;&#x2F;h3&gt;
&lt;p&gt;This site uses functional cookies only on pages where math formulas are displayed with the help of the &lt;a href=&quot;https:&#x2F;&#x2F;www.mathjax.org&#x2F;&quot;&gt;MathJax&lt;&#x2F;a&gt; library.
These cookies are only used in the context of MathJax and the display of math formulas.
The readability of the website represents a legitimate interest within the meaning of Article 6(1)(f) GDPR.
I do not use any other cookies on this website.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;embedded-content-from-other-websites&quot;&gt;Embedded content from other websites&lt;&#x2F;h3&gt;
&lt;p&gt;Posts on this site may contain embedded content (e.g. videos, pictures, comments, etc.).
Embedded content from other websites behaves just as if the visitor had visited the other website.&lt;&#x2F;p&gt;
&lt;p&gt;These websites may collect information about you, use cookies, embed additional third-party tracking services, and record your interaction with this embedded content, if you have an account and are logged in to this other site.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-rights-do-you-have-to-your-data&quot;&gt;What rights do you have to your data?&lt;&#x2F;h3&gt;
&lt;p&gt;You may request that I export your personal information, including any data you have provided to me.
In addition, you may request the deletion of any personal information I have stored about you.
This does not include the data that I need to keep for administrative, legal, or security-related purposes.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
