<?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: Max Schmitt</title>
    <description>The latest articles on DEV Community by Max Schmitt (@mxschmitt).</description>
    <link>https://dev.to/mxschmitt</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%2F152928%2F84b735e1-ed46-49df-84b3-c37f2291e163.jpeg</url>
      <title>DEV Community: Max Schmitt</title>
      <link>https://dev.to/mxschmitt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mxschmitt"/>
    <language>en</language>
    <item>
      <title>Running Playwright Codegen with existing Chromium Profiles</title>
      <dc:creator>Max Schmitt</dc:creator>
      <pubDate>Thu, 19 Sep 2024 10:16:53 +0000</pubDate>
      <link>https://dev.to/mxschmitt/running-playwright-codegen-with-existing-chromium-profiles-5g7k</link>
      <guid>https://dev.to/mxschmitt/running-playwright-codegen-with-existing-chromium-profiles-5g7k</guid>
      <description>&lt;p&gt;Playwright is a powerful tool for browser automation, and one of its lesser-known features is the ability to run tests using existing browser profiles. This can be incredibly useful when you need to test scenarios that require specific browser configurations, extensions, or saved login states. In this article, we'll explore how to use Playwright to launch Microsoft Edge or Google Chrome with custom profiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Environment
&lt;/h2&gt;

&lt;p&gt;Before we dive into the code, make sure you have Playwright installed in your project:&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; @playwright/test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a file named &lt;code&gt;codegen.mjs&lt;/code&gt; in your project directory. We'll use this file to run our Playwright scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Launching a Browser with the Default Profile
&lt;/h2&gt;

&lt;p&gt;Let's start with a basic example that launches Microsoft Edge with the default profile:&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;chromium&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="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="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;context&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="nf"&gt;launchPersistentContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Users&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;YourUsername&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;AppData&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Local&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Microsoft&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Edge&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;User Data&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;headless&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;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;msedge&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="c1"&gt;// For macOS, use:&lt;/span&gt;
  &lt;span class="c1"&gt;// Microsoft Edge: '/Users/YourUsername/Library/Application Support/Microsoft Edge'&lt;/span&gt;
  &lt;span class="c1"&gt;// Google Chrome:  '/Users/YourUsername/Library/Application Support/Google/Chrome'&lt;/span&gt;

  &lt;span class="kd"&gt;const&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&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="nf"&gt;pause&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// This will open Codegen if needed&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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 script does the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Imports the &lt;code&gt;chromium&lt;/code&gt; object from Playwright.&lt;/li&gt;
&lt;li&gt;Launches a persistent context using the default Edge profile.&lt;/li&gt;
&lt;li&gt;Gets the first page from the context.&lt;/li&gt;
&lt;li&gt;Pauses the execution, which opens the Playwright Inspector (Codegen) for interactive debugging.&lt;/li&gt;
&lt;li&gt;Closes the context when done.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Using a Specific Browser Profile
&lt;/h2&gt;

&lt;p&gt;To use a different profile, we need to add the &lt;code&gt;--profile-directory&lt;/code&gt; argument. The profile directory might be different to the profile&lt;br&gt;
name. In order to find out the profile directory, you can navigate to &lt;code&gt;chrome://version&lt;/code&gt; in the browser and look for the &lt;code&gt;Profile Path&lt;/code&gt; field. The last part of the path is the profile directory name.&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;chromium&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="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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Setup&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&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="nf"&gt;launchPersistentContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Users&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;YourUsername&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;AppData&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Local&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Microsoft&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;Edge&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;User Data&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;headless&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;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;msedge&lt;/span&gt;&lt;span class="dl"&gt;'&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;`--profile-directory=Profile 2`&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// For macOS, use:&lt;/span&gt;
  &lt;span class="c1"&gt;// Microsoft Edge: '/Users/YourUsername/Library/Application Support/Microsoft Edge'&lt;/span&gt;
  &lt;span class="c1"&gt;// Google Chrome:  '/Users/YourUsername/Library/Application Support/Google/Chrome'&lt;/span&gt;

  &lt;span class="kd"&gt;const&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&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="nf"&gt;pause&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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 key difference here is the &lt;code&gt;args&lt;/code&gt; option in the &lt;code&gt;launchPersistentContext&lt;/code&gt; method, which specifies the profile directory to use.&lt;/p&gt;

&lt;p&gt;Remember to replace &lt;code&gt;YourUsername&lt;/code&gt; with your actual username on your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Playwright Codegen
&lt;/h2&gt;

&lt;p&gt;In both examples, we've included &lt;code&gt;await page.pause();&lt;/code&gt;. This line opens the Playwright Inspector, also known as Codegen. This tool allows you to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inspect the page structure&lt;/li&gt;
&lt;li&gt;Record actions and generate code&lt;/li&gt;
&lt;li&gt;Step through your script&lt;/li&gt;
&lt;li&gt;Modify and rerun parts of your script&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Running the Script
&lt;/h2&gt;

&lt;p&gt;To run the script, use the following command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node codegen.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Important Notes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Profile Availability&lt;/strong&gt;: Ensure that the browser profile you're trying to use isn't already open. If it is, Playwright won't be able to launch it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Profile Names&lt;/strong&gt;: The profile directory names (e.g., "Profile 2") should match exactly with the names in your browser's profile directory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Browser Channel&lt;/strong&gt;: In our examples, we've used &lt;code&gt;channel: 'msedge'&lt;/code&gt; for Microsoft Edge. For Google Chrome, you would use &lt;code&gt;channel: 'chrome'&lt;/code&gt;. Make sure to adjust the profile path accordingly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Path Accuracy&lt;/strong&gt;: Double-check that the path to your browser's user data directory is correct. It may vary depending on your operating system and browser version.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Useful links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://playwright.dev/docs/intro" rel="noopener noreferrer"&gt;Getting started with Playwright&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://playwright.dev/docs/codegen-intro#running-codegen" rel="noopener noreferrer"&gt;Codegen docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aka.ms/playwright/discord" rel="noopener noreferrer"&gt;Playwright on Discord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/playwright" rel="noopener noreferrer"&gt;Star us on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>playwright</category>
      <category>browserprofiles</category>
      <category>automation</category>
    </item>
    <item>
      <title>Implementing Microsoft Entra Certificate-Based Authentication with Playwright</title>
      <dc:creator>Max Schmitt</dc:creator>
      <pubDate>Wed, 18 Sep 2024 13:33:06 +0000</pubDate>
      <link>https://dev.to/playwright/implementing-microsoft-entra-certificate-based-authentication-with-playwright-4lbc</link>
      <guid>https://dev.to/playwright/implementing-microsoft-entra-certificate-based-authentication-with-playwright-4lbc</guid>
      <description>&lt;h3&gt;
  
  
  Introduction to Certificate-Based Authentication (CBA)
&lt;/h3&gt;

&lt;p&gt;Microsoft &lt;a href="https://techcommunity.microsoft.com/t5/microsoft-entra-blog/microsoft-entra-certificate-based-authentication-enhancements/ba-p/1751778" rel="noopener noreferrer"&gt;recently announced&lt;/a&gt; (July 2024) support for certificate-based authentication (CBA) for Microsoft Entra. CBA is a phishing-resistant, passwordless, and convenient way to authenticate users with X.509 certificates without relying on passwords. With the recent &lt;a href="https://github.com/microsoft/playwright/releases/tag/v1.46.0" rel="noopener noreferrer"&gt;v1.46 release&lt;/a&gt;, Playwright now supports the CBA method for authenticating with Entra.&lt;/p&gt;

&lt;h3&gt;
  
  
  TLS Client Certificates and Their Role in CBA
&lt;/h3&gt;

&lt;p&gt;TLS client certificates are digital credentials that authenticate clients to servers in secure connections. They contain the client's public key, are signed by trusted authorities, and enable mutual authentication. These certificates enhance security by verifying client identities, preventing unauthorized access to sensitive resources in various IT environments.&lt;/p&gt;

&lt;p&gt;If no client certificate is available, your browser will display a prompt like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwi5pnfgpyh1kta5novw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwi5pnfgpyh1kta5novw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing CBA with Playwright
&lt;/h3&gt;

&lt;p&gt;Step 1: Obtain the certificates&lt;/p&gt;

&lt;p&gt;If the certificate files are not yet available on the client, we recommend defining &lt;a href="https://playwright.dev/docs/test-fixtures" rel="noopener noreferrer"&gt;base fixtures&lt;/a&gt; that will fetch the certificates in every worker process. The certificates will then remain in memory. See &lt;a href="https://playwright.dev/docs/api/class-testoptions#test-options-client-certificates" rel="noopener noreferrer"&gt;here&lt;/a&gt; for all available options for &lt;code&gt;clientCertificates&lt;/code&gt; (e.g., if you are using PEM instead of PKCS#12).&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;DefaultAzureCredential&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;@azure/identity&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;SecretClient&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;@azure/keyvault-secrets&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;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="c1"&gt;// DefaultAzureCredential automatically detects and uses the most appropriate authentication method,&lt;/span&gt;
&lt;span class="c1"&gt;// including environment variables, managed identities, and OIDC tokens from GitHub Actions.&lt;/span&gt;
&lt;span class="c1"&gt;// We recommend OIDC logins via e.g. azure/login:&lt;/span&gt;
&lt;span class="c1"&gt;// https://github.com/Azure/login/?tab=readme-ov-file#login-with-openid-connect-oidc-recommended&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;credential&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DefaultAzureCredential&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;vaultName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;YOUR KEYVAULT NAME&amp;gt;&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;KEYVAULT_URI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;vaultName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.vault.azure.net`&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;secretName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;YOUR SECRET&amp;gt;&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;secretClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SecretClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;KEYVAULT_URI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;credential&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="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;clientCertificates&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="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;certificateSecret&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;secretClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secretName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;origin&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://certauth.login.microsoftonline.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="c1"&gt;// Alternatively, if you use e.g. PPE, it's: https://certauth.login.windows-ppe.net&lt;/span&gt;
        &lt;span class="na"&gt;pfx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;certificateSecret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&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;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

        &lt;span class="c1"&gt;// You might need to provide your passphrase here:&lt;/span&gt;
        &lt;span class="c1"&gt;// passphrase: process.env.SECRET_PASSPHRASE&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;export&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Step 2: Update your test &lt;code&gt;@playwright/test&lt;/code&gt; imports&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="gd"&gt;-import { test, expect } from '@playwright/test';
&lt;/span&gt;&lt;span class="gi"&gt;+import { test, expect } from './baseTest';
&lt;/span&gt;&lt;span class="err"&gt;

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

&lt;/div&gt;

&lt;p&gt;Step 3: Log in inside a test&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;./baseTest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&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;is able to login&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="nf"&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://your-application.com/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Your login logic here&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="nf"&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;textbox&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;Email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example@foo.com&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="nf"&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="nf"&gt;click&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;You can also provide client certificates as a parameter of &lt;a href="https://playwright.dev/docs/api/class-browser#browser-new-context" rel="noopener noreferrer"&gt;browser.newContext()&lt;/a&gt; and &lt;a href="https://playwright.dev/docs/api/class-apirequest#api-request-new-context" rel="noopener noreferrer"&gt;apiRequest.newContext()&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Troubleshooting
&lt;/h3&gt;

&lt;p&gt;To debug which network requests are being made, you can set the &lt;code&gt;DEBUG=pw:client-certificates&lt;/code&gt; environment variable. This will print all the connections that are being established.&lt;/p&gt;

&lt;p&gt;It's a known issue that the authentication does not work if the &lt;code&gt;--disable-web-security&lt;/code&gt; argument is passed to Chromium.&lt;/p&gt;

&lt;h2&gt;
  
  
  Useful links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://playwright.dev/docs/intro" rel="noopener noreferrer"&gt;Getting started with Playwright&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://playwright.dev/docs/api/class-testoptions#test-options-client-certificates" rel="noopener noreferrer"&gt;TLS Client Certificate docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aka.ms/playwright/discord" rel="noopener noreferrer"&gt;Playwright on Discord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/playwright" rel="noopener noreferrer"&gt;Star us on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>azure</category>
      <category>entra</category>
      <category>playwright</category>
      <category>authentication</category>
    </item>
    <item>
      <title>Troubleshoot your Playwright or Puppeteer tests and find the root cause for failing tests</title>
      <dc:creator>Max Schmitt</dc:creator>
      <pubDate>Thu, 22 Oct 2020 13:19:26 +0000</pubDate>
      <link>https://dev.to/mxschmitt/troubleshoot-your-playwright-tests-and-find-the-root-cause-for-failing-tests-4082</link>
      <guid>https://dev.to/mxschmitt/troubleshoot-your-playwright-tests-and-find-the-root-cause-for-failing-tests-4082</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;End-to-end tests are important to ensure that there are no regressions in the functionality of a web application over time. As part of a modern development workflow, we would want to run them automatically in the CI before every code merge to the codebase (e.g. GitHub pull request) and deploy.&lt;/p&gt;

&lt;p&gt;An end-to-end test is usually a programmatically executed real user flow in the app, typically executed in the cloud on headless browser instances as part of CI process. End-to-end tests help us to keep our app working and the users satisfied.&lt;/p&gt;

&lt;p&gt;On this blog post, we will focus on Playwright, a Node.js library which makes it possible to automate Chromium, Firefox, and WebKit with a single API and Root Cause, a library to help us troubleshoot end-to-end tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging tests
&lt;/h2&gt;

&lt;p&gt;One of the challenges of creating, running and maintaining end-to-end tests is that once changes are implemented, tests might fail and finding the root cause of the failure can be difficult. For this scenario, &lt;a href="https://www.testim.io" rel="noopener noreferrer"&gt;Testim&lt;/a&gt; has created an Open Source tool called &lt;a href="https://github.com/testimio/root-cause" rel="noopener noreferrer"&gt;Root Cause&lt;/a&gt;, that makes it easy to debug and analyze failures with a central interface even when they fail on the CI. It focuses on the following core needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visualize to troubleshoot - Show screenshots and highlight used elements&lt;/li&gt;
&lt;li&gt;Store locally or in the cloud - Analyse the results locally or on a central hosted interface&lt;/li&gt;
&lt;li&gt;Drill-down with data you need - View console logs or the network traffic as HAR files&lt;/li&gt;
&lt;li&gt;Test run reporting - Access to filter and search functionality&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Integrations
&lt;/h2&gt;

&lt;p&gt;Writing tests in the JavaScript or TypeScript ecosystem is mostly done using the Jest test runner, it provides a solid foundation for finding and running tests and has integrated helper methods to assert values against expected values. The Root Cause project provides integration for common test runners such as Jest and Mocha.&lt;/p&gt;

&lt;p&gt;For Jest, you only have to install it with &lt;code&gt;npm install @testim/root-cause-jest&lt;/code&gt; from &lt;a href="http://npmjs.com/package/@testim/root-cause-jest" rel="noopener noreferrer"&gt;NPM&lt;/a&gt; and adjust your main Jest configuration, for a full setup guide, see the official documentation on &lt;a href="https://help.testim.io/docs/root-cause-jest-integration-guide" rel="noopener noreferrer"&gt;help.testim.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once it's successfully integrated you can continue as normal with Playwright. So you can interact with pages to navigate to sites, click on elements, or call your custom logic. Root Cause internally will then record it in the background to provide a first-class debug experience if they are failing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing results
&lt;/h2&gt;

&lt;p&gt;The results which Root Cause is recording gets saved automatically on your disk. You can view them by running &lt;code&gt;npx root-cause ls&lt;/code&gt; to list them and &lt;code&gt;npx root-cause show &amp;lt;result-id&amp;gt;&lt;/code&gt; to view a selected report. This will open the interface to see all the tests which were running, stack traces if there are failing tests, and additional metadata for network traffic, pages, or console as you can see here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl67oeoccyfq7jc9r6gi5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl67oeoccyfq7jc9r6gi5.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Root Cause Cloud
&lt;/h2&gt;

&lt;p&gt;Root Cause viewer is also available as a &lt;a href="https://www.testim.io/root-cause/" rel="noopener noreferrer"&gt;hosted solution&lt;/a&gt; which gives you the flexibility of uploading your test results from your CI provider (e.g. GitHub Actions, or Travis CI) to them and act as a central interface of your project. This automatically will analyze them if multiple are failing for the same reason or give you statistically-based insights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Root Cause is a great way to simplify troubleshooting your Playwright tests. You can get started using the OSS version on GitHub. For more information on the hosted Root Cause solution on &lt;a href="https://www.testim.io/root-cause/" rel="noopener noreferrer"&gt;Testim.io&lt;/a&gt; which can be used for free for most use cases. They also provide an example showcase repository &lt;a href="https://github.com/testimio/root-cause-interactive-demo" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt; which demonstrates other integrations like with Mocha.&lt;/p&gt;

&lt;p&gt;The Open Source project is hosted on GitHub: &lt;a href="https://github.com/testimio/root-cause" rel="noopener noreferrer"&gt;github.com/testimio/root-cause&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>puppeteer</category>
      <category>ci</category>
    </item>
    <item>
      <title>Monitoring with Playwright on Checkly made easy</title>
      <dc:creator>Max Schmitt</dc:creator>
      <pubDate>Thu, 24 Sep 2020 12:41:33 +0000</pubDate>
      <link>https://dev.to/mxschmitt/monitoring-with-playwright-on-checkly-made-easy-51ao</link>
      <guid>https://dev.to/mxschmitt/monitoring-with-playwright-on-checkly-made-easy-51ao</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Nowadays, catching issues with your service early on is paramount to ensuring that your SLAs are being met. For this reason, having a reliable and feature-rich active monitoring solution in place is a must. The typical monitoring solutions are e.g. Grafana which would be feature-rich but has no good native integration for launching browsers to emulate a real user. Also, you need to host it yourself which is mostly for monitoring solutions not advised (or you need a HA installation in another data center or cloud provider).&lt;/p&gt;

&lt;p&gt;To fix these issues, Checkly has created a product to provide developer-friendly status checks, which can be used to launch browsers in the cloud. By using it you also don't need to spin up an own instance in your infrastructure since Checkly is a SaaS (Software as a Service) product and they host it for you. They even provide a full public dashboard for your customers. Starting from today they provide a full Playwright integration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F602wdydafiksj1x7s043.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F602wdydafiksj1x7s043.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;There are two types of checks which you can use. API checks to validate a given response body, status code, or schemas like JSON or GraphQL or browser-based tests on which we'll focus on this blog post. Browser-based tests are running a headless Chromium instance in the cloud to validate e.g. that your production login is working. Other common alternative scenarios would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buy an item in your shop and validate that the user flow is working.&lt;/li&gt;
&lt;li&gt;Validate that the OAuth login with e.g. Google is working.&lt;/li&gt;
&lt;li&gt;Using a search on your website to ensure your elastic search in the background is correctly configured and serves the expected data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Checkly itself gives you the ability to run any Node.js related code either with Playwright or Puppeteer to automate your user flows. They have a secret store built in to provide the secrets over to your code once it is running.&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;assert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;assert&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;playwright&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;playwright&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;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;playwright&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="nf"&gt;launch&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;page&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&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="nf"&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://playwright.tech&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&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="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Everything about the Playwright framework - Playwright Community 🎭&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="nf"&gt;waitForSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text=Playwright Community&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="nf"&gt;waitForSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text=Try 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;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isAbove&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;$&lt;/span&gt;&lt;span class="nf"&gt;$eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;article&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;8&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="nf"&gt;close&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 ensuring the following checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assert that the title has the expected value&lt;/li&gt;
&lt;li&gt;Assert that "Playwright Community" is on the page&lt;/li&gt;
&lt;li&gt;Assert that "Try Playwright" is on the page&lt;/li&gt;
&lt;li&gt;Assert that at least 8 articles are on the page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If one of them is failing, Checkly will automatically treat this as a failing test and alert the user by e.g. Slack, PagerDuty, SMS, or Email.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrations
&lt;/h2&gt;

&lt;p&gt;This at all replaces already quite a lot of common monitoring solutions. Instead of configuring complex user interfaces, you can create your own Node.js scripts that perform your status checks which will run in the cloud.&lt;/p&gt;

&lt;p&gt;For more advanced integration, Checkly provides support for GitHub, which gives you a lot of other integrations like with Vercel or Heroku. This could e.g. be used to run your end-to-end tests on a Pull Request level with an environment that was created by one of these PaaS (Platform as a Service) providers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fadwfqkncma4i8xiub47a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fadwfqkncma4i8xiub47a.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also for Terraform, one of the biggest solutions for infrastructure as code they offer integration on &lt;a href="https://github.com/checkly/terraform-provider-checkly" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. This allows you to automatically manage large numbers of checks - a gamechanger when large APIs and hundreds of checks are involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Checkly provides an easy way of adding either status checks or entire end-to-end tests to your web applications which have now also Playwright support available. We at the Playwright Community are using it to ensure that the services which we publicly expose are on one hand high-available and on the other by that fully-functional when e.g. upgrading dependencies.&lt;/p&gt;

&lt;p&gt;We're looking forward to how Checkly will make their monitoring solution even more accessible for developers with e.g. versioned code, an integrated Monaco editor with better auto-completion, support for custom NPM modules, or a better debugging experience. We would recommend giving it a try and have not to worry about where to run your status checks or end-to-end tests and benefit from their simplicity. For a more detailed outlook, they provide an official public roadmap on &lt;a href="https://github.com/checkly/public-roadmap" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;More information you'll find on &lt;a href="https://www.checklyhq.com" rel="noopener noreferrer"&gt;checklyhq.com&lt;/a&gt; and on &lt;a href="https://theheadless.dev" rel="noopener noreferrer"&gt;theheadless.dev&lt;/a&gt; a resource by Checkly to get started with Playwright.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>monitoring</category>
      <category>checkly</category>
    </item>
    <item>
      <title>Automate Chromium, Firefox, and WebKit with Go (preview)</title>
      <dc:creator>Max Schmitt</dc:creator>
      <pubDate>Thu, 10 Sep 2020 18:55:11 +0000</pubDate>
      <link>https://dev.to/mxschmitt/automate-chromium-firefox-and-webkit-with-go-preview-j9</link>
      <guid>https://dev.to/mxschmitt/automate-chromium-firefox-and-webkit-with-go-preview-j9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Automating browsers in the cloud, like on Kubernetes with a lightweight, type-safe, and easy to use programming language is especially useful for critical applications. In this blog post, we're gonna focus on the &lt;a href="https://github.com/mxschmitt/playwright-go"&gt;Go version&lt;/a&gt; of &lt;a href="https://github.com/microsoft/playwright"&gt;Playwright&lt;/a&gt;, what the current state is and what their limits are.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/microsoft/playwright"&gt;Playwright&lt;/a&gt; is a Node.js library to automate Chromium, Firefox and WebKit with a single API. Playwright is built to enable cross-browser web automation that is ever-green, capable, reliable and fast. Headless execution is supported for all the browsers on all platforms.&lt;/p&gt;

&lt;p&gt;Playwright for &lt;a href="https://golang.org"&gt;Go&lt;/a&gt; aims to be a 1:1 wrapper of the JavaScript version. It uses like &lt;a href="https://github.com/microsoft/playwright-python"&gt;Playwright for Python&lt;/a&gt; the official driver implementation internally. Go has key differences compared to JavaScript and Python which affected on the Playwright implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dynamic values: When you interact with the browser process and want to execute JavaScript in it which returns dynamic values, Playwright for Go does internally convert it to the corresponding Go data types. In the user implementation, you have to cast it from the &lt;code&gt;interface{}&lt;/code&gt; based type to the basic types, &lt;code&gt;map[string]interface{}&lt;/code&gt; or &lt;code&gt;[]interface{}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Optional parameters: For optional parameters, we are using structs with pointers to the basic types. We provide helper methods for String/Int/Bool/Float to make the developer experience smooth.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;

&lt;p&gt;The core concept of the driver implementations like Go or Python is based on a sub process which is started in the background. This will be done with the &lt;code&gt;Run()&lt;/code&gt; and the corresponding &lt;code&gt;Stop()&lt;/code&gt; method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create screenshots
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/mxschmitt/playwright-go"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Launching the driver internally&lt;/span&gt;
    &lt;span class="n"&gt;pw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;playwright&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not launch playwright: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// Start the Chromium browser&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;pw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Launch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not launch Chromium: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// Creates internally a context and a new page&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not create page: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// Visit the website and wait for a network idle for at least 500ms&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://whatsmyuseragent.org"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;playwright&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageGotoOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;WaitUntil&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;playwright&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"networkidle"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not goto: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Screenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playwright&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageScreenshotOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;playwright&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo.png"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not create screenshot: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not close browser: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not stop Playwright: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;The core concept of Playwright is based on a browser that has multiple contexts. A single context is an isolated entity that has separate state for cookies or local storage. Each context has multiple pages with browser session shared between each other.&lt;/p&gt;

&lt;p&gt;We specify &lt;code&gt;networkidle&lt;/code&gt; to the &lt;code&gt;Page.Goto()&lt;/code&gt; call which waits after there is a network idle on the page for at least 500ms. You can either pass then a path to the &lt;code&gt;Page.Screenshot()&lt;/code&gt; method or use the returned data (&lt;code&gt;[]byte&lt;/code&gt;) directly.&lt;/p&gt;
&lt;h3&gt;
  
  
  Automate websites
&lt;/h3&gt;

&lt;p&gt;In this example we're navigating to &lt;a href="https://news.ycombinator.com/"&gt;Hacker News&lt;/a&gt; (popular tech news) site, crawling the current entries, and printing them to the standard output. This could in this case be done using HTML scrapers since Hacker News is based on returning rendered HTML but our approach would also work for dynamically generated sites with JavaScript which use e.g. React, Vue or Angular as a frontend framework.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/mxschmitt/playwright-go"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;pw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;playwright&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not start playwright: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;pw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Launch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not launch browser: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not create page: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://news.ycombinator.com"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not goto: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// Looping over the DOM elements&lt;/span&gt;
    &lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QuerySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;".athing"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not get entries: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;entries&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Finding the next title link element&lt;/span&gt;
        &lt;span class="n"&gt;titleElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QuerySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"td.title &amp;gt; a"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not get title element: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;titleElement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextContent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not get text content: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c"&gt;// Printing it to the console&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%d: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not close browser: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not stop Playwright: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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 making use of a selector on the latest news entries. We then loop over them, get its corresponding header/title element and print it to the console.&lt;/p&gt;
&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this introduction, we went through two examples and an introduction of Playwright for Go and its current state. We are working to make it bulletproof, concurrent-safe, and adding more tests in the next few weeks to ensure it can be used in production. For further reference, see on &lt;a href="https://github.com/mxschmitt/playwright-go"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/playwright-community"&gt;
        playwright-community
      &lt;/a&gt; / &lt;a href="https://github.com/playwright-community/playwright-go"&gt;
        playwright-go
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Playwright for Go a browser automation library to control Chromium, Firefox and WebKit with a single API.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
🎭 &lt;a href="https://github.com/microsoft/playwright#readme"&gt;Playwright&lt;/a&gt; for &lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/17984549/91302719-343a1d80-e7a7-11ea-8d6a-9448ef598420.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wTW3vVgK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/17984549/91302719-343a1d80-e7a7-11ea-8d6a-9448ef598420.png" height="35"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;h2&gt;
Looking for maintainers and see &lt;a href="https://github.com/playwright-community/playwright-go/issues/122"&gt;here&lt;/a&gt;. Thanks!&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://pkg.go.dev/github.com/playwright-community/playwright-go" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/05a2bbef9910d2716268dd989a5731aabc4ee5b5084fa0eb9648febbe485a8a8/68747470733a2f2f706b672e676f2e6465762f62616467652f6769746875622e636f6d2f706c61797772696768742d636f6d6d756e6974792f706c61797772696768742d676f" alt="PkgGoDev"&gt;&lt;/a&gt;
&lt;a href="http://opensource.org/licenses/MIT" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/5fab2edf3816ef9fb3ebcaf6e613fa7b40ff7652ec69e5f6e7f695aa24bf5ce6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d626c75652e737667" alt="License"&gt;&lt;/a&gt;
&lt;a href="https://goreportcard.com/report/github.com/playwright-community/playwright-go" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/b2923371b751b8ae47866bc43577c4607c4cf057db7e7d1caf8f70d946c5ae27/68747470733a2f2f676f7265706f7274636172642e636f6d2f62616467652f6769746875622e636f6d2f706c61797772696768742d636f6d6d756e6974792f706c61797772696768742d676f" alt="Go Report Card"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer" href="https://github.com/playwright-community/playwright-go/workflows/Go/badge.svg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ilYkiX44--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/playwright-community/playwright-go/workflows/Go/badge.svg" alt="Build Status"&gt;&lt;/a&gt;
&lt;a href="https://aka.ms/playwright-slack" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/84f2f075bb7742762e70685924cfc27c061cdc4c4f2d0b2997483cfe6c10d96d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6a6f696e2d736c61636b2d696e666f6d6174696f6e616c" alt="Join Slack"&gt;&lt;/a&gt; &lt;a href="https://coveralls.io/github/playwright-community/playwright-go?branch=main" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/078869c17672176764045e475b19bd74d7765e82289033da2d16fb5fca6fd215/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f706c61797772696768742d636f6d6d756e6974792f706c61797772696768742d676f2f62616467652e7376673f6272616e63683d6d61696e" alt="Coverage Status"&gt;&lt;/a&gt; &lt;a href="https://www.chromium.org/Home" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/4508dec776979b31021ee4c1e0dc5388b15b0421f7bba6308a8e75ecfd36e289/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6368726f6d69756d2d3130352e302e353139352e31392d626c75652e7376673f6c6f676f3d676f6f676c652d6368726f6d65" alt="Chromium version"&gt;&lt;/a&gt; &lt;a href="https://www.mozilla.org/en-US/firefox/new/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/c6a5b98199ae0bb5574bd931dd78437dc39c2346877a7b0e78b5f50b5eb6908f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f66697265666f782d3130332e302d626c75652e7376673f6c6f676f3d6d6f7a696c6c612d66697265666f78" alt="Firefox version"&gt;&lt;/a&gt; &lt;a href="https://webkit.org/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/cfa817dc087c7a58f3051ef0d38c3aff9e1fea8b6edcc6c040ae16492ba9729d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7765626b69742d31362e302d626c75652e7376673f6c6f676f3d736166617269" alt="WebKit version"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://playwright.dev/docs/api/class-playwright" rel="nofollow"&gt;API reference&lt;/a&gt; | &lt;a href="https://github.com/playwright-community/playwright-go/tree/main/examples"&gt;Example recipes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Playwright is a Go library to automate &lt;a href="https://www.chromium.org/Home" rel="nofollow"&gt;Chromium&lt;/a&gt;, &lt;a href="https://www.mozilla.org/en-US/firefox/new/" rel="nofollow"&gt;Firefox&lt;/a&gt; and &lt;a href="https://webkit.org/" rel="nofollow"&gt;WebKit&lt;/a&gt; with a single API. Playwright is built to enable cross-browser web automation that is &lt;strong&gt;ever-green&lt;/strong&gt;, &lt;strong&gt;capable&lt;/strong&gt;, &lt;strong&gt;reliable&lt;/strong&gt; and &lt;strong&gt;fast&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Linux&lt;/th&gt;
&lt;th&gt;macOS&lt;/th&gt;
&lt;th&gt;Windows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chromium 105.0.5195.19&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebKit 16.0&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firefox 103.0&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Headless execution is supported for all the browsers on all platforms.&lt;/p&gt;
&lt;h2&gt;
Installation&lt;/h2&gt;
&lt;div class="highlight highlight-text-adblock notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;go get github.com/playwright-community/playwright-go&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Install the browsers and OS dependencies:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;go run github.com/playwright-community/playwright-go/cmd/playwright install --with-deps
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Or&lt;/span&gt;
go install github.com/playwright-community/playwright-go/cmd/playwright
playwright install --with-deps&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Alternatively you can do it inside your program via which downloads the driver and browsers:&lt;/p&gt;
&lt;div class="highlight highlight-source-go notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-s1"&gt;err&lt;/span&gt; &lt;span class="pl-c1"&gt;:=&lt;/span&gt; &lt;span class="pl-s1"&gt;playwright&lt;/span&gt;.&lt;span class="pl-en"&gt;Install&lt;/span&gt;()&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
Capabilities&lt;/h2&gt;
&lt;p&gt;Playwright is built to automate the broad and growing set of web browser capabilities used by Single Page Apps and Progressive Web Apps.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scenarios that span…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/playwright-community/playwright-go"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>playwright</category>
      <category>go</category>
      <category>cdp</category>
      <category>automation</category>
    </item>
    <item>
      <title>Automate web browsers with Python and Playwright</title>
      <dc:creator>Max Schmitt</dc:creator>
      <pubDate>Fri, 07 Aug 2020 09:48:43 +0000</pubDate>
      <link>https://dev.to/mxschmitt/automate-web-browsers-with-python-and-playwright-372c</link>
      <guid>https://dev.to/mxschmitt/automate-web-browsers-with-python-and-playwright-372c</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, we're gonna focus on the current state of using Playwright with Python. &lt;a href="https://github.com/microsoft/playwright"&gt;Playwright&lt;/a&gt; is a Node.js library to automate browsers (Chromium, Firefox, WebKit) with a single API which provides now also the interfaces to provide other cross-language support, in this particular blog post Python.&lt;/p&gt;

&lt;p&gt;In comparison to other automation libraries like Selenium, Playwright offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Being less flaky by auto-waiting for elements to be ready before executing actions (like click, fill)&lt;/li&gt;
&lt;li&gt;Native support for emulating mobile devices, geolocation, and permissions&lt;/li&gt;
&lt;li&gt;Better developer experience by automatically installing the browsers&lt;/li&gt;
&lt;li&gt;Integrations for shadow-piercing selectors, native input events for mouse and keyboard or up-/downloading files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And by that, all these features are also available in the Python integration. Be aware, that Playwright Python is currently in &lt;strong&gt;beta&lt;/strong&gt; but exposes already most of the common methods and functions to be used. Since communication with browsers is mostly async based, Playwright does also provide an async based interface. It's a developer decision in the end but in most cases, the sync version is easier debuggable with REPLs like ipdb, pdb, or IPython since they don't work with await and by that, your are more productive with writing your actual features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;p&gt;Since the core concept of Playwright is also the same as in the Python version, the function calls are mostly the same except how you access the Playwright object. For that, you have to use the &lt;code&gt;sync_playwright&lt;/code&gt; context manager with a with statement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Page screenshot - sync
&lt;/h3&gt;

&lt;p&gt;This code snippet navigates to whatsmyuseragent.org in Chromium, Firefox and WebKit, and saves 3 screenshots.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;playwright&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;browser_type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firefox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webkit&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser_type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'http://whatsmyuseragent.org/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'example-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;browser_type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.png'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Evaluate in browser context - sync
&lt;/h3&gt;

&lt;p&gt;This code snippet navigates to example.com in Firefox and executes a script in the page context to determine the window dimensions.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;playwright&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firefox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'https://www.example.com/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dimensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'''() =&amp;gt; {
      return {
        width: document.documentElement.clientWidth,
        height: document.documentElement.clientHeight,
        deviceScaleFactor: window.devicePixelRatio
      }
    }'''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dimensions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Intercept network requests - async
&lt;/h3&gt;

&lt;p&gt;This code snippet sets up request routing for a Chromium page to log all network requests.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;playwright&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;async_playwright&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;async_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&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="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_and_continue_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;continue_&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

        &lt;span class="c1"&gt;# Log and continue all network requests
&lt;/span&gt;        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'**'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;log_and_continue_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'http://todomvc.com'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_event_loop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;run_until_complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Pytest integration
&lt;/h2&gt;

&lt;p&gt;For writing actual end-to-end tests its common to use a test runner. In the Python world, Pytest is very common and we're using in our example the official &lt;a href="https://github.com/microsoft/playwright-pytest"&gt;Playwright integration&lt;/a&gt; for it. Instead of using it manually, it provides features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have a separate new page and context for each test with Pytest fixtures&lt;/li&gt;
&lt;li&gt;Run your end-to-end tests on multiple browsers by a CLI argument&lt;/li&gt;
&lt;li&gt;Run them headful with the &lt;code&gt;--headful&lt;/code&gt; argument to debug them easily&lt;/li&gt;
&lt;li&gt;Using &lt;a href="https://github.com/pytest-dev/pytest-base-url"&gt;base-url&lt;/a&gt; to only use the relative URL in your Page.goto calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's Open Source  and available on &lt;a href="https://github.com/microsoft/playwright-pytest/"&gt;GitHub&lt;/a&gt; and installable with PIP:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install pytest pytest-playwright
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Pytest has the concept that you have fixtures that will pass the values inside which are specified by the parameter name. In our case, we use for that &lt;code&gt;page&lt;/code&gt; which will call the Playwright Pytest plugin to give us a page object.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_is_chromium&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://www.google.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input[name=q]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Playwright GitHub"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input[type=submit]"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waitForSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text=microsoft/Playwright"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can run it with &lt;code&gt;pytest&lt;/code&gt; or optionally specify multiple browsers to run the test on like &lt;code&gt;pytest --browser chromium --browser firefox --browser webkit&lt;/code&gt; which will run 3 tests in the end.&lt;/p&gt;

&lt;p&gt;For more detail information about the Pytest usage, you'll find the documentation on &lt;a href="https://github.com/microsoft/playwright-pytest#readme"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Playwright Python is still beta, but for small projects with are not used in production its worth it to try it out to see if you benefit from it compared to other automation libraries. If you encounter any bugs or find some missing features, feel free to file an issue on &lt;a href="https://github.com/microsoft/playwright-python"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/microsoft"&gt;
        microsoft
      &lt;/a&gt; / &lt;a href="https://github.com/microsoft/playwright-python"&gt;
        playwright-python
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Python version of the Playwright testing and automation library.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
🎭 &lt;a href="https://playwright.dev" rel="nofollow"&gt;Playwright&lt;/a&gt; for Python &lt;a href="https://pypi.python.org/pypi/playwright/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/448b0454f6350cc425e2d40f20d63fb732994d1e853d5db7ac7a0f0fa5e15e5a/68747470733a2f2f62616467652e667572792e696f2f70792f706c61797772696768742e737667" alt="PyPI version"&gt;&lt;/a&gt; &lt;a href="https://anaconda.org/Microsoft/playwright" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/376a0c5e576cc7c404b7d3bae5c8a0f0764635841eb9a8f339ccf894229fa04d/68747470733a2f2f696d672e736869656c64732e696f2f636f6e64612f762f6d6963726f736f66742f706c6179777269676874" alt="Anaconda version"&gt;&lt;/a&gt; &lt;a href="https://aka.ms/playwright-slack" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/84f2f075bb7742762e70685924cfc27c061cdc4c4f2d0b2997483cfe6c10d96d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6a6f696e2d736c61636b2d696e666f6d6174696f6e616c" alt="Join Slack"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Playwright is a Python library to automate &lt;a href="https://www.chromium.org/Home" rel="nofollow"&gt;Chromium&lt;/a&gt;, &lt;a href="https://www.mozilla.org/en-US/firefox/new/" rel="nofollow"&gt;Firefox&lt;/a&gt; and &lt;a href="https://webkit.org/" rel="nofollow"&gt;WebKit&lt;/a&gt; browsers with a single API. Playwright delivers automation that is &lt;strong&gt;ever-green&lt;/strong&gt;, &lt;strong&gt;capable&lt;/strong&gt;, &lt;strong&gt;reliable&lt;/strong&gt; and &lt;strong&gt;fast&lt;/strong&gt;. &lt;a href="https://playwright.dev/python/docs/why-playwright" rel="nofollow"&gt;See how Playwright is better&lt;/a&gt;.&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Linux&lt;/th&gt;
&lt;th&gt;macOS&lt;/th&gt;
&lt;th&gt;Windows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chromium 113.0.5672.53&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebKit 16.4&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firefox 112.0&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
Documentation&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://playwright.dev/python/docs/intro" rel="nofollow"&gt;https://playwright.dev/python/docs/intro&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
API Reference&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://playwright.dev/python/docs/api/class-playwright" rel="nofollow"&gt;https://playwright.dev/python/docs/api/class-playwright&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
Example&lt;/h2&gt;
&lt;div class="highlight highlight-source-python notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s1"&gt;playwright&lt;/span&gt;.&lt;span class="pl-s1"&gt;sync_api&lt;/span&gt; &lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-s1"&gt;sync_playwright&lt;/span&gt;

&lt;span class="pl-k"&gt;with&lt;/span&gt; &lt;span class="pl-en"&gt;sync_playwright&lt;/span&gt;() &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-s1"&gt;p&lt;/span&gt;:
    &lt;span class="pl-k"&gt;for&lt;/span&gt; &lt;span class="pl-s1"&gt;browser_type&lt;/span&gt; &lt;span class="pl-c1"&gt;in&lt;/span&gt; [&lt;span class="pl-s1"&gt;p&lt;/span&gt;.&lt;span class="pl-s1"&gt;chromium&lt;/span&gt;, &lt;span class="pl-s1"&gt;p&lt;/span&gt;.&lt;span class="pl-s1"&gt;firefox&lt;/span&gt;, &lt;span class="pl-s1"&gt;p&lt;/span&gt;.&lt;span class="pl-s1"&gt;webkit&lt;/span&gt;]:
        &lt;span class="pl-s1"&gt;browser&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;browser_type&lt;/span&gt;.&lt;span class="pl-en"&gt;launch&lt;/span&gt;()
        &lt;span class="pl-s1"&gt;page&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;browser&lt;/span&gt;.&lt;span class="pl-en"&gt;new_page&lt;/span&gt;()
        &lt;span class="pl-s1"&gt;page&lt;/span&gt;.&lt;span class="pl-en"&gt;goto&lt;/span&gt;(&lt;span class="pl-s"&gt;'http://whatsmyuseragent.org/'&lt;/span&gt;)
        &lt;span class="pl-s1"&gt;page&lt;/span&gt;.&lt;span class="pl-en"&gt;screenshot&lt;/span&gt;(&lt;span class="pl-s1"&gt;path&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;f'example-&lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-s1"&gt;browser_type&lt;/span&gt;.&lt;span class="pl-s1"&gt;name&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt;.png'&lt;/span&gt;)
        &lt;span class="pl-s1"&gt;browser&lt;/span&gt;.&lt;span class="pl-en"&gt;close&lt;/span&gt;()&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-python notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-s1"&gt;asyncio&lt;/span&gt;
&lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s1"&gt;playwright&lt;/span&gt;.&lt;span class="pl-s1"&gt;async_api&lt;/span&gt; &lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-s1"&gt;async_playwright&lt;/span&gt;
&lt;span class="pl-k"&gt;async&lt;/span&gt; &lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;main&lt;/span&gt;():
    &lt;span class="pl-k"&gt;async&lt;/span&gt; &lt;span class="pl-k"&gt;with&lt;/span&gt; &lt;span class="pl-en"&gt;async_playwright&lt;/span&gt;() &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-s1"&gt;p&lt;/span&gt;:
        &lt;span class="pl-k"&gt;for&lt;/span&gt; &lt;span class="pl-s1"&gt;browser_type&lt;/span&gt; &lt;span class="pl-c1"&gt;in&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/microsoft/playwright-python"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>playwright</category>
      <category>python</category>
      <category>selenium</category>
      <category>automation</category>
    </item>
    <item>
      <title>Using Playwright on Heroku</title>
      <dc:creator>Max Schmitt</dc:creator>
      <pubDate>Thu, 09 Jul 2020 22:09:23 +0000</pubDate>
      <link>https://dev.to/mxschmitt/using-playwright-on-heroku-127d</link>
      <guid>https://dev.to/mxschmitt/using-playwright-on-heroku-127d</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Simplifying the deployment process nowadays of an application is a huge benefit to focus on the actual development instead of DevOps related tasks to create and configure a server for example. &lt;a href="https://www.heroku.com" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt; provides such a platform for running various tech stacks and languages easily and exposes them to the web which is called Platform as a Service (PaaS).&lt;/p&gt;

&lt;p&gt;Using Playwright will give the developer the possibility to either use Playwright on &lt;a href="https://devcenter.heroku.com/articles/heroku-ci" rel="noopener noreferrer"&gt;Heroku CI&lt;/a&gt;, a Continuous Integration provider by Heroku, or using it on the Heroku platform on a normal Dyno. They can then perform tasks like end-to-end testing or making preview images of web pages by starting a headless browser instance and making a screenshot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;For the Heroku ecosystem, you need buildpacks, which can modify the system and hook into the compilation or test step of your application while it's building. For our case, we have to install the dependencies during the installation stage. Due to the requirement, that Heroku buildpacks are only allowed to modify the files inside the current working directory, the Playwright buildpack has to set an environment variable to store the browsers in the &lt;code&gt;node_modules&lt;/code&gt; folder instead of a global directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;There is already a buildpack available, which configures the system for Playwright. It will as required install the dependencies and adjust the Playwright configuration by setting an environment variable. You'll find it on &lt;a href="https://github.com/mxschmitt/heroku-playwright-buildpack" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to use this buildpack, set the buildpack URL in your application either via the CLI or the web interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku buildpacks:set https://github.com/mxschmitt/heroku-playwright-buildpack -a my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It's important, that it's ordered before the Node.js buildpack, otherwise, the browsers won't be installed correctly.&lt;/p&gt;

&lt;p&gt;Also, you can then adjust the environment variables of your application and set the &lt;code&gt;PLAYWRIGHT_BUILDPACK_BROWSERS&lt;/code&gt; environment variable to the browsers which you want to install. For example, &lt;code&gt;chromium&lt;/code&gt; to install Chromium only and save by that slug size. If you want to install more, separate them by a comma. WebKit is currently not yet supported, see on &lt;a href="https://github.com/mxschmitt/heroku-playwright-buildpack/issues/2" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; for more information about that.&lt;/p&gt;

&lt;p&gt;It's also common to only install the &lt;a href="https://playwright.dev/#version=v1.1.1&amp;amp;path=docs%2Finstallation.md&amp;amp;q=download-single-browser-binary" rel="noopener noreferrer"&gt;browser-specific NPM packages&lt;/a&gt;, which will reduce installation time and slug size on Heroku in the end.&lt;/p&gt;

&lt;p&gt;On the actual Playwright usage, it differs not much except that you have to run Chromium with the &lt;code&gt;--no-sandbox&lt;/code&gt; argument because on Heroku the Chromium Sandbox is not supported. For the full usage of Playwright, see on their official website &lt;a href="https://playwright.dev" rel="noopener noreferrer"&gt;playwright.dev&lt;/a&gt;.&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="c1"&gt;// Use the browser specific NPM package&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;chromium&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;playwright-chromium&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="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;// Start Chromium with the '--no-sandbox' argument&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="nf"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--no-sandbox&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newContext&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;page&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&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="nf"&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;http://whatsmyuseragent.org/&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="nf"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`chromium.png`&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="nf"&gt;close&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;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we went through how to set up your Heroku environment to install the needed Playwright dependencies. Now you can start using your own logic by controlling the browsers. A full example of how it will look like in the end, you'll find on &lt;a href="https://github.com/mxschmitt/heroku-playwright-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or on &lt;a href="https://heroku.playwright.tech" rel="noopener noreferrer"&gt;heroku.playwright.tech&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/playwright-community" rel="noopener noreferrer"&gt;
        playwright-community
      &lt;/a&gt; / &lt;a href="https://github.com/playwright-community/heroku-playwright-buildpack" rel="noopener noreferrer"&gt;
        heroku-playwright-buildpack
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Buildpack for running Playwright with Chromium and Firefox on Heroku.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Heroku Playwright Buildpack&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;This buildpack installs all the needed dependencies to use Playwright with Chromium and Firefox on Heroku.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://heroku.com/deploy?template=https://github.com/mxschmitt/heroku-playwright-example" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/dc2056acd0e6ff421bfc2b129417f4f832d626c61d1c083221211d8503a429f7/68747470733a2f2f7777772e6865726f6b7563646e2e636f6d2f6465706c6f792f627574746f6e2e737667" alt="Deploy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;For using this buildpack, you have to add the buildpack &lt;strong&gt;before&lt;/strong&gt; installing your Node.js dependencies.&lt;/p&gt;

&lt;div class="highlight highlight-text-adblock notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;heroku buildpacks:set https://github.com/mxschmitt/heroku-playwright-buildpack.git -a my-app&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For a full example, see &lt;a href="https://github.com/mxschmitt/heroku-playwright-example" rel="noopener noreferrer"&gt;here&lt;/a&gt; a usage with the Express library.&lt;/p&gt;

&lt;p&gt;It's common to use the &lt;code&gt;PLAYWRIGHT_BUILDPACK_BROWSERS&lt;/code&gt; environment variable which accepts a comma-separated list of the browser names (&lt;code&gt;chromium&lt;/code&gt;, &lt;code&gt;firefox&lt;/code&gt;, &lt;code&gt;webkit&lt;/code&gt;). By default, it's installing the dependencies for all the browsers. To only install Chromium dependencies for example, just set it to &lt;code&gt;chromium&lt;/code&gt;. This will reduce the slug size in the end too.&lt;/p&gt;

&lt;p&gt;You should also install the browser specific NPM packages like &lt;code&gt;playwright-chromium.&lt;/code&gt; to reduce the slug size.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Examples&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Chromium&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;For using Chromium, it's &lt;strong&gt;necessary&lt;/strong&gt; to use &lt;code&gt;chromiumSandbox: false&lt;/code&gt; in the launch options, since on Heroku is no support for the Chromium sandbox.&lt;/p&gt;

&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/playwright-community/heroku-playwright-buildpack" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>playwright</category>
      <category>javascript</category>
      <category>chromium</category>
      <category>heroku</category>
    </item>
    <item>
      <title>Using Jest with Playwright</title>
      <dc:creator>Max Schmitt</dc:creator>
      <pubDate>Mon, 06 Jul 2020 22:32:47 +0000</pubDate>
      <link>https://dev.to/mxschmitt/using-jest-with-playwright-28dh</link>
      <guid>https://dev.to/mxschmitt/using-jest-with-playwright-28dh</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0AxonYjY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/i09aqx5vsjweb5s9hj2r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0AxonYjY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/i09aqx5vsjweb5s9hj2r.png" alt="Alt Text" width="796" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having a test runner like Jest configured has a lot of benefits instead of writing your tests from scratch. It gives you out of the box the ability to focus a single test, providing a formatted output or a whole suite for common assertions called &lt;code&gt;expect&lt;/code&gt; in Jest. The framework is maintained by Facebook and is mostly the default choice for writing frontend or backend unit tests in the JavaScript ecosystem. It's framework agnostic and by that, you can test with it e.g. your express backend or your React components.&lt;/p&gt;

&lt;p&gt;The way how you should integrate Playwright into your project depends on your needs. Currently there two common ways of doing that. Either you can do it manually by using the hooks (&lt;code&gt;beforeAll&lt;/code&gt;, &lt;code&gt;afterAll&lt;/code&gt;, &lt;code&gt;beforeEach&lt;/code&gt;, &lt;code&gt;afterEach&lt;/code&gt;) or you use &lt;a href="https://github.com/playwright-community/jest-playwright"&gt;&lt;code&gt;jest-playwright&lt;/code&gt;&lt;/a&gt;. We recommend to use jest-playwright due it has features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-browser and device (like iPhones with given screen sizes) support&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://yarnpkg.com/en/package/jest-dev-server"&gt;jest-dev-server&lt;/a&gt; integration which can start your webserver like create-react-app before running the tests&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/playwright-community/expect-playwright"&gt;expect-playwright&lt;/a&gt; integration which provides common &lt;code&gt;expect&lt;/code&gt; helper functions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using &lt;code&gt;jest-playwright&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;To get started we first have to install the needed Node.js packages either over NPM or Yarn. It's common to install them as &lt;code&gt;devDependencies&lt;/code&gt; so they won't be installed on your production environments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -D jest jest-playwright-preset playwright
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Instead of installing all the browsers with the &lt;code&gt;playwright&lt;/code&gt; package, you can also just install e.g. &lt;code&gt;playwright-chromium&lt;/code&gt; to save bandwidth and storage on your system and have a faster installation time.&lt;/p&gt;

&lt;p&gt;Once we installed the dependencies, we have to create a Jest configuration to instruct Jest which preset should be used for which files. You can either reuse your existing configuration (&lt;code&gt;jest.config.js&lt;/code&gt;) or create a custom one and name it e.g. &lt;code&gt;jest.e2e.config.js&lt;/code&gt;. In the end, when you run your tests, you then have to specify the custom config file with the &lt;code&gt;-c&lt;/code&gt; flag. It's also common to separate your unit tests from the end-to-end tests with a separate configuration file.&lt;/p&gt;

&lt;p&gt;Inside the Jest configuration file you have to put:&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jest-playwright-preset&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;Also since this is a normal Jest configuration you can pass a &lt;code&gt;RegExp&lt;/code&gt; or glob pattern which determines the files which Jest should run. It's useful to use e.g. a custom folder or file suffix as a naming convention to separate your unit tests from the end-to-end tests. See here as a reference for the &lt;a href="https://jestjs.io/docs/en/configuration#testregex-string--arraystring"&gt;&lt;code&gt;testRegex&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://jestjs.io/docs/en/configuration#testmatch-arraystring"&gt;&lt;code&gt;testMatch&lt;/code&gt;&lt;/a&gt; setting.&lt;/p&gt;

&lt;p&gt;For the basic configuration this is already enough to get started. If you want to setup custom browsers, devices, screen resolutions or other launch settings for Playwright, then you need a custom Playwright configuration. Create a file called &lt;code&gt;jest-playwright.config.js&lt;/code&gt; in the root directory of your project to use the settings which are described in the &lt;a href="https://github.com/playwright-community/jest-playwright#configuration"&gt;jest-playwright documentation&lt;/a&gt;.&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;launchOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headless&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="na"&gt;contextOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ignoreHTTPSErrors&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="na"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;browsers&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;chromium&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;firefox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;devices&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;Keep in mind, that all the values in the configuration are optional and only showed here to give insights how it can be used in common testing setups.&lt;/p&gt;

&lt;p&gt;To run your tests you can add &lt;code&gt;jest&lt;/code&gt; or &lt;code&gt;jest -c config.e2e.config.js&lt;/code&gt; to your &lt;code&gt;scripts&lt;/code&gt; section of your &lt;code&gt;package.json&lt;/code&gt; to run them easier by using &lt;code&gt;npm run test&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example output how it will look like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASS browser: chromium src/e2e/platform.e2e.ts (23.729s)
Profile Settings
    ✓ should be redirected to login page if not logged in (568ms)
    ✓ should be able to login by email and password (404ms)
    ✓ should be able to see their email and name on account settings page (155ms)
    ✓ should be able to change their email and name on account settings page (562ms)
File handling
    ✓ should be able to import a CSV (705ms)
    ✓ should be able to view a files (742ms)
    ✓ should be able to delete a file (606ms)
    ✓ should be able to edit a file (806ms)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For more information about &lt;code&gt;jest-playwright&lt;/code&gt;, you'll find the full documentation on &lt;a href="https://github.com/playwright-community/jest-playwright"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Using it from scratch
&lt;/h2&gt;

&lt;p&gt;As you can see in the following code, we use the lifecycle hooks to launch a browser before all the tests which run and close them once all are done. For every test &lt;code&gt;beforeEach&lt;/code&gt; will create a new page as a global variable which will be used inside the tests. In this example we are visiting example.com and testing if the title of it contains &lt;code&gt;Example Domain&lt;/code&gt;.&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="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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;playwright&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&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;beforeAll&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&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="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;afterAll&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="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;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="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="o"&gt;=&amp;gt;&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="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;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;afterEach&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="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;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;it&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 work&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="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://www.example.com&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="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;title&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Example 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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the creation of the browser (&lt;code&gt;chromium.launch&lt;/code&gt;) or the creation of the page (&lt;code&gt;browser.newPage&lt;/code&gt;) you can passs options e.g. to start the browser as headful instead of headless to manually click around or to ignore invalid SSL certificates which is useful for local testing.&lt;/p&gt;
&lt;h2&gt;
  
  
  Writing tests
&lt;/h2&gt;

&lt;p&gt;Once you have a setup in place, you can then follow up by using tools like &lt;a href="https://github.com/playwright-community/expect-playwright"&gt;&lt;code&gt;expect-playwright&lt;/code&gt;&lt;/a&gt; (pre-installed with jest-playwright) which gives you a few utility methods on your Jest matching class to use it like that:&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="nx"&gt;it&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...&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// before&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;waitForSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#foo&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;textContent&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;$eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&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;textContent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;stringContaining&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// after by using expect-playwright&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;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#foo&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;my 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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Or you are also able to assert the value of input DOM nodes on the page:&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="nx"&gt;it&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...&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="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;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;toEqualValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#my-element&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;Playwright&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;For more information about that we recommend to dig into these projects/articles to find out more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Different selector engines on &lt;a href="https://playwright.dev/#version=v1.0.2&amp;amp;path=docs%2Fcore-concepts.md&amp;amp;q=selectors"&gt;playwright.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Utility helper functions for Jest: &lt;a href="https://github.com/playwright-community/expect-playwright"&gt;&lt;code&gt;expect-playwright&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Run if needed your backend or frontend before your Jest tests with &lt;a href="https://github.com/playwright-community/jest-playwright#start-a-server"&gt;jest-dev-server&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/playwright-community"&gt;
        playwright-community
      &lt;/a&gt; / &lt;a href="https://github.com/playwright-community/jest-playwright"&gt;
        jest-playwright
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Running tests using Jest &amp;amp; Playwright 
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Jest Playwright&lt;/h1&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/playwright-community/jest-playwright/workflows/Node.js/badge.svg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GsqrUKKG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/playwright-community/jest-playwright/workflows/Node.js/badge.svg" alt="CI"&gt;&lt;/a&gt;
&lt;a href="https://coveralls.io/github/playwright-community/jest-playwright?branch=master" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/f38c9e4a513f0094c0eddb9581f8d5c1b1f98da468a0759040a38d69d6c496da/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f706c61797772696768742d636f6d6d756e6974792f6a6573742d706c61797772696768742f62616467652e7376673f6272616e63683d6d6173746572" alt="Coverage Status"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a889cab3bb93bc3def7b57caf844213eb1209dcbf62e66e3337a4bb5edbe8cde/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f6a6573742d706c61797772696768742d707265736574"&gt;&lt;img src="https://camo.githubusercontent.com/a889cab3bb93bc3def7b57caf844213eb1209dcbf62e66e3337a4bb5edbe8cde/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f6a6573742d706c61797772696768742d707265736574" alt="npm"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
⚠️ We recommend the official &lt;a href="https://playwright.dev/docs/test-intro" rel="nofollow"&gt;Playwright test-runner (@playwright/test)&lt;/a&gt; ⚠️
&lt;/h2&gt;
&lt;p&gt;It's more flexible, lightweight, optimized for Playwright, and has TypeScript support out of the box. This doesn't mean, that we stop with maintaining this package.&lt;/p&gt;

&lt;p&gt;Running your tests using &lt;a href="https://github.com/facebook/jest"&gt;Jest&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/microsoft/playwright"&gt;Playwright&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install -D jest jest-playwright-preset playwright&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Also you can use &lt;code&gt;jest-playwright-preset&lt;/code&gt; with specific playwright packages
&lt;code&gt;playwright-webkit&lt;/code&gt;, &lt;code&gt;playwright-chromium&lt;/code&gt; and &lt;code&gt;playwright-firefox&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install -D jest jest-playwright-preset playwright-firefox&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Node.js 14+&lt;/li&gt;
&lt;li&gt;Playwright 1.2.0+&lt;/li&gt;
&lt;li&gt;Jest 28+&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Usage&lt;/h2&gt;
&lt;p&gt;Update your Jest configuration, either:&lt;/p&gt;
&lt;p&gt;with &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-json notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-ent"&gt;"jest"&lt;/span&gt;: {
  &lt;span class="pl-ent"&gt;"preset"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;jest-playwright-preset&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
}&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;or with &lt;code&gt;jest.config.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-smi"&gt;module&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;exports&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-c1"&gt;preset&lt;/span&gt;: &lt;span class="pl-s"&gt;'jest-playwright-preset'&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;And add the Jest command as in the script section of your &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-json notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;{
  &lt;span class="pl-ent"&gt;"scripts"&lt;/span&gt;: {
    &lt;span class="pl-ent"&gt;"test"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;jest&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
  }
}&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Now you can use Playwright in your tests:&lt;/p&gt;
&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;// example.test.js&lt;/span&gt;
&lt;span class="pl-en"&gt;beforeAll&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;async&lt;/span&gt; &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/playwright-community/jest-playwright"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>playwright</category>
      <category>javascript</category>
      <category>jest</category>
      <category>e2e</category>
    </item>
    <item>
      <title>Automate Microsoft Edge and Google Chrome with Playwright</title>
      <dc:creator>Max Schmitt</dc:creator>
      <pubDate>Mon, 06 Jul 2020 22:29:53 +0000</pubDate>
      <link>https://dev.to/mxschmitt/automate-microsoft-edge-and-google-chrome-with-playwright-4ppi</link>
      <guid>https://dev.to/mxschmitt/automate-microsoft-edge-and-google-chrome-with-playwright-4ppi</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Testing a web application with end-to-end tests on the actually supported browsers is a real benefit to ensure that the application works as expected for example directly in the Continuous Integration workflow before it is merged and deployed. Playwright provides already full support for Chromium, Firefox, and WebKit out of the box without installing the browsers manually, but since most of the users out there use Google Chrome or Microsoft Edge instead of the Open Source Chromium variant, it's in some scenarios safer to use them to emulate a more real-life browser environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  General
&lt;/h2&gt;

&lt;p&gt;Since these browsers are based on the Chromium browser, Playwright interacts with them over the &lt;a href="https://chromedevtools.github.io/devtools-protocol/"&gt;Chrome DevTools Protocol&lt;/a&gt; to open new tabs, click on elements or execute JavaScript. Due to this core requirement, we have to use a recent version (daily build - Canary) of them to ensure that the needed APIs schemas are matching and existing. To use them we have to only adjust the &lt;a href="https://playwright.dev/#version=v1.1.1&amp;amp;path=docs%2Fapi.md&amp;amp;q=browsertypelaunchoptions--options-executablepath"&gt;executable path&lt;/a&gt; option which Playwright will use to launch the browsers.&lt;/p&gt;

&lt;p&gt;On macOS systems, the browsers are installed in the &lt;code&gt;/Applications&lt;/code&gt; directory, where you have inside the related binaries. For Linux, the browsers are commonly installed in the &lt;code&gt;/usr/bin&lt;/code&gt; directory, you'll find some examples below. On Windows systems, the browsers are installed in the &lt;code&gt;C:\Program Files (x86)\&lt;/code&gt; directory.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/Applications/Microsoft\ Edge\ Canary.app/Contents/MacOS/Microsoft\ Edge\ Canary&lt;/code&gt; - Microsoft Edge Canary on macOS&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary&lt;/code&gt; - Google Chrome Canary on macOS&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/usr/bin/google-chrome-unstable&lt;/code&gt; - Google Chrome Canary on Ubuntu&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;C:\Users\&amp;lt;username&amp;gt;\AppData\Local\Google\Chrome SxS\Application\chrome.exe&lt;/code&gt; - Google Chrome Canary on Windows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/Applications/Brave Browser Nightly.app/Contents/MacOS/Brave Browser Nightly&lt;/code&gt; - Brave Nightly on macOS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: To easier find out the executable path of the browsers, you can open the version page of the related browser. This would be &lt;code&gt;edge://version&lt;/code&gt;, &lt;code&gt;chrome://version&lt;/code&gt; or &lt;code&gt;brave://version&lt;/code&gt; depending your browser. On this special site, you'll find the correct executable path if you have a GUI installed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browsers
&lt;/h2&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;playwright&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;playwright-core&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="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;playwright&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;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;headless&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;executablePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/Applications/Microsoft\ Edge\ Canary.app/Contents/MacOS/Microsoft\ Edge\ Canary`&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;page&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newPage&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;setContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;input id="foo"&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;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;#foo&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;keksstar&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="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;content&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;screenshot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;screenshot.png&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="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;evaluate&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="c1"&gt;// -&amp;gt; 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.0 Safari/537.36 Edg/85.0.563.0'&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the next couple of examples, we're gonna focus on the different browsers, which operating systems they support, where you can download them, and what executable path you need to set. This example demonstrates the basic usage by specifying the &lt;code&gt;executablePath&lt;/code&gt; which launches Microsoft Edge instead of the normal Chromium. Also, we're using the &lt;a href="https://www.npmjs.com/package/playwright-core"&gt;&lt;code&gt;playwright-core&lt;/code&gt;&lt;/a&gt; package, which only installs the library instead of downloading the browsers which we don't need in our case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Google Chrome
&lt;/h3&gt;

&lt;p&gt;Google Chrome is available for all the major operating systems and can be downloaded on the &lt;a href="https://www.google.com/chrome/canary/"&gt;official website&lt;/a&gt;. To use Playwright, we need a recent &lt;strong&gt;Canary build&lt;/strong&gt;. For Linux, it is common to install it via their APT repository, see e.g. this &lt;a href="https://askubuntu.com/a/510186"&gt;guide&lt;/a&gt; also keep in mind then to install the Canary variant: &lt;code&gt;apt install google-chrome-unstable&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Microsoft Edge
&lt;/h3&gt;

&lt;p&gt;Microsoft Edge is only available for macOS and Windows and can be downloaded on the &lt;a href="https://www.microsoftedgeinsider.com/download/canary/"&gt;official website&lt;/a&gt;. Until now (Mid 2020) no Linux build was released. (It was announced in 2019 and confirmed the existence in Mid 2020 on their yearly Microsoft Build 2020 conference.) To use Playwright, we need a recent &lt;strong&gt;Canary build&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Brave
&lt;/h3&gt;

&lt;p&gt;Brave itself does not rely on the official Chromium release schedule, that's why their latest versions are not the same as Chromium. Because of that it's not guaranteed, that all Playwright functionality is working out of the box. If you want to still try it out, you can obtain their &lt;strong&gt;Nightly version&lt;/strong&gt; on their &lt;a href="https://brave.com/de/download-nightly/"&gt;official website&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Chromium browsers
&lt;/h3&gt;

&lt;p&gt;Other popular Chromium-based browsers like &lt;strong&gt;Vivaldi&lt;/strong&gt; and &lt;strong&gt;Opera&lt;/strong&gt; are not yet working which is tracked in &lt;a href="https://github.com/microsoft/playwright/issues/647#issuecomment-652664342"&gt;this&lt;/a&gt; GitHub issue. For Firefox and WebKit, you have to use the attached&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this blog article, we went through an overview of how to use other Chromium-based browsers and control them with the Playwright library. We strongly advise, that these browsers will only be used when you need to test a very specific browser feature. For follow up usage and documentation about Playwright features, you'll find more on their official website &lt;a href="https://playwright.dev"&gt;playwright.dev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>javascript</category>
      <category>chromium</category>
      <category>chrome</category>
    </item>
    <item>
      <title>Tracking frontend coverage of your e2e tests with Playwright</title>
      <dc:creator>Max Schmitt</dc:creator>
      <pubDate>Mon, 06 Jul 2020 22:26:59 +0000</pubDate>
      <link>https://dev.to/mxschmitt/tracking-frontend-coverage-of-your-e2e-tests-with-playwright-5126</link>
      <guid>https://dev.to/mxschmitt/tracking-frontend-coverage-of-your-e2e-tests-with-playwright-5126</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;End-to-End tests play a critical role in modern software development nowadays. As Guillermo Rauch (CEO of Vercel, formerly ZEIT) outlined in his &lt;a href="https://rauchg.com/2020/develop-preview-test"&gt;last blog article&lt;/a&gt; about software development, software engineering teams attend to prioritize and implement End-to-End tests over normal unit tests. As an example, they start the real application setup and their dependencies to use it as a real user by browser emulation instead of testing e.g. React components and comparing input and output of them. Especially integrated into the Continuous Integration lifecycle with a CI provider like GitHub Actions ensures always, that the End-to-End tests are passing before they are merged and deployed to production.&lt;/p&gt;

&lt;p&gt;With this mindset, its especially good for the team to have a way to track the actual coverage down to the code level, to see which functions and pages were executed with the End-to-End tests in our case with Playwright. Doing this manually is very time inefficient and error-prone. So we are focusing in this article, what tools and components you need, to automatically track the coverage and send it to your preferred coverage visualization provider like Codecov or Coveralls to have it visualized it like &lt;a href="https://coveralls.io/builds/31569579/source?filename=create-react-app-coverage/src/App.tsx"&gt;here&lt;/a&gt; on Coveralls.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2NcsKyBT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/un3z6r8rzcn2ixkirwqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2NcsKyBT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/un3z6r8rzcn2ixkirwqk.png" alt="End-to-End test coverage on Coveralls" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;There are multiple ways, to get the coverage data of your tests, you can use the &lt;a href="https://playwright.dev/#version=v1.1.1&amp;amp;path=docs%2Fapi.md&amp;amp;q=class-chromiumcoverage"&gt;Chrome specific coverage tracking feature&lt;/a&gt;, which gets the coverage data directly from the V8 runtime, but this is browser-specific and has no support for 1:many source-maps if e.g. your bundle is minified it wouldn't yet work, see &lt;a href="https://github.com/istanbuljs/v8-to-istanbul/issues/21"&gt;here&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;In this tutorial, we are focusing on the usage with the Babel plugin &lt;a href="https://github.com/istanbuljs/babel-plugin-istanbul"&gt;babel-plugin-istanbul&lt;/a&gt; which you have to add during the build process (transcompilation) to your project. This will hold then the coverage data in a global variable called &lt;code&gt;__coverage__&lt;/code&gt; on the window object. Jest-Playwright, will automatically take care of storing and merging the coverage data. If you want to do it manually, you have to store it after each reload and page navigation because it's stored on the Window object which is not persistent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Babel
&lt;/h2&gt;

&lt;p&gt;The configuration with Babel depends on each application. In general its framework agnostic, so it can be used with e.g. React, Vue or Angular. In our example, we are using a React application with &lt;code&gt;create-react-app&lt;/code&gt; which per default does not accept any additional Babel plugins, that's why we have to use a third-party tool called &lt;a href="https://github.com/timarney/react-app-rewired#how-to-rewire-your-create-react-app-project"&gt;react-app-rewired&lt;/a&gt; to make it configurable. So in this case we have to create a file called &lt;code&gt;config-overrides.js&lt;/code&gt;, to add the &lt;a href="https://github.com/istanbuljs/babel-plugin-istanbul"&gt;babel-plugin-istanbul&lt;/a&gt; plugin there. See &lt;a href="https://github.com/playwright-community/playwright-jest-examples/blob/master/create-react-app-coverage/config-overrides.js"&gt;here&lt;/a&gt; for the full file on GitHub and &lt;a href="https://github.com/timarney/react-app-rewired#how-to-rewire-your-create-react-app-project"&gt;here&lt;/a&gt; for getting started with react-app-rewired.&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;override&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addBabelPlugins&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;customize-cra&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;override&lt;/span&gt;&lt;span class="p"&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;E2E_TESTS&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;addBabelPlugins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;babel-plugin-istanbul&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;It's common to only add the babel plugin when you actually need the coverage information, for that we have there the test on the &lt;code&gt;E2E_TESTS&lt;/code&gt; environment variable in place which we only set, when we bundle the web application for the end-to-end tests.x&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up jest-playwright
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;jest-playwright&lt;/code&gt; acts as a Jest environment which provides a Playwright &lt;a href="https://playwright.dev/#version=v1.1.1&amp;amp;path=docs%2Fapi.md&amp;amp;q=class-page"&gt;Page&lt;/a&gt; instance for each test. After installing it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -D jest jest-playwright-preset playwright
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can enable it in your Jest configuration &lt;code&gt;jest.config.js&lt;/code&gt;:&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jest-playwright-preset&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;For a full reference for &lt;code&gt;jest-playwright&lt;/code&gt; check out the official &lt;a href="https://github.com/playwright-community/jest-playwright#readme"&gt;docs&lt;/a&gt;. Its also recommended using a separate Jest configuration for the e2e tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Collecting coverage with jest-playwright
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;jest-playwright&lt;/code&gt; will provide a method for saving the test coverage after each test and merging it after all the tests are completed. First you have to enable the coverage collection in the configuration in the &lt;code&gt;jest-playwright.config.js&lt;/code&gt; file as follows:&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="c1"&gt;// https://github.com/playwright-community/jest-playwright/#configuration&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;browsers&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;chromium&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;firefox&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;webkit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;serverOptions&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="s2"&gt;npm start&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;launchTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;debug&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="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;E2E_TESTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&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;collectCoverage&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we are also using the &lt;a href="https://github.com/playwright-community/jest-process-manager#readme"&gt;&lt;code&gt;jest-process-manager&lt;/code&gt;&lt;/a&gt; to automatically start the React application before we are running the Jest test suite.&lt;/p&gt;

&lt;p&gt;Once this is setup, &lt;code&gt;jest-playwright&lt;/code&gt;, will start the development server and save the coverage data for all the pages which you are using inside the tests.&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="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="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;http://localhost:3000&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use Turquoise as a default background color&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="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;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;toHaveSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text=#1abc9c&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use Red as a background color&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="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;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text=Red&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;toHaveSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text=#e74c3c&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;At that point, you can run your tests as usual with the &lt;code&gt;jest&lt;/code&gt; command which is ideally configured in the script section of your package.json. When everything was successfully configured, you will see that the coverage data was successfully written to the file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UaSQ_bJw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/cocx5z0uquw9b0ngl6c3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UaSQ_bJw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/cocx5z0uquw9b0ngl6c3.png" alt="Command line output of jest-playwright" width="581" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The output will be placed under the &lt;code&gt;.nyc_output/coverage.json&lt;/code&gt; path, so it can be consumed using the &lt;a href="https://github.com/istanbuljs/nyc#readme"&gt;Istanbul command line interface&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualize it using NYC
&lt;/h2&gt;

&lt;p&gt;Once this is done, you can directly work with your coverage data by using the &lt;code&gt;npx nyc report --reporter=html&lt;/code&gt; command which will create an HTML website in the &lt;code&gt;coverage&lt;/code&gt; directory. You can then open the &lt;code&gt;coverage/index.html&lt;/code&gt; file with your favorite browser to analyse it which will look in our example like as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1C2dFYnY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/9znfn07riwhhm378cpn4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1C2dFYnY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/9znfn07riwhhm378cpn4.png" alt="Istanbul HTML coverage report" width="620" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Uploading it to Coveralls/Codecov
&lt;/h2&gt;

&lt;p&gt;It depends now on the coverage provider. Coveralls for example needs the &lt;code&gt;lcov&lt;/code&gt; data format which is different from the format which we have generated (&lt;code&gt;coverage.json&lt;/code&gt;). By using the &lt;code&gt;nyc report --reporter=lcovonly&lt;/code&gt; command you can convert it to the lcov data format which will save the file under &lt;code&gt;coverage/lcov.info&lt;/code&gt;. It's also useful to add it with the &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; syntax in the script section of your package.json to always generate the lcov data after running the test command.&lt;/p&gt;

&lt;p&gt;Codecov on the other hand will accept the &lt;code&gt;coverage.json&lt;/code&gt; file, so there is no need to convert it.&lt;/p&gt;

&lt;p&gt;It also depends on the CI provider of your choice to upload it to the coverage provider. For GitHub, it's recommended to use the available official GitHub Actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/marketplace/actions/coveralls-github-action"&gt;Coveralls GitHub Action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/marketplace/actions/codecov"&gt;Codecov GitHub Action&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we went through the setup which is required to configure Playwright and Jest for storing and merging the coverage data of your End-to-End tests. You'll find the full repository &lt;a href="https://github.com/playwright-community/playwright-jest-examples/tree/master/create-react-app-coverage"&gt;on GitHub&lt;/a&gt; as a reference on GitHub.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>javascript</category>
      <category>coverage</category>
      <category>codecov</category>
    </item>
    <item>
      <title>Generate Open Graph images on-demand with Next.js on Vercel</title>
      <dc:creator>Max Schmitt</dc:creator>
      <pubDate>Mon, 06 Jul 2020 22:24:00 +0000</pubDate>
      <link>https://dev.to/mxschmitt/generate-open-graph-images-on-demand-with-next-js-on-vercel-3lp8</link>
      <guid>https://dev.to/mxschmitt/generate-open-graph-images-on-demand-with-next-js-on-vercel-3lp8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Images in social media like Twitter or Facebook are very important to give the user directly an overview of your content, topics, and a preview of it. Designing this manually takes usually time and knowledge in photo editing software like Adobe Photoshop or Gimp. In this tutorial, we'll cover, how to generate automatically preview thumbnails using Playwright and the Next.js framework. In general, this technique is framework agnostic and can be used with other alternative frameworks which support serverless functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration
&lt;/h2&gt;

&lt;p&gt;The concept needs to be integrated into two parts of your application. The main layout or component that renders your pages needs to reference to your AWS Lambda function path. Due to the requirement of Twitter, that they need a full URL, we have to prefix the path with it. In Vercel, you have to manually add the &lt;code&gt;VERCEL_URL&lt;/code&gt; environment variable to your project for building the full URL.&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="c1"&gt;// Vercel specific&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getAbsoluteURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&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;baseURL&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;VERCEL_URL&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`https://&lt;/span&gt;&lt;span class="p"&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;VERCEL_URL&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;baseURL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we determine the path of the current page by using the &lt;a href="https://nextjs.org/docs/api-reference/next/router#userouter"&gt;&lt;code&gt;useRouter&lt;/code&gt;&lt;/a&gt; React hook, this will be passed over to the servless function via a GET parameter. Also this component has the option to pass an existing image as a prop over which will be preferred to only use the generated screenshot as a fallback.&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;MyComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;image&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;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&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;searchParams&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;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`/api/thumbnail?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;searchParams&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="c1"&gt;// Open Graph &amp;amp; Twitter images need a full URL including domain&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullImageURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getAbsoluteURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&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;Head&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;meta&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;og:image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;fullImageURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&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="nx"&gt;meta&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;twitter:image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;fullImageURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&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;/Head&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;The last step is to use something like &lt;a href="https://github.com/nfl/react-helmet"&gt;&lt;code&gt;react-helmet&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://nextjs.org/docs/api-reference/next/head"&gt;&lt;code&gt;next/head&lt;/code&gt;&lt;/a&gt; for Next.js to write the full image URL to the DOM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serverless function
&lt;/h2&gt;

&lt;p&gt;Since the page is now referencing from the content over to the AWS Lamda function, we have to implement the automated screenshot generation. Currently, the only way for using Playwright on AWS Lamda is to use the &lt;a href="https://github.com/JupiterOne/playwright-aws-lambda"&gt;&lt;code&gt;playwright-aws-lambda&lt;/code&gt;&lt;/a&gt; package which uses a custom-built Chromium version under the hood.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Serverless functions&lt;/em&gt; are a standardized way of exporting an HTTP handler in a file which then will be available by the given file path. They are available in plenty of languages including Node.js, Go, and Python.&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;playwright&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-aws-lambda&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;getAbsoluteURL&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;utils/utils&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="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="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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Start the browser with the AWS Lambda wrapper (playwright-aws-lambda)&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;playwright&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;launchChromium&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// Create a page with the Open Graph image size best practise&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;630&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// Generate the full URL out of the given path (GET parameter)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getAbsoluteURL&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;query&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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;goto&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="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screenshot&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;png&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;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="c1"&gt;// Set the s-maxage property which caches the images then on the Vercel edge&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;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cache-Control&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;s-maxage=31536000, stale-while-revalidate&lt;/span&gt;&lt;span class="dl"&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;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&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;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// write the image to the response with the specified Content-Type&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;end&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Serverless function will in a nutshell launch the special Chromium instance, navigate to the given URL, create a screenshot, and then writes it to the response.&lt;/p&gt;

&lt;p&gt;The entire source code for the &lt;a href="https://github.com/playwright-community/playwright-community/blob/e747e467a66d81a95f4013f683f7306050da70ad/layouts/main.tsx#L16-L21"&gt;page header&lt;/a&gt; and the &lt;a href="https://github.com/playwright-community/playwright-community/blob/e747e467a66d81a95f4013f683f7306050da70ad/pages/api/thumbnail.ts#L1-L24"&gt;Serverless function&lt;/a&gt; is available on &lt;a href="https://github.com/playwright-community/playwright-community"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outcome
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AOXcL0_l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/leugx02jikgehv5xdk52.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AOXcL0_l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/leugx02jikgehv5xdk52.png" width="800" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To summarize it, we created now the logic to generate dynamic Open Graph thumbnail images which will be generated on-demand if no custom image was given. They are automatically updated and cached on the edge of Vercel's webservers and look like the example above in our case.&lt;/p&gt;

&lt;p&gt;Currently, it's only possible to use Chromium with a third-party library on AWS Lambda out of the box. For more information about the further progress, see &lt;a href="https://github.com/microsoft/playwright/issues/2404"&gt;here&lt;/a&gt; in the GitHub issue.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>javascript</category>
      <category>nextjs</category>
      <category>lambda</category>
    </item>
    <item>
      <title>Record your Playwright browser with playwright-video</title>
      <dc:creator>Max Schmitt</dc:creator>
      <pubDate>Mon, 06 Jul 2020 22:15:36 +0000</pubDate>
      <link>https://dev.to/mxschmitt/using-jest-with-playwright-40lh</link>
      <guid>https://dev.to/mxschmitt/using-jest-with-playwright-40lh</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Video recordings of your end-to-end test case execution can play a critical role. Not only would these help in debugging issues more efficiently, but you can use recorded videos to show test execution activities to your team and internal stakeholders. Additionally, these recorded videos can be added to your CI pipeline artifacts if a test is failing.&lt;/p&gt;

&lt;p&gt;Currently, there is no native way of recording a session with Playwright's standards. But the &lt;a href="https://github.com/qawolf/qawolf"&gt;QAWolf&lt;/a&gt; team has created for that a third party library to record Chromium based sessions. It's based on &lt;a href="https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-startScreencast"&gt;screencast feature&lt;/a&gt; which Chromium will expose over the Chrome DevTools Protocol.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;To install it, you need the actual package and an available installation of ffmpeg. For most cases &lt;code&gt;@ffmpeg-installer/ffmpeg&lt;/code&gt; should be enough which will download a precompiled binary of ffmpeg when you install your NPM dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i -D playwright-video @ffmpeg-installer/ffmpeg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Basic usage
&lt;/h2&gt;

&lt;p&gt;It's implemented per Page level, that means you can have a recording for a single page which you start and stop as soon as you are done.&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="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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;playwright-chromium&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;saveVideo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;playwright-video&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newContext&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;page&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newPage&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;capture&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;saveVideo&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recording.mp4&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;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://google.com&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;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://apple.com&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;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://github.com&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;capture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have called the &lt;a href="https://github.com/qawolf/playwright-video#api"&gt;&lt;code&gt;saveVideo&lt;/code&gt;&lt;/a&gt; function by providing the page, the browser session will be recorded and stored on the given filepath until you call the stop function. In the example above Playwright visits a few websites and then closes the session.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage with &lt;code&gt;jest-playwright&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;To integrate &lt;code&gt;jest-playwright&lt;/code&gt; with &lt;code&gt;playwright-video&lt;/code&gt; you can use the available utility methods to hook into the startup and teardown of the Playwright session.&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;saveVideo&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-video&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;capture&lt;/span&gt;
&lt;span class="nx"&gt;beforeAll&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;capture&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;saveVideo&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recording.mp4&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;afterAll&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="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;capture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&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;Profile Settings&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;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;can visit a few web pages successfully&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="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://google.com&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;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://apple.com&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;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://github.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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shown code does the same as the other implementation and navigates by that through three different websites. The recording will also be stored in the &lt;code&gt;recording.mp4&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;A complete working implementation of &lt;code&gt;playwright-video&lt;/code&gt; with &lt;code&gt;jest-playwright&lt;/code&gt; as a reference you also find in the official examples repository of &lt;a href="https://github.com/playwright-community/playwright-jest-examples"&gt;&lt;code&gt;jest-playwright&lt;/code&gt;&lt;/a&gt; project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration in GitHub Actions
&lt;/h2&gt;

&lt;p&gt;The big benefit of having recordings in place as mentioned is for example to debug failed or flaky tests easier. In most cases, you run the end-to-end tests in CI environments like GitHub Actions. Since we've already seen the integration with &lt;code&gt;jest-playwright&lt;/code&gt;, we only have to adjust our CI pipeline configuration to upload the recording if the end-to-end tests are failing. For GitHub Actions a minimal example would look like that:&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;e2e&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use Node.js&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;13.x&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Node.js dependencies&lt;/span&gt;
      &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run e2e tests&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run test:e2e&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v2&lt;/span&gt;
      &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ failure() || success() }}&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Chromium Recording&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;recording.mp4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example after the general steps (checkout, install Node.js) are running the end-to-end tests as usual via &lt;code&gt;jest-playwright&lt;/code&gt; (we call Jest in the &lt;code&gt;package.json&lt;/code&gt; script section) will be triggered via &lt;code&gt;npm run test:e2e&lt;/code&gt;. By adding the if condition to the action for uploading the artifacts, we instruct the CI provider to upload the recording also if the pipeline has failed. You then can download the file once you open the GitHub Action run on GitHub itself. For more information check out the official action to upload files &lt;a href="https://github.com/actions/upload-artifact#where-does-the-upload-go"&gt;actions/checkout&lt;/a&gt; on GitHub.&lt;/p&gt;




&lt;p&gt;Playwright itself has on his &lt;a href="https://github.com/microsoft/playwright/issues/1914"&gt;roadmap&lt;/a&gt; for the next features in the upcoming few releases, that a native screencast implementation will follow. This will give us cross-browser support and probably a smoother video in the end.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>javascript</category>
      <category>testing</category>
      <category>jest</category>
    </item>
  </channel>
</rss>
