<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Luc Gagan</title>
    <description>The latest articles on DEV Community by Luc Gagan (@lucgagan).</description>
    <link>https://dev.to/lucgagan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1088206%2F4aa02800-c7af-45b2-a6bb-5fc92907d754.jpg</url>
      <title>DEV Community: Luc Gagan</title>
      <link>https://dev.to/lucgagan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lucgagan"/>
    <language>en</language>
    <item>
      <title>Introducing Auto Playwright: Transforming Playwright Tests with AI</title>
      <dc:creator>Luc Gagan</dc:creator>
      <pubDate>Sat, 11 Nov 2023 14:48:49 +0000</pubDate>
      <link>https://dev.to/lucgagan/introducing-auto-playwright-transforming-playwright-tests-with-ai-e0p</link>
      <guid>https://dev.to/lucgagan/introducing-auto-playwright-transforming-playwright-tests-with-ai-e0p</guid>
      <description>&lt;h1&gt;
  
  
  Introducing Auto Playwright: Transforming Playwright Tests with AI
&lt;/h1&gt;

&lt;p&gt;In the dynamic field of software development, efficient and effective testing solutions are crucial. I'm excited to introduce a project that's close to my heart - &lt;a href="https://github.com/lucgagan/auto-playwright"&gt;Auto Playwright&lt;/a&gt;. This open-source tool integrates AI with Playwright to revolutionize web testing, offering a seamless, intuitive way to automate testing tasks using plain-text AI-driven prompts.&lt;/p&gt;

&lt;p&gt;The original article: &lt;a href="https://ray.run/blog/auto-playwright"&gt;https://ray.run/blog/auto-playwright&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Streamlining Your Testing Workflow with Auto Playwright
&lt;/h2&gt;

&lt;p&gt;Auto Playwright is here to redefine how we approach Playwright tests. It's straightforward to get started:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Installation&lt;/strong&gt;: Simply install the &lt;code&gt;auto-playwright&lt;/code&gt; dependency via npm:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;auto-playwright &lt;span class="nt"&gt;-D&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Configuration&lt;/strong&gt;: Set up your OpenAI API key to leverage the AI capabilities:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'your_key_here'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Usage&lt;/strong&gt;: Integrate &lt;code&gt;auto&lt;/code&gt; function in your tests easily:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;auto&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto-playwright&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;End-to-end test example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;executes query, action and assertion&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// `auto` can query data&lt;/span&gt;
  &lt;span class="c1"&gt;// In this case, the result is plain-text contents of the header&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get the header text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// `auto` can perform actions&lt;/span&gt;
  &lt;span class="c1"&gt;// In this case, auto will find and fill in the search text input&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`type "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;headerText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" in the search box`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// `auto` can assert the state of the website&lt;/span&gt;
  &lt;span class="c1"&gt;// In this case, the result is a boolean outcome&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchInputHasHeaderText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`is the contents of the search box equal to "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;headerText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"?`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchInputHasHeaderText&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Practical Use Cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Simplifying Complex Tasks
&lt;/h3&gt;

&lt;p&gt;Auto Playwright can handle a variety of tasks, from simple queries to complex navigation and data retrieval. For example, automating a user registration process becomes a breeze with instructions like "go to registration page" and "fill in name, email, and password".&lt;/p&gt;

&lt;h3&gt;
  
  
  Efficient Testing
&lt;/h3&gt;

&lt;p&gt;Auto Playwright excels in making test development faster and more efficient. Though not a complete replacement for traditional testing methods due to AI processing times and API costs, it shines in detailed, less frequent testing scenarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tech Behind Auto Playwright
&lt;/h2&gt;

&lt;p&gt;At its core, Auto Playwright uses OpenAI's technology to interpret plain text instructions into executable test commands. This makes the process more intuitive and accessible, even for those with limited coding expertise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Choose Auto Playwright?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ease of Use&lt;/strong&gt;: Rapid creation of tests using plain text prompts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wide Compatibility&lt;/strong&gt;: Supports all browsers compatible with Playwright.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost-Effective&lt;/strong&gt;: Free to use, with minimal costs associated with OpenAI usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Join the Community
&lt;/h2&gt;

&lt;p&gt;I encourage you to try Auto Playwright in your projects and join our community to shape the future of AI-powered test automation. Your feedback and contributions are invaluable.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/lucgagan/auto-playwright"&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ray.run/discord"&gt;Join Our Discord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ray.run/newsletter"&gt;Subscribe to Our Newsletter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Auto Playwright isn't just a tool; it's a leap forward in test automation. It makes testing more accessible and aligned with the dynamic nature of web development. As the digital landscape evolves, tools like Auto Playwright will shape the future of testing and development.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Embrace the future of test automation - simplified, efficient, and AI-powered.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>testing</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>The Power of Conversion: Docker Run to Docker Compose and Vice-Versa</title>
      <dc:creator>Luc Gagan</dc:creator>
      <pubDate>Thu, 09 Nov 2023 10:09:09 +0000</pubDate>
      <link>https://dev.to/lucgagan/the-power-of-conversion-docker-run-to-docker-compose-and-vice-versa-4a58</link>
      <guid>https://dev.to/lucgagan/the-power-of-conversion-docker-run-to-docker-compose-and-vice-versa-4a58</guid>
      <description>&lt;p&gt;Title: Streamline Your Docker Workflow: Introducing Rayrun's Docker Run and Compose Conversion Tool&lt;/p&gt;




&lt;p&gt;As the world of software development continuously evolves, so does the need for more efficient and simplified workflows. Docker, a cornerstone in this evolving landscape, has revolutionized the way applications are built, shipped, and run. However, navigating between Docker commands and Docker Compose manifests can often be a cumbersome task. This is where Rayrun's latest tool makes a significant impact.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Power of Conversion: Docker Run to Docker Compose and Vice-Versa
&lt;/h3&gt;

&lt;p&gt;Rayrun has unveiled a groundbreaking tool on its website, &lt;a href="https://ray.run"&gt;Ray.run&lt;/a&gt;, designed to seamlessly convert &lt;code&gt;docker run&lt;/code&gt; commands to Docker Compose manifests and the other way around. &lt;/p&gt;

&lt;h4&gt;
  
  
  What is Docker Run and Docker Compose?
&lt;/h4&gt;

&lt;p&gt;Before diving into the specifics of the tool, let's understand the basics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker Run&lt;/strong&gt;: This command is used to run a Docker container. It offers a plethora of options to configure the container at startup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker Compose&lt;/strong&gt;: An essential tool for defining and running multi-container Docker applications. It uses YAML files to configure application services.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The Conversion Tool: Simplifying the Process
&lt;/h4&gt;

&lt;p&gt;The tool's functionality is straightforward yet powerful. A typical &lt;code&gt;docker run&lt;/code&gt; command like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; redis &lt;span class="nt"&gt;-p&lt;/span&gt; 6379:6379 redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is effortlessly transformed into a Docker Compose manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;6379:6379'&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users can access this tool at &lt;a href="https://ray.run/tools/docker-run-to-docker-compose"&gt;Docker Run to Docker Compose&lt;/a&gt; and &lt;a href="https://ray.run/tools/docker-compose-to-docker-run"&gt;Docker Compose to Docker Run&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Leveraging Open-Source: Composerize and Decomposerize
&lt;/h4&gt;

&lt;p&gt;The foundation of this tool is built on the robust open-source packages, &lt;a href="https://github.com/composerize"&gt;Composerize&lt;/a&gt; and &lt;a href="https://github.com/decomposerize"&gt;Decomposerize&lt;/a&gt;. These packages ensure reliable and accurate conversion, making the tool not just a utility but a necessity for developers working with Docker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Tool Matters: Enhancing Workflow Efficiency
&lt;/h3&gt;

&lt;p&gt;The significance of this tool lies in its ability to streamline workflow processes. It's particularly beneficial for developers who frequently switch between single-container and multi-container setups. The tool ensures that this transition is smooth, error-free, and efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Use-Case in Playwright End-to-End Testing
&lt;/h3&gt;

&lt;p&gt;A compelling use-case for this tool is in the context of Playwright for end-to-end (e2e) testing. Playwright, a framework for web testing, benefits significantly from a Dockerized environment, ensuring consistency across testing platforms. Here's how Rayrun's tool enhances this process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplified Configuration&lt;/strong&gt;: Quickly convert Playwright's Docker run commands to Compose files, making it easier to manage and scale test environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Efficient Testing Pipelines&lt;/strong&gt;: Integrate seamlessly into CI/CD pipelines, allowing for more robust and reliable testing scenarios.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexibility and Scalability&lt;/strong&gt;: Effortlessly switch between single-container tests and complex, multi-container setups depending on testing needs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion: A Step Towards Simplified Docker Management
&lt;/h3&gt;

&lt;p&gt;In conclusion, Rayrun's Docker conversion tool is more than just a utility – it's a step towards more efficient and manageable Docker workflows. By bridging the gap between Docker run commands and Compose manifests, it not only saves time but also enhances the overall development and testing process, especially in sophisticated frameworks like Playwright.&lt;/p&gt;

&lt;p&gt;Experience the ease and efficiency of Docker management with Rayrun's tool – a must-have in every developer's toolkit.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>playwright</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What would you like to see in a Playwright cheat sheet?</title>
      <dc:creator>Luc Gagan</dc:creator>
      <pubDate>Tue, 17 Oct 2023 03:45:02 +0000</pubDate>
      <link>https://dev.to/lucgagan/what-would-you-like-to-see-in-a-playwright-cheat-sheet-3jm</link>
      <guid>https://dev.to/lucgagan/what-would-you-like-to-see-in-a-playwright-cheat-sheet-3jm</guid>
      <description>&lt;p&gt;&lt;a href="https://ray.run/blog/mastering-playwright-test-automation-your-comprehensive-cheat-sheet"&gt;https://ray.run/blog/mastering-playwright-test-automation-your-comprehensive-cheat-sheet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is currently my #1 post by popularity. I am trying to level it up to keep the high ranking position. What do you typically seek when you search for a Playwright cheat sheet?&lt;/p&gt;

</description>
      <category>playwright</category>
    </item>
    <item>
      <title>Efficient E2E Testing for Next.js: A Playwright Tutorial</title>
      <dc:creator>Luc Gagan</dc:creator>
      <pubDate>Sun, 15 Oct 2023 00:10:15 +0000</pubDate>
      <link>https://dev.to/lucgagan/efficient-e2e-testing-for-nextjs-a-playwright-tutorial-1mmh</link>
      <guid>https://dev.to/lucgagan/efficient-e2e-testing-for-nextjs-a-playwright-tutorial-1mmh</guid>
      <description>&lt;p&gt;Supercharge your Next.js app testing with Playwright – a tool for automating Chromium, Firefox, and WebKit browsers. Whether you're gearing up for End-to-End (E2E) or Integration tests, Playwright offers a seamless experience across all platforms. In this guide, I will walk you through setting up and running your first Playwright E2E test for a Next.js application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quickstart
&lt;/h2&gt;

&lt;p&gt;If you're eager to dive right in, the quickest way to kick things off is by leveraging the &lt;code&gt;create-next-app&lt;/code&gt; with the &lt;code&gt;with-playwright&lt;/code&gt; example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;latest&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;playwright&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;playwright&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run &lt;code&gt;npm run test:e2e&lt;/code&gt; to run the tests. Continue reading to learn how to setup Playwright in an existing Next.js project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manual Setup
&lt;/h2&gt;

&lt;p&gt;For those of you with an existing NPM project and would prefer a manual setup, no worries, you're covered! Begin by installing the &lt;code&gt;@playwright/test&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;playwright&lt;/span&gt;&lt;span class="sr"&gt;/tes&lt;/span&gt;&lt;span class="err"&gt;t
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, update your &lt;code&gt;package.json&lt;/code&gt; scripts to incorporate Playwright:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next start"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test:e2e"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"playwright test"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Crafting Your First Playwright E2E Test
&lt;/h2&gt;

&lt;p&gt;Imagine you have two Next.js pages set up as shown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/index.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/nav&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// pages/about.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;About&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's time to ensure your navigation operates flawlessly. Here's how to craft a test for it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// e2e/example.spec.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;navigates to the about page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/About/&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toHaveURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;toContainText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;About Page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Use &lt;code&gt;page.goto('/')&lt;/code&gt; and have &lt;code&gt;"baseURL": "http://ray.run"&lt;/code&gt; set in the &lt;code&gt;playwright.config.ts&lt;/code&gt; file for concise code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Running Playwright Tests
&lt;/h2&gt;

&lt;p&gt;Remember, Playwright tests the actual Next.js app, so the server should be up and running. It is also a good idea to test against production build to mimic real-world behavior.&lt;/p&gt;

&lt;p&gt;Start your Next.js server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;
&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in another terminal, run your Playwright tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;e2e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use &lt;code&gt;webServer&lt;/code&gt; to Start the Development Server
&lt;/h2&gt;

&lt;p&gt;With Playwright's &lt;a href="https://playwright.dev/docs/test-webserver"&gt;&lt;code&gt;webServer&lt;/code&gt; feature&lt;/a&gt;, you can let it handle starting the development server and ensuring it's ready for action.&lt;/p&gt;

&lt;p&gt;Just add this configration to your &lt;code&gt;playwright.config.ts&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PlaywrightTestConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PlaywrightTestConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;webServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;npm run dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reuseExistingServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run your tests without having to start the development server manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run &lt;span class="nb"&gt;test&lt;/span&gt;:e2e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Playwright on Continuous Integration (CI)
&lt;/h2&gt;

&lt;p&gt;When running on CI, Playwright defaults to headless mode. To have all dependencies in place, execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;playwright&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the Experimental test mode for Playwright
&lt;/h2&gt;

&lt;p&gt;There is not a lot of documentation on this yet, but Vercel team is working on an &lt;a href="https://github.com/vercel/next.js/blob/canary/packages/next/src/experimental/testmode/playwright/README.md"&gt;experimental test mode for Playwright&lt;/a&gt;. This mode will allow you to intercept requests and mock responses. This is useful for testing API routes and other server-side code.&lt;/p&gt;

&lt;p&gt;Suppose you have an API route at &lt;code&gt;/api/hello&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/api/hello.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextApiRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextApiResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ResponseData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextApiRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ResponseData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This route is called using &lt;code&gt;getServerSideProps&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/index.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GetServerSideProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;testid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GetServerSideProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000/api/hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you want to write a test that verifies that the page renders the name returned by the API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prints name retrieved from the API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;msw&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello Foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The latter asserts that the page renders the name returned by the API. However, using the experimental test proxy, you can intercept the request to &lt;code&gt;/api/hello&lt;/code&gt; and mock the response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mocking API responses
&lt;/h2&gt;

&lt;p&gt;First, you need to install &lt;a href="https://www.npmjs.com/package/msw"&gt;&lt;code&gt;msw&lt;/code&gt;&lt;/a&gt; to mock responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; msw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you need to change your import to &lt;code&gt;next/experimental/testmode/playwright/msw&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/experimental/testmode/playwright/msw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can mock the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/experimental/testmode/playwright/msw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prints name retrieved from the API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;msw&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;msw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000/api/hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello Bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  App Router
&lt;/h2&gt;

&lt;p&gt;You can also use the experimental test mode to test the Next.js app/ router.&lt;/p&gt;

&lt;p&gt;Define a route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/api/hello/route.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fetch the data in your page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/page.tsx&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000/api/hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;testid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can test the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/experimental/testmode/playwright/msw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prints name retrieved from the API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;msw&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;msw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000/api/hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello Bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Error: No test info
&lt;/h2&gt;

&lt;p&gt;It appears that when you are running tests in the experimental test mode, you may get the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: No test info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This happens when you try to access the web server directly, outside of the test. As far as I can tell, there is no way to access the Next.js web server directly when running in the experimental test mode. You can only access it by running a Playwright test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In closing, integrating Playwright into your Next.js workflow ensures a robust, tested, and reliable application. If this is your first time using Playwright, you should familiarize with &lt;a href="https://playwright.dev/docs/trace-viewer"&gt;Trace viewer&lt;/a&gt; next as it will help you debug your tests. Now, go ahead and craft tests that leave no stone unturned. Happy testing!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>playwright</category>
    </item>
    <item>
      <title>DevTools</title>
      <dc:creator>Luc Gagan</dc:creator>
      <pubDate>Thu, 12 Oct 2023 02:49:14 +0000</pubDate>
      <link>https://dev.to/lucgagan/devtools-19oi</link>
      <guid>https://dev.to/lucgagan/devtools-19oi</guid>
      <description>&lt;p&gt;I developed an obsession! Here, I said it.&lt;/p&gt;

&lt;p&gt;For the last 3 months, I've been spending every afternoon building DevTools. It started with utilities that I use for testing. Then I needed something simple to migrate from Cypress to Playwright. Then I was learning Regex, etc. In total it now features 85 tools.&lt;/p&gt;

&lt;p&gt;All tools are ad free and free to use. No catch. I just wanted to build something that others would use.&lt;/p&gt;

&lt;p&gt;Here is the current list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/ascii-to-hex"&gt;ASCII to Hex Converter&lt;/a&gt; – An ASCII to Hex tool converts ASCII (American Standard Code for Information Interchange) text, which represents characters, into hexadecimal format, which is a base-16 number system commonly used in computing and digital systems.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/base64-decoder"&gt;Base64 Decoder&lt;/a&gt; – A Base64 text decoder tool converts Base64 text into its original form, allowing for the decoding of binary data that has been encoded using Base64.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/base64-encoder"&gt;Base64 Encoder&lt;/a&gt; – A Base64 text encoder tool converts text into Base64, a group of binary-to-text encoding schemes that represent binary data in an ASCII string format by translating it into a radix-64 representation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/base64url-decoder"&gt;Base64URL Decoder&lt;/a&gt; – A Base64URL text decoder tool converts Base64URL text into its original form, allowing for the decoding of binary data that has been encoded using Base64URL.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/base64url-encoder"&gt;Base64URL Encoder&lt;/a&gt; – A Base64URL text encoder tool converts text into Base64URL, a variant of Base64 that uses URL and filename safe alphabet, allowing for the encoding of binary data into a URL-friendly format.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/bash-command-formatter"&gt;Bash Command Formatter&lt;/a&gt; – A Bash command formatter tool takes in a Bash command and formats it, applying proper indentation, line breaks, and formatting to make it more readable and structured.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/byte-pair-decoder"&gt;Byte Pair (BPE) Decoder&lt;/a&gt; – A byte pair decoder tool decodes text that has been encoded using byte pair encoding (BPE), a data compression technique that replaces the most frequently occurring character pairs with a single, unused character, reducing the size of the data. It is used by a lot of Transformer models, including GPT, GPT-2, RoBERTa, BART, and DeBERTa.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/byte-pair-encoder"&gt;Byte Pair (BPE) Encoder&lt;/a&gt; – A byte pair encoder tool encodes text using byte pair encoding (BPE), a data compression technique that replaces the most frequently occurring character pairs with a single, unused character, reducing the size of the data. It is used by a lot of Transformer models, including GPT, GPT-2, RoBERTa, BART, and DeBERTa.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/color-converter"&gt;Color Converter&lt;/a&gt; – A color converter tool converts colors between different color spaces, such as RGB (Red, Green, Blue), CMYK (Cyan, Magenta, Yellow, Black), and HEX (Hexadecimal).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/cron-generator"&gt;Cron Expression Generator&lt;/a&gt; – A cron expression generator tool takes in custom instructions and generates a cron expression, which is a string that represents a set of times, allowing for the scheduling of tasks to be run at specific times.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/cron-parser"&gt;Cron Expression Parser&lt;/a&gt; – A cron expression parser tool parses a cron expression into a human-readable description, allowing for easy understanding and analysis of the cron expression.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/css-formatter"&gt;CSS Formatter&lt;/a&gt; – Takes a CSS string and applies proper indentation, line breaks, and formatting to make it more readable and structured.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/csv-to-json"&gt;CSV to JSON Converter&lt;/a&gt; – A CSV to JSON tool converts CSV (Comma-Separated Values) formatted data into JSON (JavaScript Object Notation) format, allowing for interoperability and compatibility between the two data representations.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/csv-to-xml"&gt;CSV to XML Converter&lt;/a&gt; – A CSV to XML tool converts CSV (Comma-Separated Values) formatted data into XML (Extensible Markup Language) format, allowing for interoperability and compatibility between the two data representations.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/csv-to-yaml"&gt;CSV to YAML Converter&lt;/a&gt; – A CSV to YAML tool converts CSV (Comma-Separated Values) formatted data into YAML (YAML Ain't Markup Language) format, allowing for interoperability and compatibility between the two data representations.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/curl-builder"&gt;cURL Command Builder&lt;/a&gt; – A cURL builder tool takes in custom YAML instructions and generates a cURL (Client URL) command, which is a command-line tool that allows for the transfer of data using various protocols, such as HTTP, HTTPS, FTP, FTPS, and SFTP.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/curl-to-fetch"&gt;cURL to Fetch Converter&lt;/a&gt; – A cURL to Fetch tool converts cURL (Client URL) commands into Fetch commands, allowing for easy migration from cURL to Fetch.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/cypress-to-playwright"&gt;Cypress to Playwright Converter&lt;/a&gt; – A Cypress to Playwright tool converts Cypress code into Playwright code, allowing for easy migration from Cypress to Playwright.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/date-parser"&gt;Date Parser&lt;/a&gt; – A natural language date parser tool parses a date string into various canonical expressions of date, allowing for easy manipulation and analysis of the date.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/file-size-converter"&gt;File Size Converter&lt;/a&gt; – A file size converter tool converts file sizes between different units, such as bytes, kilobytes, megabytes, gigabytes, and terabytes, allowing for easy transformation of file sizes to suit different use cases.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/glob-to-regex"&gt;Glob to Regex Converter&lt;/a&gt; – A glob to regex tool converts a glob pattern into a regular expression, allowing for easy transformation of glob patterns into regular expressions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/graphql-formatter"&gt;GraphQL Formatter&lt;/a&gt; – Takes a GraphQL string and applies proper indentation, line breaks, and formatting to make it more readable and structured.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/hash-generator"&gt;Hash Generator&lt;/a&gt; – A hash generator tool takes in a string of characters and outputs a hash value, which is a fixed-length alphanumeric string that uniquely identifies the input data.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/hash-identifier"&gt;Hash Identifier&lt;/a&gt; – A hash identifier tool takes in a hash value and identifies the type of hash, such as MD5, SHA-1, or SHA-256.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/hex-to-ascii"&gt;Hex to ASCII Converter&lt;/a&gt; – A Hex to ASCII tool converts hexadecimal (a base-16 number system) into ASCII (American Standard Code for Information Interchange) text, allowing the representation of hexadecimal numbers as readable characters.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/html-entity-decoder"&gt;HTML Entity Decoder&lt;/a&gt; – HTML entity decoder tool converts HTML entities back into their corresponding characters, ensuring the proper display and interpretation of special characters and symbols in HTML code.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/html-entity-encoder"&gt;HTML Entity Encoder&lt;/a&gt; – HTML entity encoder tool converts special characters and symbols in HTML code into their corresponding HTML entities, ensuring proper rendering and preventing parsing issues.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/html-formatter"&gt;HTML Formatter&lt;/a&gt; – Takes an HTML string and applies proper indentation, line breaks, and formatting to make it more readable and structured.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/html-to-markdown"&gt;HTML to Markdown Converter&lt;/a&gt; – An HTML to Markdown tool converts HTML (Hypertext Markup Language) formatted text into Markdown, a lightweight markup language that uses plain text formatting syntax to convert to HTML.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/idn-decoder"&gt;IDN Decoder&lt;/a&gt; – An IDN decoder tool converts IDN (Internationalized Domain Name) text into its Unicode representation, allowing for the decoding of Punycode into Unicode characters.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/idn-encoder"&gt;IDN Encoder&lt;/a&gt; – An IDN encoder tool converts IDN (Internationalized Domain Name) text into its Punycode representation , allowing for the encoding of Unicode characters into ASCII characters.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/javascript-formatter"&gt;JavaScript Formatter&lt;/a&gt; – Takes a JavaScript string and applies proper indentation, line breaks, and formatting to make it more readable and structured.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/json-diff-analyzer"&gt;JSON Diff Analyzer&lt;/a&gt; – A JSON diff analyzer tool compares two JSON (JavaScript Object Notation) objects and highlights the differences between them, allowing for easy comparison and analysis of the two objects.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/json-formatter"&gt;JSON Formatter&lt;/a&gt; – Takes a JSON string and applies proper indentation, line breaks, and formatting to make it more readable and structured.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/json-minifier"&gt;JSON Minifier&lt;/a&gt; – Reduces the size of a JSON file by removing unnecessary white spaces, line breaks, and comments, optimizing it for faster transmission and improved efficiency.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/json-schema-to-typescript"&gt;JSON Schema to TypeScript Converter&lt;/a&gt; – A JSON Schema to TypeScript tool converts JSON Schema (JavaScript Object Notation Schema) into TypeScript, a typed superset of JavaScript that compiles to plain JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/json-to-csv"&gt;JSON to CSV Converter&lt;/a&gt; – A JSON to CSV tool converts JSON (JavaScript Object Notation) formatted data into CSV (Comma-Separated Values) format, allowing for interoperability and compatibility between the two data representations.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/json-to-json-schema"&gt;JSON to JSON Schema Converter&lt;/a&gt; – A JSON to JSON Schema tool converts JSON (JavaScript Object Notation) data into a JSON schema, which is a vocabulary that allows you to annotate and validate JSON documents.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/json-to-toml"&gt;JSON to TOML Converter&lt;/a&gt; – A JSON to TOML tool converts JSON (JavaScript Object Notation) formatted data into TOML (Tom's Obvious, Minimal Language) format, enabling easy transformation and representation of JSON data in a more human-readable and structured TOML syntax.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/json-to-typescript"&gt;JSON to TypeScript Converter&lt;/a&gt; – A JSON to TypeScript tool converts JSON (JavaScript Object Notation) into TypeScript, a typed superset of JavaScript that compiles to plain JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/json-to-xml"&gt;JSON to XML Converter&lt;/a&gt; – A JSON to XML tool converts JSON (JavaScript Object Notation) formatted data into XML (Extensible Markup Language) format, enabling easy transformation and representation of JSON data in a more human-readable and structured XML syntax.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/json-to-yaml"&gt;JSON to YAML Converter&lt;/a&gt; – A JSON to YAML tool converts JSON (JavaScript Object Notation) formatted data into YAML (YAML Ain't Markup Language) format, enabling easy transformation and representation of JSON data in a more human-readable and structured YAML syntax.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/json-viewer"&gt;JSON Viewer&lt;/a&gt; – A JSON viewer tool takes in a JSON (JavaScript Object Notation) string and displays it in a tree-like structure, allowing for easy visualization and navigation of the data.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/line-deduplicator"&gt;Line Deduplicator&lt;/a&gt; – A line deduplicator tool processes a list of lines and removes any duplicates, leaving only unique lines, which can help in data cleaning and information management.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/line-sorter"&gt;Line Sorter&lt;/a&gt; – A line sorter tool takes in a list of lines as input and rearranges them into a specified order, such as alphabetical or numerical, aiding in the organization and analysis of data.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/markdown-previewer"&gt;Markdown Previewer&lt;/a&gt; – A Markdown previewer tool takes in a Markdown string and displays how it would render, allowing for easy visualization and navigation of the data.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/markdown-to-html"&gt;Markdown to HTML Converter&lt;/a&gt; – A Markdown to HTML tool converts Markdown formatted text into HTML (Hypertext Markup Language), a markup language that uses tags to structure and format web pages.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/playwright-to-cypress"&gt;Playwright to Cypress Converter&lt;/a&gt; – A Playwright to Cypress tool converts Playwright code into Cypress code, allowing for easy migration from Playwright to Cypress.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/query-string-parser"&gt;Query String Parser&lt;/a&gt; – A query string parser tool parses a query string into a JavaScript object, allowing for easy manipulation and analysis of the query string.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/random-credit-card-generator"&gt;Random Credit Card Generator&lt;/a&gt; – A random credit card generator tool generates a random credit card number, expiration date, and CVV, which can be used for testing and data generation purposes.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/random-ip-address-generator"&gt;Random IP Address Generator&lt;/a&gt; – A random IP address generator tool generates a random IP address, which can be used for testing and data generation purposes.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/random-mac-address-generator"&gt;Random MAC Address Generator&lt;/a&gt; – A random MAC address generator tool generates a random MAC address, which can be used for testing and data generation purposes.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/random-mailing-address-generator"&gt;Random Mailing Address Generator&lt;/a&gt; – A random address generator tool generates a random address, which can be used for testing and data generation purposes.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/random-person-generator"&gt;Random Person Generator&lt;/a&gt; – A random person generator tool generates a random person's name, age, email, address, interests, and other information, which can be used for testing and data generation purposes.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/regex-optimizer"&gt;Regular Expression Optimizer&lt;/a&gt; – A regular expression optimizer tool optimizes a regular expression by removing unnecessary characters and expressions, allowing for the creation of more efficient regular expressions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/regex-tester"&gt;Regular Expression Tester&lt;/a&gt; – A regular expression tester tool takes in a regular expression and tests it against a string, allowing for easy testing and debugging of regular expressions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/saml-decoder"&gt;SAML Decoder&lt;/a&gt; – A SAML decoder tool decodes SAML (Security Assertion Markup Language) formatted data, which is an XML-based open standard for exchanging authentication and authorization data between parties, allowing for the interpretation of SAML messages.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/sql-formatter"&gt;SQL Formatter&lt;/a&gt; – Takes a SQL query and applies proper indentation, line breaks, and formatting to make it more readable and structured.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/string-analyzer"&gt;String Analyzer&lt;/a&gt; – A string inspector tool analyzes a string of text and provides information about its length, character count, word count, sentence count, and line count, aiding in the processing and management of text data.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/string-case-converter"&gt;String Case Converter&lt;/a&gt; – A string case converter tool converts text between different cases, such as camelCase, PascalCase, snake_case, and kebab-case, allowing for easy transformation of text to suit different naming conventions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/svg-to-jsx"&gt;SVG to JSX Converter&lt;/a&gt; – An SVG to JSX tool converts SVG (Scalable Vector Graphics) formatted data into JSX (JavaScript XML) format, enabling easy transformation and representation of SVG data in a more human-readable and structured JSX syntax.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/text-diff-analyzer"&gt;Text Diff Analyzer&lt;/a&gt; – A text diff analyzer tool compares two pieces of text and highlights the differences between them, allowing for easy comparison and analysis of the two texts.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/toml-to-json"&gt;TOML to JSON Converter&lt;/a&gt; – A TOML to JSON tool converts TOML (Tom's Obvious, Minimal Language) formatted data into JSON (JavaScript Object Notation) format, enabling easy transformation and representation of TOML data in a more human-readable and structured JSON syntax.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/toml-to-xml"&gt;TOML to XML Converter&lt;/a&gt; – A TOML to XML tool converts TOML (Tom's Obvious, Minimal Language) formatted data into XML (Extensible Markup Language) format, enabling easy transformation and representation of TOML data in a more human-readable and structured XML syntax.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/toml-to-yaml"&gt;TOML to YAML Converter&lt;/a&gt; – A TOML to YAML tool converts TOML (Tom's Obvious, Minimal Language) formatted data into YAML (YAML Ain't Markup Language) format, enabling easy transformation and representation of TOML data in a more human-readable and structured YAML syntax.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/typescript-to-javascript"&gt;TypeScript to JavaScript Converter&lt;/a&gt; – A TypeScript to JavaScript tool converts TypeScript, a typed superset of JavaScript that compiles to plain JavaScript, into JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/unit-converter"&gt;Unit Converter&lt;/a&gt; – A unit converter tool converts units between different systems of measurement, such as converting miles to kilometers or converting pounds to kilograms.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/url-extractor"&gt;URL Extractor&lt;/a&gt; – A URL extractor tool extracts URLs (Uniform Resource Locators) from a string of text, allowing for easy extraction and analysis of URLs.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/url-parser"&gt;URL Parser&lt;/a&gt; – A URL parser tool parses a URL (Uniform Resource Locator) into its component parts, such as the protocol, host, port, path, query parameters, and fragment, allowing for easy manipulation and analysis of the URL.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/uuid-decoder"&gt;UUID Decoder&lt;/a&gt; – A UUID decoder tool converts UUIDs (Universally Unique Identifiers) into their component parts, allowing for easy analysis and manipulation of the UUID.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/uuid-generator"&gt;UUID Generator&lt;/a&gt; – A UUID generator tool generates a universally unique identifier (UUID), a 128-bit number used to identify information in computer systems, ensuring uniqueness across space and time.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/uuid-validator"&gt;UUID Validator&lt;/a&gt; – A UUID validator tool validates a UUID (Universally Unique Identifier), which is a 128-bit number used to uniquely identify information in computer systems.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/xml-formatter"&gt;XML Formatter&lt;/a&gt; – Takes an XML string and applies proper indentation, line breaks, and formatting to make it more readable and structured.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/xml-to-csv"&gt;XML to CSV Converter&lt;/a&gt; – An XML to CSV tool converts XML (Extensible Markup Language) formatted data into CSV (Comma-Separated Values) format, allowing for interoperability and compatibility between the two data representations.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/xml-to-json"&gt;XML to JSON Converter&lt;/a&gt; – An XML to JSON tool converts XML (Extensible Markup Language) formatted data into JSON (JavaScript Object Notation) format, enabling easy transformation and representation of XML data in a more human-readable and structured JSON syntax.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/xml-to-toml"&gt;XML to TOML Converter&lt;/a&gt; – An XML to TOML tool converts XML (Extensible Markup Language) formatted data into TOML (Tom's Obvious, Minimal Language) format, enabling easy transformation and representation of XML data in a more human-readable and structured TOML syntax.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/xml-to-yaml"&gt;XML to YAML Converter&lt;/a&gt; – An XML to YAML tool converts XML (Extensible Markup Language) formatted data into YAML (YAML Ain't Markup Language) format, enabling easy transformation and representation of XML data in a more human-readable and structured YAML syntax.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/yaml-formatter"&gt;YAML Formatter&lt;/a&gt; – Takes a YAML string and applies proper indentation, line breaks, and formatting to make it more readable and structured.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/yaml-to-csv"&gt;YAML to CSV Converter&lt;/a&gt; – A YAML to CSV tool converts YAML (YAML Ain't Markup Language) formatted data into CSV (Comma-Separated Values) format, allowing for interoperability and compatibility between the two data representations.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/yaml-to-json"&gt;YAML to JSON Converter&lt;/a&gt; – A YAML to JSON tool converts YAML (YAML Ain't Markup Language) formatted data into JSON (JavaScript Object Notation) format, allowing for interoperability and compatibility between the two data representations.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/yaml-to-toml"&gt;YAML to TOML Converter&lt;/a&gt; – A YAML to TOML tool converts YAML (YAML Ain't Markup Language) formatted data into TOML (Tom's Obvious, Minimal Language) format, enabling easy transformation and representation of YAML data in a more human-readable and structured TOML syntax.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ray.run/tools/yaml-to-xml"&gt;YAML to XML Converter&lt;/a&gt; – A YAML to XML tool converts YAML (YAML Ain't Markup Language) formatted data into XML (Extensible Markup Language) format, enabling easy transformation and representation of YAML data in a more human-readable and structured XML syntax.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sideprojects</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Mastering Playwright: Best Practices for Web Automation with the Page Object Model</title>
      <dc:creator>Luc Gagan</dc:creator>
      <pubDate>Mon, 02 Oct 2023 23:48:50 +0000</pubDate>
      <link>https://dev.to/lucgagan/mastering-playwright-best-practices-for-web-automation-with-the-page-object-model-4pam</link>
      <guid>https://dev.to/lucgagan/mastering-playwright-best-practices-for-web-automation-with-the-page-object-model-4pam</guid>
      <description>&lt;p&gt;Originally published on &lt;a href="https://ray.run/blog/mastering-poms"&gt;https://ray.run/blog/mastering-poms&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the realm of web automation, integrating Playwright with the Page Object Model (POM) can turbocharge your testing strategy. By following best practices, you can achieve maintainable, reliable, and scalable test scripts. Let's dive deep into how POM elevates Playwright's capabilities for all you QA engineers out there.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Anatomy of a Page Object
&lt;/h2&gt;

&lt;p&gt;A Page Object encapsulates all the interactions and elements of a particular web page (or a segment of it) within a class. This separation brings about three primary constituents:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Element Selectors&lt;/strong&gt;: These are the definitions that point to specific elements on the web page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Methods&lt;/strong&gt;: Functions that encapsulate one or more interactions with the web elements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Properties&lt;/strong&gt;: Any additional information or attributes related to the page, such as its URL.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the heart of POM lies the principle of abstraction. You're not merely scripting tests; you're crafting an intuitive interface to your application's UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Page Object Model Significance
&lt;/h2&gt;

&lt;p&gt;Using POMs ensures an organized approach to test script creation. The core idea is &lt;strong&gt;abstracting&lt;/strong&gt; the UI interactions and elements into easily manageable objects. This abstraction ensures that changes in the UI only necessitate updates in one place, making your test scripts resilient to frequent application updates.&lt;/p&gt;

&lt;p&gt;For example, consider logging into a web application on &lt;code&gt;https://ray.run/&lt;/code&gt;. Instead of writing raw Playwright commands in every test, with POM you'll encapsulate them within a &lt;code&gt;SignInPage&lt;/code&gt; class.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identifying Properties
&lt;/h3&gt;

&lt;p&gt;Imagine you're dealing with a login form on &lt;code&gt;https://ray.run/signin&lt;/code&gt;. First, identify the properties of the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;SignInPage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://ray.run/signin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Identifying Locators
&lt;/h3&gt;

&lt;p&gt;Then, pinpoint the elements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Locator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;SignInPage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://ray.run/signin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;emailInputLocator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Locator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;passwordInputLocator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Locator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;signinButtonLocator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Locator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emailInputLocator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passwordInputLocator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signInButtonLocator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sign In&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Identifying Actions
&lt;/h2&gt;

&lt;p&gt;Then, identify the actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Locator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;SignInPage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://ray.run/signin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;emailInputLocator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Locator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;passwordInputLocator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Locator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;signinButtonLocator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Locator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emailInputLocator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passwordInputLocator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signInButtonLocator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sign In&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emailInputLocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passwordInputLocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signInButtonLocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Identifying Assertions
&lt;/h3&gt;

&lt;p&gt;Then, identify the assertions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Locator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;SignInPage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://ray.run/signin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;emailInputLocator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Locator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;passwordInputLocator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Locator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;signinButtonLocator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Locator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emailInputLocator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passwordInputLocator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signInButtonLocator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sign In&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emailInputLocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passwordInputLocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signInButtonLocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;isSignedIn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Signed In&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure ensures that any UI change only mandates an update within our &lt;code&gt;SignInPage&lt;/code&gt; class, not across multiple test scripts.&lt;/p&gt;

&lt;p&gt;Notice how we've added every abstraction layer one by one. This is similar to how you'd adopt POMs in a real-world scenario. Also, the level of abstraction that you choose is entirely up to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Page Objects in Tests
&lt;/h2&gt;

&lt;p&gt;With the page objects in place, we can now write tests that utilize the encapsulated functionality. Here's an example test using the previously defined page objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SignInPage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./SignInPage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user signs in&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signInPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SignInPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signInPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signInPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo@ray.run&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signInPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSignedIn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isn't this neat? We've abstracted away the low-level details of interacting with the page, such as finding the email and password fields and clicking the submit button. This makes the test code more readable and focused on the high-level behavior of the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Fixtures to Create Page Objects
&lt;/h2&gt;

&lt;p&gt;A big part of the Playwright test framework is the concept of &lt;a href="https://playwright.dev/docs/test-fixtures"&gt;fixtures&lt;/a&gt;. Fixtures are used to set up the test environment and provide access to the browser and page objects. They can also be used to create page objects that can be used across multiple tests.&lt;/p&gt;

&lt;p&gt;Let's see how we can use fixtures to create page objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SignInPage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./SignInPage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extend&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;signInPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SignInPage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;signInPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signInPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SignInPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signInPage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can use the &lt;code&gt;signInPage&lt;/code&gt; fixture in our tests to access the page object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SignInPage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./SignInPage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user signs in&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;signInPage&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signInPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signInPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo@ray.run&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signInPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSignedIn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices for Page Objects
&lt;/h2&gt;

&lt;p&gt;Now that you've got a good understanding of the Page Object Model and how it can be implemented in Playwright, let's take a look at some best practices for creating page objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Separate Page Objects for Each Page
&lt;/h3&gt;

&lt;p&gt;Each page of your application should have its own page object. This ensures that the code remains organized and maintainable, with clear boundaries between different areas of functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Separate Actions and Assertions
&lt;/h3&gt;

&lt;p&gt;It's a good practice to separate the actions and assertions in your page objects. This makes it easier to understand the flow of the test and ensures that the page object is reusable across different tests.&lt;/p&gt;

&lt;p&gt;For example, you might be tempted to write the &lt;code&gt;login&lt;/code&gt; method like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emailInputLocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passwordInputLocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signInButtonLocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Signed In&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, putting the assertion in the &lt;code&gt;login&lt;/code&gt; method makes it less reusable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid Extending Page Objects
&lt;/h3&gt;

&lt;p&gt;It's a good practice to avoid extending page objects. This can lead to a bloated page object with too many methods and properties, making it difficult to maintain and understand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Don't do this&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AuthenticationPage&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;SignInPage&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;AuthenticationPage&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;SignupPage&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;AuthenticationPage&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a general rule, if you find yourself extending a POM class, it's a sign that you need to refactor your code. And if you are finding yourself duplicating code between POMs, then consider if you need to introduce another abstraction layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep Page Objects Small
&lt;/h3&gt;

&lt;p&gt;It's a good practice to keep your page objects small and focused on a single page or a small section of a page. This ensures that the code remains organized and maintainable, with clear boundaries between different areas of functionality.&lt;/p&gt;

&lt;p&gt;In general, avoid adding actions/assertions that are not re-used across multiple tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do Not Use State in Page Objects
&lt;/h3&gt;

&lt;p&gt;It's a good practice to avoid using state in your page objects. This ensures that the page object is reusable across different tests and makes it easier to understand the flow of the test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Don't do this&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;SignInPage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;authenticated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;isSignedIn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Signed In&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authenticated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Principles of Good Page Objects
&lt;/h2&gt;

&lt;p&gt;When implementing the Page Object Model with Playwright, it's essential to adhere to the following principles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Single Responsibility Principle (SRP):&lt;/strong&gt; Each page object should be responsible for a single page or a small section of a page. This ensures that the code remains organized and maintainable, with clear boundaries between different areas of functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abstraction:&lt;/strong&gt; Page objects should abstract away the details of interacting with the page, such as locators and methods for interacting with elements. By providing a more readable and intuitive interface, abstraction reduces the brittleness of the test code and improves its maintainability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encapsulation:&lt;/strong&gt; Page objects should encapsulate the state and behavior of the page, making it easy to reason about the page's current state and the actions that can be performed on it. This helps in maintaining a clear separation of concerns and improves the overall readability of the tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusability:&lt;/strong&gt; Page objects should be designed to be reusable across different tests, reducing code duplication and improving the efficiency of test development. By creating modular and self-contained page objects, you can easily compose tests from reusable building blocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to Understand:&lt;/strong&gt; The naming of methods and variables in page objects should be self-explanatory, making it easy for anyone to understand the purpose and functionality of the code. Clear and descriptive names enhance the readability and maintainability of the tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separation of Concerns:&lt;/strong&gt; The test code should focus on the high-level behavior of the page, while the page objects should handle the low-level details of interacting with the page. This separation allows for better code organization and promotes a more maintainable and scalable test suite.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Advantages of the Page Object Model
&lt;/h2&gt;

&lt;p&gt;POMs offer distinct advantages for you as a QA engineer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt;: Common page interactions are defined once and used across multiple test scripts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Readability&lt;/strong&gt;: Tests become self-explanatory. Colleagues can understand the flow without diving into the UI details.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability&lt;/strong&gt;: UI changes? No problem. Update the respective page object, and you're good to go!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Page Object Model (POM) is a design pattern that abstracts page specific properties, locators, actions, and assertions.&lt;/li&gt;
&lt;li&gt;Implementing POM in Playwright involves creating separate classes for each page, implementing methods for user actions, and using these page objects in your tests.&lt;/li&gt;
&lt;li&gt;Avoid common pitfalls such as extending page objects, mixing actions and assertions, and creating bloated page objects.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://playwright.dev/docs/pom"&gt;Playwright Official POM Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember, the implementation of the Page Object Model in Playwright is just one part of a comprehensive test automation strategy. Read the other articles in &lt;a href="https://ray.run/blog"&gt;Rayrun blog&lt;/a&gt; to continue exploring and learning about other design patterns and best practices to enhance your test automation efforts. Happy testing!&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>tutorial</category>
      <category>learning</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The Ultimate Software Testing Glossary: Your Feedback Needed!</title>
      <dc:creator>Luc Gagan</dc:creator>
      <pubDate>Sat, 23 Sep 2023 16:05:44 +0000</pubDate>
      <link>https://dev.to/lucgagan/the-ultimate-software-testing-glossary-your-feedback-needed-483a</link>
      <guid>https://dev.to/lucgagan/the-ultimate-software-testing-glossary-your-feedback-needed-483a</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Every so often, we stumble upon challenges that push us to create solutions. For me, that challenge was identifying a singular, detailed, and comprehensive reference for software testing terms. While there are numerous resources available, most seemed either incomplete or lacking in depth. Driven by this, I've combined insights from ChatGPT and my personal notes to craft what I believe is the most thorough glossary dedicated to software testing. However, this is just the beginning, and I'm reaching out to the vibrant Dev.to community to make this resource even better!&lt;/p&gt;

&lt;h3&gt;
  
  
  Dive Into The Glossary
&lt;/h3&gt;

&lt;p&gt;Curious about what's inside? Here's the link: &lt;a href="https://ray.run/glossary"&gt;Software Testing Glossary&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How You Can Help
&lt;/h2&gt;

&lt;p&gt;Your expertise is invaluable. To ensure the glossary is as valuable as possible, I've incorporated a feedback mechanism. Every term comes with a "Was this helpful?" form that will gather your insights.&lt;/p&gt;

&lt;p&gt;You might wonder, "Do I need to review all 184 terms?" The answer is no. Simply browse and if something stands out or needs correction, do let me know. Every piece of feedback, whether minute or significant, can lead to a richer and more refined resource.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's On The Horizon?
&lt;/h3&gt;

&lt;p&gt;To give you a glimpse of what I'm planning next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Wikipedia Links:&lt;/strong&gt; Enhancing each term with a relevant Wikipedia link for deeper insights.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Synonyms:&lt;/strong&gt; Including alternative terms to make searches more user-friendly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category Tags:&lt;/strong&gt; Classifying each term under relevant categories (e.g., automated testing, manual testing) for streamlined navigation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive References:&lt;/strong&gt; Anytime these terms are cited in &lt;a href="https://ray.run/"&gt;https://ray.run/&lt;/a&gt; articles, users will have the convenience of hovering over the term to view its definition.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's Co-create
&lt;/h2&gt;

&lt;p&gt;I've laid the foundation, but with your insights and suggestions, we can elevate this glossary to new heights. What features or improvements would make this your go-to reference in times of need? Let's collaborate and create a tool that benefits us all!&lt;/p&gt;

&lt;p&gt;Looking forward to your invaluable feedback and suggestions!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>Bash Command Prettifier: The Solution to Formatting Tedious, Long Commands</title>
      <dc:creator>Luc Gagan</dc:creator>
      <pubDate>Tue, 19 Sep 2023 21:09:23 +0000</pubDate>
      <link>https://dev.to/lucgagan/bash-command-prettifier-the-solution-to-formatting-tedious-long-commands-4m49</link>
      <guid>https://dev.to/lucgagan/bash-command-prettifier-the-solution-to-formatting-tedious-long-commands-4m49</guid>
      <description>&lt;p&gt;Hello fellow DevOps enthusiasts and command-line warriors!&lt;/p&gt;

&lt;p&gt;If you're anything like me, you value clarity and readability in your documentation. But there's a common nuisance we all face: those lengthy, unbroken bash commands. You know the ones I'm talking about. The ones that scroll endlessly, making you lose track of parameters, especially when copying them from tutorials or platforms like GCP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let me paint a picture:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;GCP throws at you:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud beta container &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"rayrun"&lt;/span&gt; node-pools create &lt;span class="s2"&gt;"test-runners-1"&lt;/span&gt; &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="s2"&gt;"rayrun"&lt;/span&gt; &lt;span class="nt"&gt;--zone&lt;/span&gt; &lt;span class="s2"&gt;"us-central1-c"&lt;/span&gt; &lt;span class="nt"&gt;--machine-type&lt;/span&gt; &lt;span class="s2"&gt;"e2-medium"&lt;/span&gt; &lt;span class="nt"&gt;--image-type&lt;/span&gt; &lt;span class="s2"&gt;"COS_CONTAINERD"&lt;/span&gt; &lt;span class="nt"&gt;--disk-type&lt;/span&gt; &lt;span class="s2"&gt;"pd-balanced"&lt;/span&gt; &lt;span class="nt"&gt;--disk-size&lt;/span&gt; &lt;span class="s2"&gt;"100"&lt;/span&gt; &lt;span class="nt"&gt;--metadata&lt;/span&gt; disable-legacy-endpoints&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--scopes&lt;/span&gt; &lt;span class="s2"&gt;"https://www.googleapis.com/auth/devstorage.read_only"&lt;/span&gt;,&lt;span class="s2"&gt;"https://www.googleapis.com/auth/logging.write"&lt;/span&gt;,&lt;span class="s2"&gt;"https://www.googleapis.com/auth/monitoring"&lt;/span&gt;,&lt;span class="s2"&gt;"https://www.googleapis.com/auth/servicecontrol"&lt;/span&gt;,&lt;span class="s2"&gt;"https://www.googleapis.com/auth/service.management.readonly"&lt;/span&gt;,&lt;span class="s2"&gt;"https://www.googleapis.com/auth/trace.append"&lt;/span&gt; &lt;span class="nt"&gt;--num-nodes&lt;/span&gt; &lt;span class="s2"&gt;"3"&lt;/span&gt; &lt;span class="nt"&gt;--enable-autoupgrade&lt;/span&gt; &lt;span class="nt"&gt;--enable-autorepair&lt;/span&gt; &lt;span class="nt"&gt;--max-surge-upgrade&lt;/span&gt; 1 &lt;span class="nt"&gt;--max-unavailable-upgrade&lt;/span&gt; 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;And with a sigh, you reformat it to:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud beta container &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"rayrun"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  node-pools create &lt;span class="s2"&gt;"test-runners-1"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="s2"&gt;"rayrun"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--zone&lt;/span&gt; &lt;span class="s2"&gt;"us-central1-c"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--machine-type&lt;/span&gt; &lt;span class="s2"&gt;"e2-medium"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image-type&lt;/span&gt; &lt;span class="s2"&gt;"COS_CONTAINERD"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--disk-type&lt;/span&gt; &lt;span class="s2"&gt;"pd-balanced"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--disk-size&lt;/span&gt; &lt;span class="s2"&gt;"100"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--metadata&lt;/span&gt; disable-legacy-endpoints&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--scopes&lt;/span&gt; &lt;span class="s2"&gt;"https://www.googleapis.com/auth/devstorage.read_only"&lt;/span&gt;,&lt;span class="s2"&gt;"https://www.googleapis.com/auth/logging.write"&lt;/span&gt;,&lt;span class="s2"&gt;"https://www.googleapis.com/auth/monitoring"&lt;/span&gt;,&lt;span class="s2"&gt;"https://www.googleapis.com/auth/servicecontrol"&lt;/span&gt;,&lt;span class="s2"&gt;"https://www.googleapis.com/auth/service.management.readonly"&lt;/span&gt;,&lt;span class="s2"&gt;"https://www.googleapis.com/auth/trace.append"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--num-nodes&lt;/span&gt; &lt;span class="s2"&gt;"3"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-autoupgrade&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-autorepair&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-surge-upgrade&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-unavailable-upgrade&lt;/span&gt; 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are wondering, the example is taken directly from Google Cloud Platform documentation.&lt;/p&gt;

&lt;p&gt;I found myself sinking more time into reformatting these lines than I'd like to admit. And I knew there had to be a better way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introducing the Bash Command Prettifier:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where the &lt;strong&gt;Bash Command Prettifier&lt;/strong&gt; comes into play. I designed this tool to tackle this exact issue head-on. The aim? To convert tedious, single-line commands into a neatly organized, multi-line format that's both pleasant to the eye and perfect for documentation.&lt;/p&gt;

&lt;p&gt;Simply input your marathon-length bash command, and voila! Out comes a formatted, doc-friendly version.&lt;/p&gt;

&lt;p&gt;🌟 &lt;strong&gt;Find the open-source code here&lt;/strong&gt;: &lt;a href="https://gist.github.com/lucgagan/9f4219058f89c9d003ceced6d1d544b3"&gt;Bash Command Prettifier on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is an online demo: &lt;a href="https://ray.run/tools/bash-command-formatter"&gt;https://ray.run/tools/bash-command-formatter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In Conclusion:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In our fast-paced DevOps world, every second counts. Little efficiencies can make all the difference in productivity and mental well-being. My hope is that the Bash Command Prettifier serves you well and shaves precious minutes off your documentation tasks.&lt;/p&gt;

&lt;p&gt;I'd love to hear your feedback or any suggestions for improvement. Wishing you clear and readable bash commands! Until next time. 🚀&lt;/p&gt;

</description>
      <category>bash</category>
      <category>tooling</category>
      <category>devops</category>
    </item>
    <item>
      <title>Getting JSON with TypeScript types from ChatGPT response</title>
      <dc:creator>Luc Gagan</dc:creator>
      <pubDate>Tue, 27 Jun 2023 07:18:54 +0000</pubDate>
      <link>https://dev.to/lucgagan/getting-json-with-typescript-types-from-chatgpt-response-38hb</link>
      <guid>https://dev.to/lucgagan/getting-json-with-typescript-types-from-chatgpt-response-38hb</guid>
      <description>&lt;p&gt;I updated &lt;a href="https://github.com/lucgagan/completions"&gt;completions&lt;/a&gt; library to allow statically typed responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Suggest a random startup name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// These are the examples of what&lt;/span&gt;
    &lt;span class="c1"&gt;// the response should look like.&lt;/span&gt;
    &lt;span class="na"&gt;examples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OpenAI&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="c1"&gt;// This is the schema that the&lt;/span&gt;
    &lt;span class="c1"&gt;// response must satisfy.&lt;/span&gt;
    &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;additionalProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;domain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the &lt;code&gt;response.content&lt;/code&gt; value's type is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  name: string;
  domain: string
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behind the scenes, the SDK will use the &lt;code&gt;expect&lt;/code&gt; parameter to generate a prompt that will be sent to the API. The prompt will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Suggest a random startup name

Respond ONLY with a JSON object that satisfies the following schema:

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "domain": { "type": "string" },
  },
  "required": ["name", "domain"],
}

Examples:

{
  "name": "OpenAI",
  "domain": "openai.com"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK will parse the response and validate it against the schema. If the response is invalid, it will throw an error. If the response is valid, it will return the response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   name: "Dex",&lt;/span&gt;
&lt;span class="c1"&gt;//   domain: "dex.com",&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This abstraction makes it easy to get JSON response from ChatGPT and you get a typed result!&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Accessibility Testing with Playwright</title>
      <dc:creator>Luc Gagan</dc:creator>
      <pubDate>Mon, 26 Jun 2023 14:45:23 +0000</pubDate>
      <link>https://dev.to/lucgagan/accessibility-testing-with-playwright-3n2k</link>
      <guid>https://dev.to/lucgagan/accessibility-testing-with-playwright-3n2k</guid>
      <description>&lt;p&gt;Web accessibility (a11y) is a critical aspect of modern software development, ensuring that applications are usable by as many people as possible, including those with disabilities. As software QA engineers, we are often tasked with ensuring that our applications meet varying a11y standards. One of the most powerful tools we can leverage for this task is &lt;code&gt;@playwright/test&lt;/code&gt;. This article will delve into the intricacies of utilizing playwright for a11y testing, offering a comprehensive guide on how to harness its capabilities effectively. &lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Accessibility (a11y) Testing
&lt;/h2&gt;

&lt;p&gt;Before we delve into the specifics of a11y testing using Playwright, it's essential to understand what a11y testing entails. This form of testing helps identify and rectify usability issues in your application that could hinder users with disabilities. These issues could range from text with poor color contrast, making it hard to read for users with vision impairments, to UI controls and form elements lacking labels that a screen reader can identify.&lt;/p&gt;

&lt;p&gt;However, it's important to note that while automated a11y tests can detect some common issues, many problems can only be discovered through manual testing. Therefore, a combination of automated testing, manual assessments, and inclusive user testing is recommended.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to Playwright
&lt;/h2&gt;

&lt;p&gt;Playwright is a powerful, open-source framework that provides a high-level API to control web browsers. It's designed to enable reliable end-to-end testing of web applications. Though Playwright supports multiple programming languages, we will focus on TypeScript in this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Playwright for Accessibility Testing
&lt;/h2&gt;

&lt;p&gt;Playwright offers several features that make it a valuable tool for a11y testing. One of its most significant features is the ability to capture accessibility tree snapshots, offering you an insight into how your application is perceived by assistive technologies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://ray.run/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accessibility&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accessibility tree&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessibility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toMatchSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accessibility-tree.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code snippet, we navigate to the desired page and capture the accessibility tree, comparing it with a previous run stored in &lt;code&gt;accessibility-tree.json&lt;/code&gt;. This comparison allows you to identify any changes in the accessibility tree that may affect a11y.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveraging Axe-Core/Playwright for Automated Accessibility Testing
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;@axe-core/playwright&lt;/code&gt; package is a valuable tool in the Playwright ecosystem for automating a11y tests. It provides support for running the axe accessibility testing engine as part of your Playwright tests. Let's take a look at how to utilize this package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;AxeBuilder&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@axe-core/playwright&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;homepage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should not have any automatically detectable accessibility issues&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://ray.run/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accessibilityScanResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AxeBuilder&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accessibilityScanResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;violations&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we're testing a page for automatically detectable accessibility violations. We import the &lt;code&gt;@axe-core/playwright&lt;/code&gt; package and use Playwright's syntax to navigate to the page under test. We then await &lt;code&gt;AxeBuilder.analyze()&lt;/code&gt; to run the accessibility scan against the page and verify that there are no violations in the returned scan results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Accessibility Testing Scenarios with Playwright
&lt;/h2&gt;

&lt;p&gt;Playwright provides several advanced features that allow you to perform more sophisticated a11y tests. For example, the &lt;code&gt;AxeBuilder&lt;/code&gt; class supports numerous configuration options for axe. You can specify these options using a Builder pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;AxeBuilder&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@axe-core/playwright&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;homepage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should not have any automatically detectable accessibility issues&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://ray.run/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axeBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AxeBuilder&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;axeBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#someElement&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accessibilityScanResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axeBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accessibilityScanResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;violations&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we've used the &lt;code&gt;AxeBuilder.include()&lt;/code&gt; function to constrain an a11y scan to only run against one specific part of a page. This allows for more targeted testing based on your application's specific needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Landmark Testing with Playwright
&lt;/h2&gt;

&lt;p&gt;Landmark testing involves checking for the existence of specific landmarks on your webpage. These landmarks are critical because they help users with assistive technologies navigate your website more effectively. Here's an example of how you can utilize Playwright for landmark testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;within&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;landmarks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;has main nav&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nav&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;navigation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;has main landmark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;has banner landmark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;banner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;banner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;banner&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we're checking for the existence of a &lt;code&gt;navigation&lt;/code&gt;, &lt;code&gt;main&lt;/code&gt;, and &lt;code&gt;banner&lt;/code&gt; landmark and ensuring they're visible on the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incorporating Lighthouse for Accessibility Testing
&lt;/h2&gt;

&lt;p&gt;While Axe provides robust automated accessibility testing capabilities, it's important to supplement it with additional tools for a comprehensive evaluation. One such tool is Lighthouse, an open-source project by Google that offers a range of audits for assessing web page quality, including accessibility.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can access Lighthouse from Chrome by opening Developer Tools and selecting the Lighthouse tab.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To incorporate Lighthouse into your Playwright tests, you can use the &lt;code&gt;playwright-lighthouse&lt;/code&gt; package. Here's an example of how you can run a Lighthouse accessibility audit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Browser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;getPort&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get-port&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;playAudit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;playwright-lighthouse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extend&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Browser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`--remote-debugging-port=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;

  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// eslint-disable-next-line no-empty-pattern&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Assign a unique port for each playwright worker to support parallel tests&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;getPort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accessibility Testing with Lighthouse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should pass the Lighthouse accessibility audit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://ray.run/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;playAudit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;thresholds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;accessibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we run the Lighthouse audit and assess that the accessibility score is 100, indicating that the page passes the audit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manual Validation and Exploratory Testing
&lt;/h2&gt;

&lt;p&gt;While automated accessibility testing is a valuable tool, it's important to remember that it can only cover a portion of the WCAG (Web Content Accessibility Guidelines) requirements. To ensure a truly inclusive user experience, manual validation and exploratory testing are essential.&lt;/p&gt;

&lt;p&gt;Manual validation involves manually inspecting and interacting with a website or application using assistive technologies, such as screen readers or keyboard navigation. This allows testers to identify any accessibility issues that may not be captured by automated tests.&lt;/p&gt;

&lt;p&gt;Exploratory testing, on the other hand, involves exploring a website or application from the perspective of different user personas. By simulating real-world scenarios, testers can uncover potential accessibility barriers and usability issues that may impact users with disabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Playwright is a powerful tool for a11y testing, providing a high-level API for automated and manual testing. By leveraging its capabilities, QA engineers can ensure that their applications are accessible to as many users as possible, enhancing user experience and inclusivity.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>playwright</category>
      <category>testing</category>
      <category>programming</category>
    </item>
    <item>
      <title>Unlock the Power of ChatGPT Functions and Supercharge Your Conversations!</title>
      <dc:creator>Luc Gagan</dc:creator>
      <pubDate>Mon, 26 Jun 2023 00:24:26 +0000</pubDate>
      <link>https://dev.to/lucgagan/unlock-the-power-of-chatgpt-functions-and-supercharge-your-conversations-40j0</link>
      <guid>https://dev.to/lucgagan/unlock-the-power-of-chatgpt-functions-and-supercharge-your-conversations-40j0</guid>
      <description>&lt;p&gt;OpenAI's cutting-edge models, like &lt;code&gt;gpt-3.5-turbo-0613&lt;/code&gt; and &lt;code&gt;gpt-4-0613&lt;/code&gt;, introduce an exciting feature called "&lt;a href="https://openai.com/blog/function-calling-and-other-api-updates"&gt;function calling&lt;/a&gt;." It empowers developers to define and invoke functions within the model's responses, making it a breeze to retrieve structured data and accomplish a wide range of tasks. In this article, we'll dive into the world of ChatGPT functions, exploring what they are, how to use them, and providing you with inspiring examples of their boundless use cases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZfurVWWO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhm33tat3icbyqur8e0n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZfurVWWO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhm33tat3icbyqur8e0n.png" alt="Making ChatGPT use our locally defined functions" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start by unraveling the magic of ChatGPT functions!&lt;/p&gt;

&lt;h2&gt;
  
  
  What are ChatGPT Functions?
&lt;/h2&gt;

&lt;p&gt;By embedding functions directly into the system prompt, you can guide the model to output a JSON object containing function arguments that seamlessly integrate with your code. It's important to note that the Chat Completions API doesn't execute the functions itself. Instead, it generates the necessary JSON to facilitate function calls, giving you complete control and flexibility.&lt;/p&gt;

&lt;p&gt;The latest ChatGPT models have been fine-tuned to intelligently detect when to call functions based on the input and respond with JSON that perfectly matches the function's signature.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exciting Use Cases for ChatGPT Functions
&lt;/h3&gt;

&lt;p&gt;Let's explore some captivating use cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Supercharged Chatbots: Energize your chatbots by defining functions that interact with external APIs. Your chatbot can now answer questions and provide information sourced from various services. Imagine defining functions like &lt;code&gt;send_email(to: string, body: string)&lt;/code&gt; or &lt;code&gt;get_current_weather(location: string, unit: 'celsius' | 'fahrenheit')&lt;/code&gt; to send emails or fetch weather information, respectively.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Natural Language to API Calls: Convert user queries expressed in natural language into powerful API calls. Define functions that leverage context to transform queries like "Who are my top customers?" into API calls such as &lt;code&gt;get_customers(min_revenue: int, created_before: string, limit: int)&lt;/code&gt;. Seamlessly retrieve customer data from your internal API and dazzle your users with personalized insights.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unleashing Structured Data: Extract structured data from unstructured text with ease. Define functions like &lt;code&gt;extract_data(name: string, birthday: string)&lt;/code&gt; or &lt;code&gt;sql_query(query: string)&lt;/code&gt; to dive into text inputs and extract specific information. Turn mountains of unstructured data into actionable insights, giving you a competitive edge.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As another example, I use functions in my &lt;a href="https://ray.run/jobs"&gt;Playwright Job Board&lt;/a&gt; to extract the name of the company from a job description and pull information from Crunchbase.&lt;/p&gt;

&lt;p&gt;These examples merely scratch the surface of what ChatGPT functions can achieve. They open doors to integrating AI-driven conversational agents with external services, automating workflows, and unlocking hidden gems in unstructured data.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Harness the Power of ChatGPT Functions
&lt;/h3&gt;

&lt;p&gt;Now that we've ignited your curiosity, let's explore how to put ChatGPT functions into action using the &lt;a href="https://github.com/lucgagan/completions"&gt;&lt;code&gt;completions&lt;/code&gt; JavaScript package&lt;/a&gt;. Buckle up, here's your guide:&lt;/p&gt;

&lt;p&gt;1. Get started by installing the &lt;a href="https://github.com/lucgagan/completions"&gt;&lt;code&gt;completions&lt;/code&gt; package&lt;/a&gt; via npm. A single command sets the stage for your ChatGPT adventure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;completions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2. Import the necessary modules and create a chat instance. It's time to rev up the engines!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createChat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CancelledCompletionError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;completions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createChat&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-3.5-turbo-0613&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3. Define your functions within the &lt;code&gt;createChat&lt;/code&gt; configuration. Get ready to unleash the magic!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createChat&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-3.5-turbo-0613&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_current_weather&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Get the current weather in a given location&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The city and state, e.g. San Francisco, CA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;celsius&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fahrenheit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;location&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Albuquerque&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;72&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fahrenheit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;forecast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sunny&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;windy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;functionCall&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4. Send a message to the chat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is the weather in Albuquerque?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Prepare to be amazed: "The weather in Albuquerque is 72 degrees Fahrenheit, sunny with a light breeze."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As mentioned earlier, ChatGPT will automatically determine when and which function to call, but you can optionally tell it to call a specific function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is the weather in Albuquerque?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;functionCall&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_current_weather&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Output: "The weather in Albuquerque is 72 degrees fahrenheit, sunny with a light breeze."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The latter is mostly useful if you have functions with similar descriptions. Otherwise, ChatGPT is doing Okay job at picking the right function to call automatically.&lt;/p&gt;

&lt;p&gt;With a simple message to the chat, the model will work its magic and, if suitable, invoke the defined function to retrieve the current weather in Albuquerque. The response you receive will contain the generated text, including the weather information obtained through the function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;ChatGPT functions empower you to build captivating conversational AI experiences by seamlessly integrating external services, automating workflows, and extracting meaningful insights. With ChatGPT, the possibilities are endless! So let your creativity soar, follow the provided examples, and embrace the exhilarating world of ChatGPT functions. Get ready to make your applications intelligent, interactive, and truly extraordinary!&lt;/p&gt;

</description>
      <category>openai</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>Discover Your Next Role on My New Playwright QA Job Board!</title>
      <dc:creator>Luc Gagan</dc:creator>
      <pubDate>Sat, 24 Jun 2023 21:43:21 +0000</pubDate>
      <link>https://dev.to/lucgagan/discover-your-next-role-on-my-new-playwright-qa-job-board-406c</link>
      <guid>https://dev.to/lucgagan/discover-your-next-role-on-my-new-playwright-qa-job-board-406c</guid>
      <description>&lt;h1&gt;
  
  
  Discover Your Next Role on My New Playwright QA Job Board!
&lt;/h1&gt;

&lt;p&gt;Hello everyone! I hope you're all as excited about this news as I am. This weekend, I embarked on a fantastic journey of development and successfully built a niche QA job board - but it's not just any job board, it's one specifically for companies using Playwright (&lt;a href="https://playwright.dev/"&gt;https://playwright.dev/&lt;/a&gt;)! Check it out at &lt;a href="https://ray.run/jobs"&gt;https://ray.run/jobs&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;This might sound a bit unusual at first. A job board exclusively for Playwright jobs? But before you dismiss it as too specialized, allow me to explain why I felt this was a necessary step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a Playwright-specific Job Board?
&lt;/h2&gt;

&lt;p&gt;There's currently a massive, industry-wide shift taking place, with a surge towards test automation using Playwright. Playwright is a fantastic tool that has taken the tech industry by storm. It's allowing businesses to automate their browser-based testing, making QA more streamlined, faster, and more effective than ever. &lt;/p&gt;

&lt;p&gt;However, despite this boom, there's one problem that's been glaringly evident. Finding these specific Playwright jobs is like searching for a needle in a haystack! It's painstakingly hard. So, to make life easier for everyone in our wonderful community, I thought, "Why not create a focused platform where employers and potential employees can connect, without the clutter of irrelevant listings?" &lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes &lt;a href="https://ray.run/jobs"&gt;https://ray.run/jobs&lt;/a&gt; Unique?
&lt;/h2&gt;

&lt;p&gt;Here's the exciting part: &lt;a href="https://ray.run/jobs"&gt;https://ray.run/jobs&lt;/a&gt; is not just your ordinary job board. All the jobs listed are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;100% Remote&lt;/strong&gt; 🌎: The beauty of technology is that it brings the world to our doorstep. With the world rapidly embracing remote work, we ensure that all job postings are remote, allowing you to work from the comfort of your home, or wherever else you fancy!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Specifically for Playwright Users&lt;/strong&gt; 🎭: This job board is built specifically for Playwright enthusiasts. If you have skills in @playwright/test, this is THE platform for you to showcase your expertise and find companies looking for your skills.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manually Sourced by Me&lt;/strong&gt; 🙈: That's right, folks! Every job you see on this platform has been handpicked by me. I've scoured the internet to bring the best, most relevant opportunities to you.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Direct Apply Links&lt;/strong&gt; 🎯: There's no jumping through hoops here. All jobs listed on the board link directly to the application form. No middlemen, no unnecessary steps, just a straightforward path to your potential new job.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;So there you have it, folks! A high-energy weekend project turned into a unique platform that brings you one step closer to your dream job. If you're a Playwright enthusiast looking for an exciting new opportunity, make sure you visit &lt;a href="https://ray.run/jobs"&gt;https://ray.run/jobs&lt;/a&gt;. You might just find your next role there.&lt;/p&gt;

&lt;p&gt;Remember, in this ever-changing tech landscape, it's the niche skills that often set you apart. So keep honing your Playwright skills and get ready to make a significant impact in the industry. Here's to an exciting journey ahead with Playwright!&lt;/p&gt;

</description>
      <category>sideprojects</category>
      <category>job</category>
      <category>playwright</category>
    </item>
  </channel>
</rss>
