<?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: Swikriti Tripathi</title>
    <description>The latest articles on DEV Community by Swikriti Tripathi (@swikritit).</description>
    <link>https://dev.to/swikritit</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%2F764966%2Fddb03218-6124-42a0-a28d-4d27e5316fd7.jpeg</url>
      <title>DEV Community: Swikriti Tripathi</title>
      <link>https://dev.to/swikritit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/swikritit"/>
    <language>en</language>
    <item>
      <title>"Wondering which test automation framework to choose? 🤔 Here's my detailed blog comparing Cypress and Playwright for speed and efficiency in local and CI environments! 🚀 #Cypress #Playwright #AutomationTesting"</title>
      <dc:creator>Swikriti Tripathi</dc:creator>
      <pubDate>Thu, 09 Jan 2025 11:08:11 +0000</pubDate>
      <link>https://dev.to/swikritit/wondering-which-test-automation-framework-to-choose-heres-my-detailed-blog-comparing-cypress-b16</link>
      <guid>https://dev.to/swikritit/wondering-which-test-automation-framework-to-choose-heres-my-detailed-blog-comparing-cypress-b16</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/swikritit" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F764966%2Fddb03218-6124-42a0-a28d-4d27e5316fd7.jpeg" alt="swikritit"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/swikritit/comparing-test-execution-speed-of-modern-test-automation-frameworks-cypress-vs-playwright-3hg8" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Comparing Test Execution Speed of Modern Test Automation Frameworks: Cypress vs Playwright&lt;/h2&gt;
      &lt;h3&gt;Swikriti Tripathi ・ Jan 9&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#playwright&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#cypress&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#automation&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Comparing Test Execution Speed of Modern Test Automation Frameworks: Cypress vs Playwright</title>
      <dc:creator>Swikriti Tripathi</dc:creator>
      <pubDate>Thu, 09 Jan 2025 10:55:44 +0000</pubDate>
      <link>https://dev.to/swikritit/comparing-test-execution-speed-of-modern-test-automation-frameworks-cypress-vs-playwright-3hg8</link>
      <guid>https://dev.to/swikritit/comparing-test-execution-speed-of-modern-test-automation-frameworks-cypress-vs-playwright-3hg8</guid>
      <description>&lt;h2&gt;
  
  
  Comparing Test Execution Speed: Cypress vs Playwright for Modern Test Automation Frameworks
&lt;/h2&gt;

&lt;p&gt;As the testing world increasingly shifts towards automation, tests must run efficiently in a CI/CD environment. Quick feedback on the latest code changes is critical—nobody wants to wait an hour or more to see if their pull request passes. Additionally, CI/CD services can be costly, so minimizing execution time directly translates to saving money. Every millisecond counts in this modern testing landscape.&lt;/p&gt;

&lt;p&gt;With this in mind, I experimented to compare the test execution speed of two of today’s most popular modern test automation frameworks: &lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt; and &lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're not already familiar with these frameworks, here's a brief introduction to each:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cypress&lt;/strong&gt; is a front-end testing tool designed for modern web applications. Initially built for end-to-end testing, it has since expanded its capabilities to include component testing, accessibility testing, and more. Cypress supports test scripting in JavaScript and TypeScript and offers compatibility with Chromium-based browsers, Firefox, and WebKit (experimental). It operates directly in the browser, providing a seamless developer experience with features like built-in debugging, automatic waiting, and real-time reloading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Playwright&lt;/strong&gt; is an end-to-end testing framework for modern web applications that supports multiple programming languages, including JavaScript, TypeScript, Python, C#, and Java. It fully supports all major browser engines—Chromium, WebKit, and Firefox—making it an excellent choice for cross-browser testing. With powerful features like multiple browser context handling, parallel execution, and robust language integrations, Playwright is ideal for scalable and efficient test automation in diverse environments.&lt;/p&gt;

&lt;p&gt;For this project, I wrote end-to-end tests in a TDD style using both frameworks for the &lt;a href="https://www.saucedemo.com/v1/index.html" rel="noopener noreferrer"&gt;Swaglabs V1 demo website&lt;/a&gt;. You can find the test scripts in &lt;a href="https://github.com/SwikritiT/swaglabs-tests" rel="noopener noreferrer"&gt;my GitHub repository here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I created three test files for each framework, covering the following functionalities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logging in&lt;/li&gt;
&lt;li&gt;Adding items to the cart&lt;/li&gt;
&lt;li&gt;Checking out&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To ensure a fair comparison, I used the same selectors and functions across both frameworks as much as possible. This approach helps maintain consistency and minimizes discrepancies in the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  File structure
&lt;/h2&gt;

&lt;p&gt;For this project, I created a root directory named &lt;code&gt;swaglabs-tests&lt;/code&gt;, which contains two sub-folders: &lt;code&gt;cypress-tests&lt;/code&gt; and &lt;code&gt;playwright-tests&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File structure for &lt;code&gt;cypress-tests&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cypress-tests
 ┣ cypress
 ┃ ┣ downloads
 ┃ ┣ e2e
 ┃ ┃ ┣ addToCart.cy.js
 ┃ ┃ ┣ checkout.cy.js
 ┃ ┃ ┗ login.cy.js
 ┃ ┗ support
 ┃ ┃ ┣ commands.js
 ┃ ┃ ┣ e2e.js
 ┃ ┃ ┗ helpers.js
 ┣ README
 ┣ cypress.config.js
 ┣ package-lock.json
 ┗ package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;File structure for &lt;code&gt;playwright-tests&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;playwright-tests
 ┣ helpers
 ┃ ┗ helpers.js
 ┣ playwright-report
 ┃ ┗ index.html
 ┣ test-results
 ┃ ┗ .last-run.json
 ┣ tests
 ┃ ┣ addToCart.spec.js
 ┃ ┣ checkout.spec.js
 ┃ ┗ login.spec.js
 ┣ package-lock.json
 ┣ package.json
 ┗ playwright.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test execution
&lt;/h2&gt;

&lt;p&gt;Each framework contains 3 test suites with a total of 9 tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Login Suite: 5 tests&lt;/li&gt;
&lt;li&gt;Add to Cart Suite: 2 tests&lt;/li&gt;
&lt;li&gt;Checkout Suite: 2 tests&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  In Local Machine
&lt;/h3&gt;

&lt;p&gt;The tests were executed in both headless and headed modes using the Chrome browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cypress Execution in Headless Mode&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;Run Finished&lt;span class="o"&gt;)&lt;/span&gt;


       Spec                                              Tests  Passing  Failing  Pending  Skipped
  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ ✔  addToCart.cy.js                          00:09        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  checkout.cy.js                           00:10        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  login.cy.js                              00:06        5        5        -        -        - │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘
    ✔  All specs passed!                        00:26        9        9        -        -        -

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

&lt;/div&gt;



&lt;p&gt;Cypress executed the tests in 26 seconds, with repeated runs showing execution times ranging from 21 seconds to 26 seconds, averaging 24 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Playwright Execution in Headless Mode&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By default, Playwright runs tests in parallel across multiple workers. To ensure a fair comparison with Cypress, I configured Playwright to run tests sequentially with a single worker by updating the &lt;code&gt;playwright.config.js&lt;/code&gt; file:&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;//playwright.config.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({}&lt;/span&gt;
    &lt;span class="p"&gt;.....&lt;/span&gt;

    &lt;span class="nx"&gt;fullyParallel&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="c1"&gt;// Disable parallel execution&lt;/span&gt;
    &lt;span class="nx"&gt;workers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Use a single worker&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Running 9 tests using 1 worker

  ✓  1 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › addToCart.spec.js:9:6 › Add to cart › user adds a item to cart &lt;span class="o"&gt;(&lt;/span&gt;3.9s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  2 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › addToCart.spec.js:13:6 › Add to cart › user adds multiple items to cart &lt;span class="o"&gt;(&lt;/span&gt;1.9s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  3 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › checkout.spec.js:9:6 › checkout › user checks-out a item from the cart &lt;span class="o"&gt;(&lt;/span&gt;2.2s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  4 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › checkout.spec.js:14:6 › checkout › user checks-out multiple items from the cart &lt;span class="o"&gt;(&lt;/span&gt;2.1s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  5 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:9:6 › Login › normal user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1.5s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  6 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:18:6 › Login › locked out user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;435ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  7 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:28:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with no password &lt;span class="o"&gt;(&lt;/span&gt;443ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  8 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:37:6 › Login › user submits empty form &lt;span class="o"&gt;(&lt;/span&gt;394ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  9 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:45:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with incorrect password &lt;span class="o"&gt;(&lt;/span&gt;423ms&lt;span class="o"&gt;)&lt;/span&gt;

  9 passed &lt;span class="o"&gt;(&lt;/span&gt;13.8s&lt;span class="o"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Playwright completed the execution in around 14 seconds, with repeated runs ranging from 13 seconds to 15 seconds, averaging at 14 seconds. This is almost half the time Cypress took.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cypress Execution in Headed Mode&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="o"&gt;(&lt;/span&gt;Run Finished&lt;span class="o"&gt;)&lt;/span&gt;

       Spec                                              Tests  Passing  Failing  Pending  Skipped
  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ ✔  addToCart.cy.js                          00:06        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  checkout.cy.js                           00:09        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  login.cy.js                              00:06        5        5        -        -        - │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘
    ✔  All specs passed!                        00:21        9        9        -        -        -


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

&lt;/div&gt;



&lt;p&gt;In headed mode, Cypress executed the tests in 21 seconds. The execution time ranged from 20 to 22 seconds in repeated runs, averaging at 21 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Playwright Execution in Headed Mode&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Running 9 tests using 1 worker

  ✓  1 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › addToCart.spec.js:9:6 › Add to cart › user adds a item to cart &lt;span class="o"&gt;(&lt;/span&gt;3.9s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  2 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › addToCart.spec.js:13:6 › Add to cart › user adds multiple items to cart &lt;span class="o"&gt;(&lt;/span&gt;2.0s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  3 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › checkout.spec.js:9:6 › checkout › user checks-out a item from the cart &lt;span class="o"&gt;(&lt;/span&gt;3.1s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  4 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › checkout.spec.js:14:6 › checkout › user checks-out multiple items from the cart &lt;span class="o"&gt;(&lt;/span&gt;2.6s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  5 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:9:6 › Login › normal user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1.6s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  6 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:18:6 › Login › locked out user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;618ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  7 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:28:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with no password &lt;span class="o"&gt;(&lt;/span&gt;580ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  8 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:37:6 › Login › user submits empty form &lt;span class="o"&gt;(&lt;/span&gt;496ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  9 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:45:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with incorrect password &lt;span class="o"&gt;(&lt;/span&gt;587ms&lt;span class="o"&gt;)&lt;/span&gt;

  9 passed &lt;span class="o"&gt;(&lt;/span&gt;16.3s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In headed mode, Playwright completed the tests in around 16.3 seconds. The execution time ranged from 15 to 16 seconds, with an average of 15.5 seconds. This is still quicker than Cypress, despite a slight overhead in headed mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Execution Time Summary Table in Local Machine&lt;/strong&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Mode&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Framework&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Average Execution Time&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Execution Range&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Headless&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cypress&lt;/td&gt;
&lt;td&gt;24 seconds&lt;/td&gt;
&lt;td&gt;21–26 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Headless&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Playwright&lt;/td&gt;
&lt;td&gt;14 seconds&lt;/td&gt;
&lt;td&gt;13–15 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Headed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cypress&lt;/td&gt;
&lt;td&gt;21 seconds&lt;/td&gt;
&lt;td&gt;20–22 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Headed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Playwright&lt;/td&gt;
&lt;td&gt;15.5 seconds&lt;/td&gt;
&lt;td&gt;15–16 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Key Observations&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Playwright is faster overall.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;In headless mode, Playwright is ~42% faster than Cypress.&lt;/li&gt;
&lt;li&gt;In headed mode, Playwright is ~26% faster than Cypress.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cypress performance is consistent&lt;/strong&gt; across headed and headless modes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Playwright exhibits slight overhead in headed mode&lt;/strong&gt; compared to headless mode but remains faster than Cypress in both modes.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  In CI
&lt;/h3&gt;

&lt;p&gt;To evaluate execution times in CI environments, I created two workflows in GitHub Actions for running tests with Cypress and Playwright. Both frameworks were configured to run tests using the Chrome browser. The workflow files can be found &lt;a href="https://github.com/SwikritiT/swaglabs-tests/tree/main/.github/workflows" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Below are the results:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cypress Execution in CI&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="o"&gt;(&lt;/span&gt;Run Finished&lt;span class="o"&gt;)&lt;/span&gt;


       Spec                                              Tests  Passing  Failing  Pending  Skipped
  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ ✔  addToCart.cy.js                          00:05        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  checkout.cy.js                           00:06        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  login.cy.js                              00:04        5        5        -        -        - │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘
    ✔  All specs passed!                        00:17        9        9        -        -        -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test Execution Time&lt;/strong&gt;: The tests took a total of 17 seconds to execute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full CI Pipeline Duration&lt;/strong&gt;: The complete CI pipeline, including setup and test execution, took 1 minute and 40 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution Time Range&lt;/strong&gt;: The test execution time ranged from 15 to 17 seconds, with an average of 16 seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fzete52d2ixk6fmmae9tw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fzete52d2ixk6fmmae9tw.png" alt="CI pipline cypress execution" width="800" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Playwright Execution in CI&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Running 9 tests using 1 worker

  ✓  1 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › addToCart.spec.js:9:6 › Add to cart › user adds a item to cart &lt;span class="o"&gt;(&lt;/span&gt;1.5s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  2 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › addToCart.spec.js:13:6 › Add to cart › user adds multiple items to cart &lt;span class="o"&gt;(&lt;/span&gt;1.6s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  3 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › checkout.spec.js:9:6 › checkout › user checks-out a item from the cart &lt;span class="o"&gt;(&lt;/span&gt;1.6s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  4 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › checkout.spec.js:14:6 › checkout › user checks-out multiple items from the cart &lt;span class="o"&gt;(&lt;/span&gt;1.7s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  5 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:9:6 › Login › normal user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1.3s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  6 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:18:6 › Login › locked out user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;188ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  7 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:28:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with no password &lt;span class="o"&gt;(&lt;/span&gt;164ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  8 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:37:6 › Login › user submits empty form &lt;span class="o"&gt;(&lt;/span&gt;162ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  9 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:45:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with incorrect password &lt;span class="o"&gt;(&lt;/span&gt;177ms&lt;span class="o"&gt;)&lt;/span&gt;

  9 passed &lt;span class="o"&gt;(&lt;/span&gt;10.0s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test Execution Time&lt;/strong&gt;: The tests were completed in 10 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full CI Pipeline Duration&lt;/strong&gt;: The complete CI pipeline took only 42 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution Time Range&lt;/strong&gt;: The test execution time ranged from 9–12 seconds, with an average of 10.5 seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F6sjk9tk9mm3c1ul5yxys.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F6sjk9tk9mm3c1ul5yxys.png" alt="CI pipline Playwright execution" width="800" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Playwright tests run significantly faster in CI as well making them time efficient.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fv21lpk1q5nusz6vy0m01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fv21lpk1q5nusz6vy0m01.png" alt="GitHub Actions page" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Execution Time Summary Table in CI&lt;/strong&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Framework&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Test Execution Time&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Full CI Duration&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;TestExecution Range&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Average Execution Time&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cypress&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;17 seconds&lt;/td&gt;
&lt;td&gt;1 minute 40 seconds&lt;/td&gt;
&lt;td&gt;15–17 seconds&lt;/td&gt;
&lt;td&gt;16 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Playwright&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10 seconds&lt;/td&gt;
&lt;td&gt;42 seconds&lt;/td&gt;
&lt;td&gt;9–12 seconds&lt;/td&gt;
&lt;td&gt;10.5 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Key Observations&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: Playwright significantly outperformed Cypress in both test execution and overall CI pipeline duration.

&lt;ul&gt;
&lt;li&gt;Playwright's test execution was nearly 1.6x faster than Cypress's (10.5 seconds vs. 16 seconds on average).&lt;/li&gt;
&lt;li&gt;The overall CI pipeline for Playwright was 2.4x faster (42 seconds vs. 1 minute and 40 seconds).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Efficiency&lt;/strong&gt;: Playwright tests run more efficiently in CI, saving both time and resources.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Time Ranges&lt;/strong&gt;: Playwright showed lower execution time variability (9–12 seconds) compared to Cypress (15–17 seconds).&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Test Execution in Multiple Browser
&lt;/h2&gt;

&lt;p&gt;I executed the test suite in &lt;strong&gt;Chrome&lt;/strong&gt; and &lt;strong&gt;Firefox&lt;/strong&gt; to evaluate cross-browser compatibility and performance. Here is how each framework performed:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Cypress Cross-Browser Test Execution&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Cypress does not natively support running tests across multiple browsers in a single command, requiring separate executions for each browser. This means tests had to be run individually for Chrome and Firefox.&lt;/p&gt;

&lt;p&gt;Additionally, Cypress uses Playwright's WebKit engine for WebKit testing, which is still in the &lt;strong&gt;experimental stage&lt;/strong&gt;. When attempting to execute tests in WebKit, I encountered an error that I was unable to resolve, so results for WebKit are unavailable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chrome headless&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
  &lt;span class="o"&gt;(&lt;/span&gt;Run Finished&lt;span class="o"&gt;)&lt;/span&gt;


       Spec                                              Tests  Passing  Failing  Pending  Skipped
  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ ✔  addToCart.cy.js                          00:06        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  checkout.cy.js                           00:09        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  login.cy.js                              00:05        5        5        -        -        - │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘
    ✔  All specs passed!                        00:21        9        9        -        -        -

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

&lt;/div&gt;



&lt;p&gt;The total test execution time in Chrome ranged between &lt;strong&gt;19-22 seconds&lt;/strong&gt;, with an average execution time of &lt;strong&gt;21 seconds&lt;/strong&gt;. This time is better than what I observed earlier while executing tests in Chrome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Firefox headless&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="o"&gt;(&lt;/span&gt;Run Finished&lt;span class="o"&gt;)&lt;/span&gt;


       Spec                                              Tests  Passing  Failing  Pending  Skipped
  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ ✔  addToCart.cy.js                          00:06        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  checkout.cy.js                           00:08        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  login.cy.js                              00:06        5        5        -        -        - │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘
    ✔  All specs passed!                        00:21        9        9        -        -        -

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

&lt;/div&gt;



&lt;p&gt;The total test execution time in Firefox ranged between &lt;strong&gt;21-22 seconds&lt;/strong&gt;, with an average execution time of &lt;strong&gt;22 seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;On average it took around &lt;strong&gt;43 seconds&lt;/strong&gt; to run tests on both browsers in headless mode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chrome headed&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
  &lt;span class="o"&gt;(&lt;/span&gt;Run Finished&lt;span class="o"&gt;)&lt;/span&gt;


       Spec                                              Tests  Passing  Failing  Pending  Skipped
  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ ✔  addToCart.cy.js                          00:05        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  checkout.cy.js                           00:07        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  login.cy.js                              00:05        5        5        -        -        - │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘
    ✔  All specs passed!                        00:19        9        9        -        -        -

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

&lt;/div&gt;



&lt;p&gt;The total test execution time in Chrome ranged between 19-21 seconds, with an average execution time of 22 seconds. This time around the Cypress is faster compared to previous runs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Firefox headed&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
  &lt;span class="o"&gt;(&lt;/span&gt;Run Finished&lt;span class="o"&gt;)&lt;/span&gt;


       Spec                                              Tests  Passing  Failing  Pending  Skipped
  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ ✔  addToCart.cy.js                          00:08        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  checkout.cy.js                           00:09        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  login.cy.js                              00:06        5        5        -        -        - │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘
    ✔  All specs passed!                        00:23        9        9        -        -        -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The total test execution time in Firefox ranged between 21-22 seconds, with an average execution time of 22 seconds.&lt;/p&gt;

&lt;p&gt;On average it took around &lt;strong&gt;44 seconds&lt;/strong&gt; to run tests on both browsers in headed mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Playwright Cross-Browser Test Execution&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Playwright natively supports running tests across multiple browsers with a single command. By configuring browser-specific projects in the Playwright configuration file, tests can be executed concurrently on all configured browsers. Here’s how I configured and executed tests in Chrome and Firefox.&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;//playwright.config.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;.....&lt;/span&gt;
    &lt;span class="na"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;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;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Desktop Firefox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Headless in Chrome and Firefox&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Running 18 tests using 1 worker

  ✓  1 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › addToCart.spec.js:9:6 › Add to cart › user adds a item to cart &lt;span class="o"&gt;(&lt;/span&gt;3.4s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  2 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › addToCart.spec.js:13:6 › Add to cart › user adds multiple items to cart &lt;span class="o"&gt;(&lt;/span&gt;1.8s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  3 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › checkout.spec.js:9:6 › checkout › user checks-out a item from the cart &lt;span class="o"&gt;(&lt;/span&gt;2.3s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  4 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › checkout.spec.js:14:6 › checkout › user checks-out multiple items from the cart &lt;span class="o"&gt;(&lt;/span&gt;2.3s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  5 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:9:6 › Login › normal user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1.5s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  6 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:18:6 › Login › locked out user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;443ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  7 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:28:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with no password &lt;span class="o"&gt;(&lt;/span&gt;435ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  8 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:37:6 › Login › user submits empty form &lt;span class="o"&gt;(&lt;/span&gt;434ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  9 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:45:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with incorrect password &lt;span class="o"&gt;(&lt;/span&gt;446ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  10 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › addToCart.spec.js:9:6 › Add to cart › user adds a item to cart &lt;span class="o"&gt;(&lt;/span&gt;2.6s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  11 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › addToCart.spec.js:13:6 › Add to cart › user adds multiple items to cart &lt;span class="o"&gt;(&lt;/span&gt;2.4s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  12 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › checkout.spec.js:9:6 › checkout › user checks-out a item from the cart &lt;span class="o"&gt;(&lt;/span&gt;3.0s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  13 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › checkout.spec.js:14:6 › checkout › user checks-out multiple items from the cart &lt;span class="o"&gt;(&lt;/span&gt;3.2s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  14 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › login.spec.js:9:6 › Login › normal user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;2.1s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  15 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › login.spec.js:18:6 › Login › locked out user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;745ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  16 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › login.spec.js:28:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with no password &lt;span class="o"&gt;(&lt;/span&gt;697ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  17 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › login.spec.js:37:6 › Login › user submits empty form &lt;span class="o"&gt;(&lt;/span&gt;677ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  18 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › login.spec.js:45:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with incorrect password &lt;span class="o"&gt;(&lt;/span&gt;701ms&lt;span class="o"&gt;)&lt;/span&gt;

  18 passed &lt;span class="o"&gt;(&lt;/span&gt;31.1s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Playwright took 31.1 sec to run all the tests in 2 browsers. Execution time ranged from 30 to 33 seconds with an average execution time of 31 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Headed mode in Chrome and Firefox&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Running 18 tests using 1 worker

  ✓  1 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › addToCart.spec.js:9:6 › Add to cart › user adds a item to cart &lt;span class="o"&gt;(&lt;/span&gt;3.3s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  2 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › addToCart.spec.js:13:6 › Add to cart › user adds multiple items to cart &lt;span class="o"&gt;(&lt;/span&gt;2.0s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  3 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › checkout.spec.js:9:6 › checkout › user checks-out a item from the cart &lt;span class="o"&gt;(&lt;/span&gt;2.5s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  4 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › checkout.spec.js:14:6 › checkout › user checks-out multiple items from the cart &lt;span class="o"&gt;(&lt;/span&gt;2.7s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  5 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:9:6 › Login › normal user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1.7s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  6 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:18:6 › Login › locked out user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;546ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  7 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:28:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with no password &lt;span class="o"&gt;(&lt;/span&gt;540ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  8 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:37:6 › Login › user submits empty form &lt;span class="o"&gt;(&lt;/span&gt;517ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  9 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.js:45:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with incorrect password &lt;span class="o"&gt;(&lt;/span&gt;554ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  10 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › addToCart.spec.js:9:6 › Add to cart › user adds a item to cart &lt;span class="o"&gt;(&lt;/span&gt;3.3s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  11 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › addToCart.spec.js:13:6 › Add to cart › user adds multiple items to cart &lt;span class="o"&gt;(&lt;/span&gt;2.7s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  12 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › checkout.spec.js:9:6 › checkout › user checks-out a item from the cart &lt;span class="o"&gt;(&lt;/span&gt;3.6s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  13 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › checkout.spec.js:14:6 › checkout › user checks-out multiple items from the cart &lt;span class="o"&gt;(&lt;/span&gt;3.8s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  14 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › login.spec.js:9:6 › Login › normal user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;2.3s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  15 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › login.spec.js:18:6 › Login › locked out user logs &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1.0s&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  16 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › login.spec.js:28:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with no password &lt;span class="o"&gt;(&lt;/span&gt;869ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  17 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › login.spec.js:37:6 › Login › user submits empty form &lt;span class="o"&gt;(&lt;/span&gt;824ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓  18 &lt;span class="o"&gt;[&lt;/span&gt;firefox] › login.spec.js:45:6 › Login › user logs &lt;span class="k"&gt;in &lt;/span&gt;with incorrect password &lt;span class="o"&gt;(&lt;/span&gt;833ms&lt;span class="o"&gt;)&lt;/span&gt;

  18 passed &lt;span class="o"&gt;(&lt;/span&gt;36.3s&lt;span class="o"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;It took around 36 sec for Playwright to run tests in both browsers in headed mode. Execution time ranged from 33 to 36 seconds with an average execution time of 36 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Summary Table: Test Execution in Multiple Browsers&lt;/strong&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Framework&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Browser&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Mode&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Execution Time (Avg)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Execution Time Range&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cypress&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;Headless&lt;/td&gt;
&lt;td&gt;21 seconds&lt;/td&gt;
&lt;td&gt;19-22 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Firefox&lt;/td&gt;
&lt;td&gt;Headless&lt;/td&gt;
&lt;td&gt;22 seconds&lt;/td&gt;
&lt;td&gt;21-22 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Both Browsers&lt;/td&gt;
&lt;td&gt;Combined&lt;/td&gt;
&lt;td&gt;43 seconds&lt;/td&gt;
&lt;td&gt;40-44 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;Headed&lt;/td&gt;
&lt;td&gt;22 seconds&lt;/td&gt;
&lt;td&gt;19-22 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Firefox&lt;/td&gt;
&lt;td&gt;Headed&lt;/td&gt;
&lt;td&gt;22 seconds&lt;/td&gt;
&lt;td&gt;21-22 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Both Browsers&lt;/td&gt;
&lt;td&gt;Combined&lt;/td&gt;
&lt;td&gt;44 seconds&lt;/td&gt;
&lt;td&gt;40-44 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Playwright&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chrome, Firefox&lt;/td&gt;
&lt;td&gt;Headless&lt;/td&gt;
&lt;td&gt;31 seconds&lt;/td&gt;
&lt;td&gt;30-33 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Chrome, Firefox&lt;/td&gt;
&lt;td&gt;Headed&lt;/td&gt;
&lt;td&gt;36 seconds&lt;/td&gt;
&lt;td&gt;33-36 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Key Observations&lt;/strong&gt;:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cypress&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Lacks native support for multi-browser execution, requiring tests to be run separately for each browser, leading to longer combined execution times.&lt;/li&gt;
&lt;li&gt;WebKit support is still experimental, and errors prevented execution in this browser.&lt;/li&gt;
&lt;li&gt;Test execution times for Chrome and Firefox are consistent and comparable.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Playwright&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Supports concurrent multi-browser execution natively, saving time and effort.&lt;/li&gt;
&lt;li&gt;Significantly faster overall test execution compared to Cypress when testing in both browsers.&lt;/li&gt;
&lt;li&gt;Headed mode is slightly slower than headless but still efficient compared to Cypress.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;General&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Playwright is better suited for scenarios requiring multi-browser compatibility testing due to its concurrency capabilities.&lt;/li&gt;
&lt;li&gt;Cypress is reliable but less efficient for multi-browser testing because of its lack of native concurrency.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Multibrowser Test execution in CI
&lt;/h3&gt;

&lt;p&gt;In this section, I have updated the workflow to execute tests in multiple browsers (Chrome and Firefox) using both Cypress and Playwright. Here’s how each framework performed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cypress&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test execution in Chrome&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
  &lt;span class="o"&gt;(&lt;/span&gt;Run Finished&lt;span class="o"&gt;)&lt;/span&gt;


       Spec                                              Tests  Passing  Failing  Pending  Skipped
  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ ✔  addToCart.cy.js                          00:05        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  checkout.cy.js                           00:06        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  login.cy.js                              00:04        5        5        -        -        - │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘
    ✔  All specs passed!                        00:16        9        9        -        -        -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It took 16 seconds for the test execution in Chrome, but the test pipeline took 41 seconds due to additional setup steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F0908ulo2xmhel0gnw437.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F0908ulo2xmhel0gnw437.png" alt="cypress test pipeline chrome" width="800" height="27"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test execution in Firefox&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
  &lt;span class="o"&gt;(&lt;/span&gt;Run Finished&lt;span class="o"&gt;)&lt;/span&gt;


       Spec                                              Tests  Passing  Failing  Pending  Skipped
  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ ✔  addToCart.cy.js                          00:05        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  checkout.cy.js                           00:06        2        2        -        -        - │
  ├────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ ✔  login.cy.js                              00:05        5        5        -        -        - │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘
    ✔  All specs passed!                        00:17        9        9        -        -        -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It took 17 seconds for test execution in Firefox, but the test pipeline also took 41 seconds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ffuxmgk00zm2ndjvq01sc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ffuxmgk00zm2ndjvq01sc.png" alt="cypress test pipeline firefox" width="800" height="27"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Total CI Execution Time: 1 minute 45 seconds&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fe01xyayvy66e3h5oj3uz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fe01xyayvy66e3h5oj3uz.png" alt="Cypress test pipeline" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Playwright&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test execution in Chrome and Firefox&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Running 18 tests using 1 worker

  ✓  1 [chromium] › addToCart.spec.js:9:6 › Add to cart › user adds a item to cart (1.6s)
  ✓  2 [chromium] › addToCart.spec.js:13:6 › Add to cart › user adds multiple items to cart (1.6s)
  ✓  3 [chromium] › checkout.spec.js:9:6 › checkout › user checks-out a item from the cart (1.7s)
  ✓  4 [chromium] › checkout.spec.js:14:6 › checkout › user checks-out multiple items from the cart (1.8s)
  ✓  5 [chromium] › login.spec.js:9:6 › Login › normal user logs in (1.4s)
  ✓  6 [chromium] › login.spec.js:18:6 › Login › locked out user logs in (205ms)
  ✓  7 [chromium] › login.spec.js:28:6 › Login › user logs in with no password (178ms)
  ✓  8 [chromium] › login.spec.js:37:6 › Login › user submits empty form (163ms)
  ✓  9 [chromium] › login.spec.js:45:6 › Login › user logs in with incorrect password (176ms)
  ✓  10 [firefox] › addToCart.spec.js:9:6 › Add to cart › user adds a item to cart (2.5s)
  ✓  11 [firefox] › addToCart.spec.js:13:6 › Add to cart › user adds multiple items to cart (1.8s)
  ✓  12 [firefox] › checkout.spec.js:9:6 › checkout › user checks-out a item from the cart (2.3s)
  ✓  13 [firefox] › checkout.spec.js:14:6 › checkout › user checks-out multiple items from the cart (2.4s)
  ✓  14 [firefox] › login.spec.js:9:6 › Login › normal user logs in (1.6s)
  ✓  15 [firefox] › login.spec.js:18:6 › Login › locked out user logs in (467ms)
  ✓  16 [firefox] › login.spec.js:28:6 › Login › user logs in with no password (499ms)
  ✓  17 [firefox] › login.spec.js:37:6 › Login › user submits empty form (387ms)
  ✓  18 [firefox] › login.spec.js:45:6 › Login › user logs in with incorrect password (428ms)

  18 passed (23.9s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test execution in both Chrome and Firefox took 24 seconds, with the test pipeline taking 25 seconds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fih9ss61675u3fuak2ulr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fih9ss61675u3fuak2ulr.png" alt="Playwright test execution" width="800" height="31"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Total CI Execution Time: 45 seconds&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fh1wzseoy4v8jj62npau2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fh1wzseoy4v8jj62npau2.png" alt="Playwrigt CI" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Summary Table: Test Execution in Multiple Browsers&lt;/strong&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Framework&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Browser(s)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Test Execution Time&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Test Pipeline Time&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Total CI Execution Time&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cypress&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;16 seconds&lt;/td&gt;
&lt;td&gt;41 seconds&lt;/td&gt;
&lt;td&gt;1m 45s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Firefox&lt;/td&gt;
&lt;td&gt;17 seconds&lt;/td&gt;
&lt;td&gt;41 seconds&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Playwright&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chrome, Firefox&lt;/td&gt;
&lt;td&gt;24 seconds&lt;/td&gt;
&lt;td&gt;25 seconds&lt;/td&gt;
&lt;td&gt;45 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Key Observations&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cypress&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Test execution times in Chrome and Firefox were very similar (16s and 17s, respectively).&lt;/li&gt;
&lt;li&gt;The test pipeline took longer than the test execution itself due to additional setup steps, taking around 41 seconds for both browsers.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Playwright&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test execution times in Chrome and Firefox were less than that of Cypress(43s for combined).&lt;/li&gt;
&lt;li&gt;The Playwright test pipeline was faster than Cypress, taking only 25 seconds in total.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;CI Efficiency&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Playwright tests are more efficient in terms of total CI execution time compared to Cypress, with Playwright completing in 45 seconds versus Cypress taking 1m 45s.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Overall summary table&lt;/strong&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Framework&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Environment&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Browser(s)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Mode&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Test Execution Time (Avg)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Test Execution Range&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Pipeline Time&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Total CI Time&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cypress&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;Headless&lt;/td&gt;
&lt;td&gt;24 seconds&lt;/td&gt;
&lt;td&gt;21–26 seconds&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;Headed&lt;/td&gt;
&lt;td&gt;21 seconds&lt;/td&gt;
&lt;td&gt;20–22 seconds&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;CI&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;Headless&lt;/td&gt;
&lt;td&gt;16 seconds&lt;/td&gt;
&lt;td&gt;15–17 seconds&lt;/td&gt;
&lt;td&gt;41 seconds&lt;/td&gt;
&lt;td&gt;1m 45s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;CI&lt;/td&gt;
&lt;td&gt;Firefox&lt;/td&gt;
&lt;td&gt;Headless&lt;/td&gt;
&lt;td&gt;17 seconds&lt;/td&gt;
&lt;td&gt;15–17 seconds&lt;/td&gt;
&lt;td&gt;41 seconds&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;td&gt;Firefox&lt;/td&gt;
&lt;td&gt;Headless&lt;/td&gt;
&lt;td&gt;22 seconds&lt;/td&gt;
&lt;td&gt;21–22 seconds&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;td&gt;Firefox&lt;/td&gt;
&lt;td&gt;Headed&lt;/td&gt;
&lt;td&gt;22 seconds&lt;/td&gt;
&lt;td&gt;21–22 seconds&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;td&gt;Both Browsers&lt;/td&gt;
&lt;td&gt;Combined&lt;/td&gt;
&lt;td&gt;44 seconds&lt;/td&gt;
&lt;td&gt;40–44 seconds&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Playwright&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;Headless&lt;/td&gt;
&lt;td&gt;14 seconds&lt;/td&gt;
&lt;td&gt;13–15 seconds&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;Headed&lt;/td&gt;
&lt;td&gt;15.5 seconds&lt;/td&gt;
&lt;td&gt;15–16 seconds&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;CI&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;Headless&lt;/td&gt;
&lt;td&gt;10.5 seconds&lt;/td&gt;
&lt;td&gt;9–12 seconds&lt;/td&gt;
&lt;td&gt;25 seconds&lt;/td&gt;
&lt;td&gt;42 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;td&gt;Chrome, Firefox&lt;/td&gt;
&lt;td&gt;Headless&lt;/td&gt;
&lt;td&gt;31 seconds&lt;/td&gt;
&lt;td&gt;30–33 seconds&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;td&gt;Chrome, Firefox&lt;/td&gt;
&lt;td&gt;Headed&lt;/td&gt;
&lt;td&gt;36 seconds&lt;/td&gt;
&lt;td&gt;33–36 seconds&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;CI&lt;/td&gt;
&lt;td&gt;Chrome, Firefox&lt;/td&gt;
&lt;td&gt;Headless&lt;/td&gt;
&lt;td&gt;22 seconds&lt;/td&gt;
&lt;td&gt;20-24 seconds&lt;/td&gt;
&lt;td&gt;25 seconds&lt;/td&gt;
&lt;td&gt;45 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Overall Observations&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Playwright consistently outperformed Cypress in test execution time across environments.&lt;/li&gt;
&lt;li&gt;In local environments, Playwright completed tests in nearly half the time compared to Cypress in headless mode.&lt;/li&gt;
&lt;li&gt;In CI, Playwright's combined browser execution (24s) was significantly faster than Cypress's separate executions (16s + 17s).&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline Efficiency&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Cypress pipelines consumed more time for setup and execution, taking 41 seconds for both Chrome and Firefox individually in CI.&lt;/li&gt;
&lt;li&gt;Playwright pipelines were optimized for multi-browser execution, completing setup and execution in just 25 seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Playwright supports native multi-browser execution, running tests concurrently across browsers with a single command.&lt;/li&gt;
&lt;li&gt;Cypress lacks this capability, requiring separate commands for each browser, resulting in increased overall execution time.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Cypress showed consistent test execution times across Chrome and Firefox in both local and CI environments.&lt;/li&gt;
&lt;li&gt;Playwright maintained fast and reliable execution with minimal time variability in both environments.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Both Cypress and Playwright are reliable testing frameworks for modern automation needs. However, Playwright stands out in terms of speed, efficiency, and multi-browser support. Its ability to run tests concurrently across multiple browsers and its optimized CI pipeline make it a better choice for teams looking to save time and resources. Cypress, while user-friendly and consistent, may not be the best fit for large-scale projects requiring extensive cross-browser testing or time-sensitive CI/CD workflows.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>cypress</category>
      <category>testing</category>
      <category>automation</category>
    </item>
    <item>
      <title>Application Programming Interface (API's) : One Byte Explainer</title>
      <dc:creator>Swikriti Tripathi</dc:creator>
      <pubDate>Wed, 19 Jun 2024 09:15:07 +0000</pubDate>
      <link>https://dev.to/swikritit/application-programming-interface-apis-one-byte-explainer-5882</link>
      <guid>https://dev.to/swikritit/application-programming-interface-apis-one-byte-explainer-5882</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/cs"&gt;DEV Computer Science Challenge v24.06.12: One Byte Explainer&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Explainer
&lt;/h2&gt;

&lt;p&gt;Imagine APIs as the postman for your digital world. They deliver messages (data requests) between your computer (web client) and websites (web servers). They make sure everything gets where it needs to go, like your own personal courier service for the internet!&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Context
&lt;/h2&gt;

</description>
      <category>devchallenge</category>
      <category>cschallenge</category>
      <category>computerscience</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to containerize your web app- a beginner-friendly tutorial for Dockerfile</title>
      <dc:creator>Swikriti Tripathi</dc:creator>
      <pubDate>Tue, 18 Jun 2024 18:07:18 +0000</pubDate>
      <link>https://dev.to/swikritit/how-to-containerize-your-web-app-a-beginner-friendly-tutorial-for-dockerfile-282e</link>
      <guid>https://dev.to/swikritit/how-to-containerize-your-web-app-a-beginner-friendly-tutorial-for-dockerfile-282e</guid>
      <description>&lt;p&gt;Welcome to part 2 of the series &lt;code&gt;Docker for Dummies&lt;/code&gt; in this blog we are going to create an image of a small web app and learn about what each step does. Without any further ado let's get started. For this blog, I'm going to use a simple vue-app quiz app that'll let you guess the name of the book based on the first line. If you want to follow along with the setup you can find the GitHub link to the web app &lt;a href="https://github.com/SwikritiT/Guessthebook-blog" rel="noopener noreferrer"&gt;here&lt;/a&gt; or you can use your app or you can create a simple &lt;code&gt;hello-world&lt;/code&gt; app in node or framework/language of your choosing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's create the image
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: this tutorial follows ubuntu commands but they should be similar for other OS's as well&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's clone the app and go inside of it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/SwikritiT/Guessthebook-blog
&lt;span class="nb"&gt;cd &lt;/span&gt;Guessthebook-blog

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

&lt;/div&gt;



&lt;p&gt;We are going to start by creating a file called &lt;code&gt;Dockerfile&lt;/code&gt; in the root of our repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dockerfile
&lt;/h3&gt;

&lt;p&gt;A Dockerfile is a script that contains all the steps necessary to build an image. Dockerfile starts with something called &lt;code&gt;base image&lt;/code&gt;. A &lt;code&gt;base image&lt;/code&gt; is a pre-configured environment that our images build upon. The base image can be an OS like Linux, alpine, or some application stack. Choosing an appropriate base image is crucial for the overall size and productivity of the image of our application. We will talk more about how to select the appropriate base image in the later part of this series so for now let's keep in mind that a lighter base-image will create a lighter app image(this comes with its limitations which we will talk about in more detail in the later part of this series). So, for this app, we are going to use &lt;code&gt;node:alpine&lt;/code&gt; images as a base, you can select your needed version of a base image from an image registry like &lt;a href="https://hub.docker.com/_/node" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: If you're not building the image of the node application, you need to select the image appropriate for your stack&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start with a base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;FROM&lt;/code&gt;: Specifies the base image to use. Every Dockerfile starts with this instruction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next step is to set the &lt;code&gt;WORKDIR&lt;/code&gt; i.e. working directory for our container where commands will be executed and files will be stored by default. Normally, for web apps, workdir is set to &lt;code&gt;/usr/src/app&lt;/code&gt; but you can customize it as per your need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set the working directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's copy the necessary config files to our container, in our case, package.json and lockfile. For this, we will use &lt;code&gt;COPY&lt;/code&gt; command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Copy package.json and package-lock.json to the working directory&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;COPY or ADD&lt;/code&gt;: Copies files from your local filesystem into the container.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next step is to install dependencies just like we do in our host machine and for this &lt;code&gt;RUN&lt;/code&gt; command can be used&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install the application dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;RUN&lt;/code&gt;: Executes commands in the container. These commands are typically used to install software packages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we copy the rest of the files to our container's working directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Copy the current directory contents into the container at /usr/src/app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command will copy every file and folder that is in your app to the container's working directory. This includes the build file, files created by IDE, or other miscellaneous files/folders that might not be necessary for building the image. So, it is a good practice to either copy only necessary files or create a &lt;code&gt;.dockerignore&lt;/code&gt; file with the list of files and folders that you don't want to be copied inside the container. The syntax of the &lt;code&gt;.dockerignore&lt;/code&gt; file is similar to that of &lt;code&gt;.gitignore&lt;/code&gt;. Learn more &lt;a href="https://docs.docker.com/build/building/context/#dockerignore-files" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step is to build our application&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build the application&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to &lt;code&gt;Expose&lt;/code&gt; the ports on which a containerized application listens for network connections&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Make port 3000 available to the world outside the container&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;EXPOSE&lt;/code&gt; is a way to document which ports the application running inside the container will use. It does not map the port to the host machine’s ports. It simply indicates which ports are intended to be accessible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We've come to the final stage where we will run the application. There are two commands that we can use to do this &lt;code&gt;CMD&lt;/code&gt; and &lt;code&gt;ENTRYPOINT&lt;/code&gt; for this part we will be using &lt;code&gt;CMD&lt;/code&gt; and we'll talk about &lt;code&gt;ENTRYPOINT&lt;/code&gt; in later parts.&lt;/p&gt;

&lt;p&gt;Since we will be running the application in production env we will use the &lt;code&gt;preview&lt;/code&gt; command for this. Later we'll also create a dockerized dev env.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Define the command to run the app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm","run","preview"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;CMD&lt;/code&gt;: Provides the command that will be run when a container is started from the image. Only one CMD instruction can be present in a Dockerfile.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now let's look at the whole &lt;code&gt;Dockerfile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start with a base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:alpine&lt;/span&gt;

&lt;span class="c"&gt;# Set the working directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;

&lt;span class="c"&gt;# Copy package.json and package-lock.json to the working directory&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="c"&gt;# Install the application dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Copy the current directory contents into the container at /usr/src/app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Build the application&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="c"&gt;# Make port 3000 available to the world outside the container&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="c"&gt;# Define the command to run the app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm","run","preview"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Docker, each line in your Dockerfile creates a new layer in the final image, like adding ingredients to a sandwich. These layers stack on top of each other, with each layer representing a change or addition, such as copying files or installing software. Docker saves these layers, and if you rebuild your image and some layers haven’t changed, Docker reuses them, speeding up the build process and reducing redundancy. So, in the above dockerfile, we have 8 layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the image
&lt;/h2&gt;

&lt;p&gt;Now that we've created the image it's time to build it and get it running. We can run the following command in the terminal from the root of our repository to build a docker image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; guessthebook:v1

&lt;span class="o"&gt;[&lt;/span&gt;+] Building 1.1s &lt;span class="o"&gt;(&lt;/span&gt;11/11&lt;span class="o"&gt;)&lt;/span&gt; FINISHED                               docker:default
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load build definition from Dockerfile                       0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring dockerfile: 191B                                       0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load metadata &lt;span class="k"&gt;for &lt;/span&gt;docker.io/library/node:20-alpine          0.9s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load .dockerignore                                          0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring context: 129B                                          0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;1/6] FROM docker.io/library/node:20-alpine@sha256:66c7d989b6dabba6b4  0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load build context                                          0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring context: 1.41kB                                        0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;2/6] WORKDIR /usr/src/app                                      0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;3/6] COPY package&lt;span class="k"&gt;*&lt;/span&gt;.json ./                                     0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;4/6] RUN npm &lt;span class="nb"&gt;install                                           &lt;/span&gt;0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;5/6] COPY &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;                                                  0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;6/6] RUN npm run build                                         0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting to image                                                     0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting layers                                                    0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; writing image sha256:08f32d7f583b7e65a844accafff8fc19930849204c5ae  0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; naming to docker.io/library/guessthebook:v1                         0.0s

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

&lt;/div&gt;



&lt;p&gt;You will get the output like such with the information of each layer being built. The command &lt;code&gt;docker build . -t guessthebook:v1&lt;/code&gt; reads the Dockerfile in the current directory, builds a Docker image according to its instructions, and tags this image as &lt;code&gt;guessthebook&lt;/code&gt; with version &lt;code&gt;v1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now if you run the following command in your terminal, you should see the relevant info about the image that we just created&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker images

REPOSITORY                                   TAG            IMAGE ID       CREATED         SIZE
guessthebook                                 v1             08f32d7f583b   5 minutes ago   275MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running a Docker Container
&lt;/h2&gt;

&lt;p&gt;Once you have built the image, you can run it with the &lt;code&gt;docker run&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 guessthebook:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command runs a container from the &lt;code&gt;guessthebook:v1&lt;/code&gt; image, mapping port &lt;code&gt;3000&lt;/code&gt; of the container to port &lt;code&gt;3000&lt;/code&gt; on the host machine. We use &lt;code&gt;-p&lt;/code&gt; for port mapping.&lt;/p&gt;

&lt;p&gt;We can improvise the above command to use the option &lt;code&gt;-d&lt;/code&gt; to run it in detached mode&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; docker run  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 guessthebook:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running the docker container you can visit &lt;code&gt;http://localhost:3000&lt;/code&gt; to ensure everything works fine. If everything is setup and working correctly you should be greeted with this screen&lt;/p&gt;

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

&lt;p&gt;You can now take a rest and play the quiz to see how many you get right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop container
&lt;/h2&gt;

&lt;p&gt;You can stop the container with the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker stop &amp;lt;container_name or &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;# can run `docker ps` to get the name and id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Publish the image to the docker hub
&lt;/h2&gt;

&lt;p&gt;If we want to take this a step further we can publish the image to the docker hub. For that create an account on &lt;a href="https://hub.docker.com/signup" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt; if you haven't already. Next, you can create a new repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9l3ctp3zg0qxnl46ckw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9l3ctp3zg0qxnl46ckw.png" alt="docker hub create repo page" width="751" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Login to your docker hub through docker CLI
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Tag your local image to match the repo image
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# docker tag local-image:tagname new-repo:tagname&lt;/span&gt;
docker tag guessthebook:v1 &amp;lt;your-docker-username&amp;gt;/guessthebook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Push the image
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# docker push new-repo:tagname&lt;/span&gt;
docker push &amp;lt;your-docker-username&amp;gt;/guessthebook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can go and check if your docker hub has the image that you just pushed.&lt;/p&gt;

&lt;p&gt;To test the image you can now run the image by pulling directly from &lt;code&gt;Docker Hub&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# let's remove the locally tagged image first&lt;/span&gt;
docker rmi &amp;lt;your-docker-username&amp;gt;/guessthebook

&lt;span class="c"&gt;# run the container&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &amp;lt;your-docker-username&amp;gt;/guessthebook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it for this blog. Hope you enjoyed this one and learned something new as well! See you in the next part of this series. If you have any queries or suggestions please comment them below!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>containerapps</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Docker for Dummies- Introduction to docker</title>
      <dc:creator>Swikriti Tripathi</dc:creator>
      <pubDate>Fri, 14 Jun 2024 08:34:05 +0000</pubDate>
      <link>https://dev.to/swikritit/docker-for-dummies-introduction-to-docker-5h67</link>
      <guid>https://dev.to/swikritit/docker-for-dummies-introduction-to-docker-5h67</guid>
      <description>&lt;h3&gt;
  
  
  Welcome to Docker for Dummies!
&lt;/h3&gt;

&lt;p&gt;This is going to be an &lt;code&gt;idk-how-many-part series&lt;/code&gt; where I'll try to explain docker in as simple a way as possible. I'm also new to the world of containerization so the purpose of this series is to try to teach this technology while learning it myself.&lt;/p&gt;

&lt;p&gt;If you are new to the tech industry or have already been a part of this world, you might've probably come across the term &lt;code&gt;Docker&lt;/code&gt; now and again, but what exactly is this docker? Are we building a ship or something?&lt;/p&gt;

&lt;h2&gt;
  
  
  The wonderful world of Docker
&lt;/h2&gt;

&lt;p&gt;Imagine you made a delicious meal and want to share it with your partner but then you realize that you are in a LDR and it won't be possible due to the distance. But what if there was a way to pack your dish, along with all the ingredients and tools you used, into a box that keeps it fresh and ready to eat anywhere, anytime? Well, there isn't one for food but there's one for software that does something like that and that's? You guessed it right &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;&lt;code&gt;Docker&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Docker is like a magical box that packages your application, along with everything it needs to run, into a neat, portable container. This container can run on any computer, anywhere, without any worries about compatibility or missing ingredients. Cool, right?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In a more technical term&lt;/em&gt; - Docker is a platform that uses containerization to allow developers to package applications along with all their dependencies into a container. This ensures that the application can run consistently across different computing environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should You Care About Docker?
&lt;/h2&gt;

&lt;p&gt;There are a lot of reasons to use docker. I'll explain some below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistency&lt;/strong&gt;: Suppose your team is building an application and you build a feature in your local system and everything is working as expected. But then your team member tries to run the same feature in their local set-up and it crashes and you suddenly find yourself amidst a decade-old problem in software engineering&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdxoktfzn941sjoitisp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdxoktfzn941sjoitisp.png" alt="it works on my machine" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
Docker ensures that your application works the same way &lt;br&gt;
everywhere. No more "it works on my machine" problems but rather &lt;br&gt;
"it works on my container" problem 🫠&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Isolated Environments&lt;/strong&gt;: If you are building 3 different applications that require 3 different versions of node, trying to set it up in your local machine, switching between them, and trying to make them work would be a hassle that I would only wish upon my enemies not that I have any enemies👀. But with docker, you can just switch between the containers and develop everything parallelly without much difficulty. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Portability&lt;/strong&gt;: Whether you’re developing on your laptop or deploying to a cloud server, Docker containers can run anywhere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Efficiency&lt;/strong&gt;: Containers are lightweight and start up in seconds, making them perfect for development and deployment.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These are just a few of the benefits. Docker is a gift that keeps on giving.&lt;/p&gt;

&lt;h2&gt;
  
  
  Virtual Machines vs Containers
&lt;/h2&gt;

&lt;p&gt;It might be easy to confuse virtual machines with container but they are completely different technologies and solve different problems. Here's a picture to differentiate them&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83n6vd9brjh2cub5grdg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83n6vd9brjh2cub5grdg.png" alt="Virtual machines vs containers" width="781" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Containers use the host OS's kernel and share it among all containers, making them more lightweight and efficient. VMs emulate an entire physical machine, including its operating system (OS), making it possible to run multiple OS instances on a single physical machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with Docker
&lt;/h2&gt;

&lt;p&gt;Ready to dip your toes into the Docker waters? Let’s start with a quick example that will get you up and running in no time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Install Docker&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First things first, you’ll need to install &lt;a href="https://docs.docker.com/desktop/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;. Head over to the Docker website and download Docker Desktop for your operating system (Windows, macOS, or Linux). Follow the installation instructions provided. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Run Your First Container&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, let's run your first container. We are going to run &lt;code&gt;hello-world&lt;/code&gt; container. Open your terminal (Command Prompt on Windows, Terminal on macOS and Linux) and you'll run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything is working fine you should see something like this on your screen&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Unable to find image &lt;span class="s1"&gt;'hello-world:latest'&lt;/span&gt; locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;Digest: sha256:d1b0b5888fbb59111dbf2b3ed698489c41046cb9d6d61743e37ef8d9f3dda06f
Status: Downloaded newer image &lt;span class="k"&gt;for &lt;/span&gt;hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the &lt;span class="s2"&gt;"hello-world"&lt;/span&gt; image from the Docker Hub.
    &lt;span class="o"&gt;(&lt;/span&gt;amd64&lt;span class="o"&gt;)&lt;/span&gt;
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 &lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

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

&lt;/div&gt;



&lt;p&gt;Let's break down what that command did, the command pulled an image &lt;a href="https://hub.docker.com/_/hello-world" rel="noopener noreferrer"&gt;&lt;code&gt;hello-world&lt;/code&gt;&lt;/a&gt; from the docker hub and ran it in your host machine. If the image is already present in your system the command will skip the pulling part and just run the existing image.&lt;/p&gt;

&lt;p&gt;Now, if you run the command again it will run the previously pulled image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Image and containers
&lt;/h2&gt;

&lt;p&gt;Think of a container as a ready-to-eat meal that you can simply heat up and consume. An image, on the other hand, is the recipe or ingredients for that meal.&lt;/p&gt;

&lt;p&gt;So just like how you need a recipe and ingredients to make a meal, you need an image and a container runtime (Docker engine) to create a container. The image provides all the necessary instructions and dependencies for the container to run, just like a recipe provides the steps and ingredients to make a meal.&lt;/p&gt;

&lt;p&gt;In short, an image is like a blueprint or template, while a container is an instance of that blueprint or template.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some useful commands
&lt;/h2&gt;

&lt;p&gt;You can try out these commands and see what the output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#  lists all the running Docker containers on your system&lt;/span&gt;
docker ps


&lt;span class="c"&gt;# lists all Docker containers on your system, including those that are currently running, as well as those that have stopped or exited.&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt; 

&lt;span class="c"&gt;# lists all the Docker images stored on your system, showing details such as the repository name, tag, image ID, creation date, and size&lt;/span&gt;

docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check the &lt;a href="https://docs.docker.com/reference/cli/docker/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; for other commands and try them out.&lt;/p&gt;

&lt;p&gt;This is it for this part. In the next part, we will be writing a dockerfile for a small web app and it should be exciting.&lt;/p&gt;

&lt;p&gt;Until then take care! Keep learning! &lt;/p&gt;

</description>
      <category>docker</category>
      <category>containers</category>
      <category>devops</category>
      <category>linux</category>
    </item>
    <item>
      <title>Debugging and Error Tracing in Playwright</title>
      <dc:creator>Swikriti Tripathi</dc:creator>
      <pubDate>Wed, 07 Dec 2022 08:22:04 +0000</pubDate>
      <link>https://dev.to/jankaritech/debugging-and-error-tracing-in-playwright-516o</link>
      <guid>https://dev.to/jankaritech/debugging-and-error-tracing-in-playwright-516o</guid>
      <description>&lt;p&gt;How many times has it happened that you wrote blocks of code thinking they work but the reality was otherwise? I think it's safe to assume that this has happened quite often. Sometimes it might be easy to find the mistake but it's not always the case, so in this blog, I'm going to try to explain some of the debugging methods in Playwright that we can incorporate into our project to make our life a little bit easier. This might be the right time to mention that you might want to read &lt;a href="https://dev.to/jankaritech/behavior-driven-development-bdd-using-playwright-n1o"&gt;part one of the series&lt;/a&gt; as we are going to use the same code here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Headed mode
&lt;/h2&gt;

&lt;p&gt;The first method would be to run the tests in headed mode. Playwright by default runs in headless mode. Use &lt;code&gt;headless:false&lt;/code&gt; while launching the browser. Additionally, you can also use the &lt;code&gt;slowMo&lt;/code&gt; option to slow down the test execution process.&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;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;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;slowMo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// or firefox, WebKit&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Playwright Inspector
&lt;/h2&gt;

&lt;p&gt;Playwright comes with a default GUI tool that we can use to inspect our scripts. Through this tool, you'll be able to step-over each script and evaluate them in real-time. There are a few ways through which we can open Playwright inspector.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. PWDEBUG
&lt;/h3&gt;

&lt;p&gt;Playwright provides us with an environment variable that'll configure it in debugging mode and opens the inspector. Set the &lt;code&gt;PWDEBUG&lt;/code&gt; variable to &lt;code&gt;1&lt;/code&gt; or &lt;code&gt;console&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In my case, I'm running the e2e test in debug mode by setting &lt;code&gt;PWDEBUG=1&lt;/code&gt; .&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;PWDEBUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 npm run &lt;span class="nb"&gt;test&lt;/span&gt;:e2e tests/acceptance/features/todo.feature


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

&lt;/div&gt;

&lt;p&gt;This will open up an inspector like so&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%2Fto1im1tyryhs8o7gsbtm.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%2Fto1im1tyryhs8o7gsbtm.png" alt="Playwright Inspector"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see this gives me the test scripts, now I can either step over each script or run everything at once&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;step over each script&lt;/strong&gt;&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%2Fd3i0tmrs3b6ycjrfymxy.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%2Fd3i0tmrs3b6ycjrfymxy.png" alt="Step over"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;run all at once&lt;/strong&gt;&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%2Free76hiupa4ctdb82ruj.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%2Free76hiupa4ctdb82ruj.png" alt="Run all"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With each step-over, the inspector will step through each line of the test highlighting the selector as you go. You can also see the logs that display each action that the tests perform.&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%2Fycjxucrpw0ezaxwnjyta.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%2Fycjxucrpw0ezaxwnjyta.png" alt="highlight selector and logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, you'll also be able to access the browser developers' tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. page.pause()
&lt;/h3&gt;

&lt;p&gt;The next method to launch the inspector is to use &lt;code&gt;page.pause()&lt;/code&gt; in the script.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nc"&gt;When&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;the user adds {string} to the todo list using the webUI&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// fill the item that was input from the feature file, to the inputText field in the UI&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;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoInput&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="c1"&gt;// pauses the test execution and launches Playwright inspector&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;// click the button&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;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoButton&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 will pause the execution of the test and launch Playwright inspector right before clicking the button.&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%2F5jmiftuuypqedorjaj4z.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%2F5jmiftuuypqedorjaj4z.png" alt="page.pause"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we can perform similar operations as explained above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trace Viewer
&lt;/h2&gt;

&lt;p&gt;Trace viewer is another GUI tool to explore the recorded Playwright traces of the tests after the tests have been executed. This is especially essential while running the tests in the Continuous Integration (CI) environment.&lt;/p&gt;

&lt;p&gt;Let's see how we can set it up in our end-to-end tests. In the &lt;code&gt;cucumber.conf.js&lt;/code&gt; file inside &lt;code&gt;before hook&lt;/code&gt; we can add the following configuration&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nc"&gt;Before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&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="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="c1"&gt;// start tracing the test execution by enabling the screenshots and snapshots&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&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;tracing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;screenshots&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;snapshots&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="nb"&gt;global&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="nb"&gt;global&lt;/span&gt;&lt;span class="p"&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;newPage&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 will start the tracing of the tests, to know about more options that can be configured while starting the trace you can go through this &lt;a href="https://playwright.dev/docs/api/class-tracing#tracing-start" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, in the &lt;code&gt;after hook&lt;/code&gt; we can add the following code to stop tracing and store it in a certain specified path.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nc"&gt;After&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;global&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="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="c1"&gt;// stop tracing and store it in the given path&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&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;tracing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&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="s1"&gt;tests/acceptance/report/trace.zip&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="nb"&gt;global&lt;/span&gt;&lt;span class="p"&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;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;Now, if we run the tests we should get a trace.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm run &lt;span class="nb"&gt;test&lt;/span&gt;:e2e tests/acceptance/features/todo.feature


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

&lt;/div&gt;

&lt;p&gt;This should create the folder &lt;code&gt;report&lt;/code&gt; inside of &lt;code&gt;tests/acceptance&lt;/code&gt; and you should see a file called &lt;code&gt;trace.zip&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you extract the file then inside &lt;code&gt;trace/resources&lt;/code&gt; you should be able to see the screenshots of the UI through various steps in test execution.&lt;/p&gt;

&lt;p&gt;But the fun part is to view the trace which we can do by running the following command from the root of the project.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx playwright show-trace tests/acceptance/report/trace.zip


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

&lt;/div&gt;

&lt;p&gt;This should open up the Playwright trace viewer GUI for you 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%2F5cu8pe55l6gozhomyu77.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%2F5cu8pe55l6gozhomyu77.png" alt="Playwright Trace Viewer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see in the picture we can access a lot of functionalities through the trace viewer like Actions, Metadata, Console, Network, and so on. These will come in handy if we need to figure out or debug the test failure. If you want to know in detail about each of these functionalities you can go through this &lt;a href="https://playwright.dev/docs/trace-viewer" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These were the few ways in which we can debug and trace tests with tools provided by Playwright. I hope you found this helpful. In the next part of the series, we'll run the tests on CI and get traces for test failures.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>testing</category>
      <category>cucumber</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Behavior Driven Development (BDD) using Playwright</title>
      <dc:creator>Swikriti Tripathi</dc:creator>
      <pubDate>Mon, 07 Feb 2022 11:44:15 +0000</pubDate>
      <link>https://dev.to/jankaritech/behavior-driven-development-bdd-using-playwright-n1o</link>
      <guid>https://dev.to/jankaritech/behavior-driven-development-bdd-using-playwright-n1o</guid>
      <description>&lt;p&gt;Playwright is an open-source NodeJS framework for browser automation. It is developed by Microsoft and the development team has members that were involved in developing &lt;a href="https://github.com/puppeteer/puppeteer" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt; for Google.&lt;/p&gt;

&lt;p&gt;One of the main features of Playwright is that it can automate Chromium, Webkit, and Firefox browsers with a single API. Along with being cross-browser, it is cross-platform and cross-language, supporting the major OS like Windows, Linux, Mac and languages like TypeScript, JavaScript, Python, .NET, Java. Playwright also comes with tools like codgen - which lets you generate automatic code by recording your actions, you can find out more about Playwright on their &lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;official website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For this blog, we will be implementing BDD in Playwright. I have a small to-do web app and I'm going to be setting up Playwright in the same. If you want to follow through you can fork and clone the project from &lt;a href="https://github.com/SwikritiT/todo" rel="noopener noreferrer"&gt;here&lt;/a&gt;. If you have your web application you can set up Playwright there as well. Let's get started!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: the whole setup is done in Ubuntu 20.04.3 LTS, so some setup steps might differ depending on your platform&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node.js version 12 or above. If you don't already have node installed in your system you can use this &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-20-04" rel="noopener noreferrer"&gt;blog as a guide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note Only Ubuntu 18.04 and Ubuntu 20.04 are officially supported.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Run from your project's root directory&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

   npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @playwright/test
   npm i &lt;span class="nt"&gt;-D&lt;/span&gt; playwright 
   npx playwright &lt;span class="nb"&gt;install&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Playwright doesn't come with the built-in support for BDD so we are going to use the help of another tool &lt;a href="https://cucumber.io/" rel="noopener noreferrer"&gt;Cucumber&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

   npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @cucumber/cucumber@7.3.1 @cucumber/pretty-formatter


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

&lt;/div&gt;

&lt;p&gt;After this, &lt;code&gt;devDependencies&lt;/code&gt; in your &lt;code&gt;package.json&lt;/code&gt; should look something like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;package.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;file&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@cucumber/cucumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.3.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@cucumber/pretty-formatter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.0.0-alpha.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@playwright/test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.18.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"playwright"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.18.1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;We are going to use &lt;code&gt;Cucumber&lt;/code&gt; to run our tests so we need to have a configuration file for it. At the root level of your project create a file &lt;code&gt;cucumber.conf.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;First of all, we are going to require the following:&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;// cucumber.conf.js file&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;Before&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="nx"&gt;AfterAll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;After&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDefaultTimeout&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;@cucumber/cucumber&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 can choose other browsers like webkit or firefox according to your requirement&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&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;Set default timeout to some reasonable amount of time&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;// cucumber.conf.js file&lt;/span&gt;

&lt;span class="c1"&gt;// in milliseconds&lt;/span&gt;
&lt;span class="nf"&gt;setDefaultTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Add the following code snippet to your file&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;// cucumber.conf.js file&lt;/span&gt;

&lt;span class="c1"&gt;// launch the browser&lt;/span&gt;
&lt;span class="nc"&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nb"&gt;global&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="nf"&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;slowMo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// close the browser&lt;/span&gt;
&lt;span class="nc"&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="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;In the above snippet of code, we launch a &lt;code&gt;chrome&lt;/code&gt; browser where our tests will be automated. You can launch a different one as per your requirement, just make sure you import the correct browser. We run the browser in the headed mode which can be done by setting &lt;code&gt;headless:false&lt;/code&gt;, this means that when the test is running we can see it being automated in the browser. You can set it to &lt;code&gt;true&lt;/code&gt; if you don't want to see the test running but where is the fun in that? Another option is &lt;code&gt;slowMo&lt;/code&gt; which slows down Playwright operations by the specified amount of milliseconds and can be helpful to watch the test run. There are various options that can be set while launching the browser, you can go through all of them &lt;a href="https://playwright.dev/docs/api/class-browsertype#browser-type-launch" rel="noopener noreferrer"&gt;here&lt;/a&gt;. After we've finished our operations we will close the browser. This configuration is for before/after all of the tests are run. Now we need to configure what happens when each test scenario is run. For this look at the snippet below:&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;// cucumber.conf.js file&lt;/span&gt;

&lt;span class="c1"&gt;// Create a new browser context and page per scenario&lt;/span&gt;
&lt;span class="nc"&gt;Before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&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="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="nb"&gt;global&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="nb"&gt;global&lt;/span&gt;&lt;span class="p"&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;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Cleanup after each scenario&lt;/span&gt;
&lt;span class="nc"&gt;After&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;global&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="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&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;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;After we've launched our browser we need to create a new browser context. Playwright allows creating &lt;code&gt;incognito&lt;/code&gt; browser contexts with &lt;code&gt;browser.newContext([options])&lt;/code&gt; method. Each browser context has its page that provides methods to interact with a single tab in a browser. We can create a page with &lt;code&gt;context.newPage()&lt;/code&gt; method. Similar to launching a browser we can set a lot of options while creating a &lt;code&gt;browser context&lt;/code&gt; as well like screenshots, record video,  geolocation, and more, you can go through all of them &lt;a href="https://playwright.dev/docs/api/class-browser#browser-new-context" rel="noopener noreferrer"&gt;here&lt;/a&gt;. After we've finished with our operations we close the &lt;code&gt;page&lt;/code&gt; and &lt;code&gt;context&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Voila&lt;/em&gt;, we're done with the configuration part. The whole &lt;code&gt;cucumber.conf.js&lt;/code&gt; file looks like this :&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;// cucumber.conf.js file&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;Before&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="nx"&gt;AfterAll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;After&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDefaultTimeout&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;@cucumber/cucumber&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;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&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;setDefaultTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// launch the browser&lt;/span&gt;
&lt;span class="nc"&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nb"&gt;global&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="nf"&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;slowMo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// close the browser&lt;/span&gt;
&lt;span class="nc"&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Create a new browser context and page per scenario&lt;/span&gt;
&lt;span class="nc"&gt;Before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&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="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="nb"&gt;global&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="nb"&gt;global&lt;/span&gt;&lt;span class="p"&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;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Cleanup after each scenario&lt;/span&gt;
&lt;span class="nc"&gt;After&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;global&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="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&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;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;
  
  
  Writing Tests
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Now some fun stuff, we start writing tests!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our file structure will look like this&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

📦tests
┗ 📂acceptance
┃ ┣ 📂features
┃ ┃ ┗ 📜todo.feature
┃ ┗ 📂stepDefinitions
┃ ┃ ┗ 📜todoContext.js



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

&lt;/div&gt;

&lt;p&gt;Following the above tree create file &lt;code&gt;tests/acceptance/features/todo.feature&lt;/code&gt;. As we are using BDD, we are going to start by writing a feature file and we are going to be using &lt;code&gt;Gherkin&lt;/code&gt; language to do so. If you don't know how to write a feature file or what &lt;code&gt;Gherkin&lt;/code&gt; is you can take the help of the following blogs as it's out of the scope of this blog and won't be explained in detail.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cucumber.io/docs/gherkin/reference/" rel="noopener noreferrer"&gt;cucumber BDD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.modernanalyst.com/Resources/Articles/tabid/115/ID/3871/BDD-An-introduction-to-feature-files.aspx" rel="noopener noreferrer"&gt;BDD – An introduction to feature files&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a basic syntax of what a feature file looks like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Feature: a short description of a software feature
As a user
I want to do this
So I can achieve that

Scenario: name of the scenario
Given [Preconditions or initial context of the system ]
When [Event or action]
Then [Expected output]


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

&lt;/div&gt;

&lt;p&gt;Now assuming you've got some knowledge of feature files and how to write them we proceed further.&lt;/p&gt;

&lt;p&gt;The application that I'm going to be testing is a todo app and the UI looks 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%2F7ufj4f7eqncyc92kdy5e.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%2F7ufj4f7eqncyc92kdy5e.png" alt="Homepage of the app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I want to test if the item I've added is displayed on the UI or not. And the feature file looks like this.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// todo.feature

Feature: todo
 As a user
 I want to add an item to the todo list
 So that I can organize tasks

 Scenario: Add item to the todo list
   Given a user has navigated to the homepage
   # the text inside the quote works as a variable that can be passed to a function
   When the user adds "test" to the todo list using the webUI
   Then card "test" should be displayed on the webUI


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

&lt;/div&gt;

&lt;p&gt;Now we implement each step of the scenario using Playwright! Create a context file &lt;code&gt;tests/acceptance/stepDefinitions/todoContext.js&lt;/code&gt;. We can get a boilerplate for each step in the scenario where we can provide our implementation. For that add the following script in your &lt;code&gt;package.json&lt;/code&gt; file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"test:e2e"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cucumber-js --require cucumber.conf.js --require tests/acceptance/stepDefinitions/**/*.js --format @cucumber/pretty-formatter"&lt;/span&gt;&lt;span class="w"&gt;



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

&lt;/div&gt;

&lt;p&gt;We will be using the &lt;code&gt;test:e2e&lt;/code&gt; script for running the test. Now go to your terminal and run the script&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm run &lt;span class="nb"&gt;test&lt;/span&gt;:e2e tests/acceptance/features/todo.feature


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

&lt;/div&gt;

&lt;p&gt;This will run your feature file. As the steps aren't implemented yet you will get something like this on your screen.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

? Given a user has navigated to the homepage
      Undefined. Implement with the following snippet:

        Given('a user has navigated to the homepage', function () {
          // Write code here that turns the phrase above into concrete actions
&lt;/span&gt;&lt;span class="gp"&gt;          return 'pending';&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;        });&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
  ? When the user adds "test" to the todo list using the webUI
      Undefined. Implement with the following snippet:

        When('the user adds {string} to the todo list using the webUI', function (string) {
          // Write code here that turns the phrase above into concrete actions
&lt;/span&gt;&lt;span class="gp"&gt;          return 'pending';&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;        });&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
  ? Then card "test" should be displayed on the webUI
      Undefined. Implement with the following snippet:

        Then('card {string} should be displayed on the webUI', function (string) {
          // Write code here that turns the phrase above into concrete actions
&lt;/span&gt;&lt;span class="gp"&gt;          return 'pending';&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;        });&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;


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

&lt;/div&gt;

&lt;p&gt;You can now add the generated snippets into your context file and start implementing them.&lt;/p&gt;

&lt;p&gt;Import following&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;// todoContext.js file&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;Given&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;When&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Then&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="s1"&gt;@cucumber/cucumber&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// import expect for assertion&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;expect&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/test&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;Define your launch url and selectors for different UI elements as per need, these are project specific. Playwright supports CSS and Xpath selectors. You can find the detailed information about them &lt;a href="https://playwright.dev/docs/locators#locate-by-css-or-xpath" rel="noopener noreferrer"&gt;here&lt;/a&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;// todoContext.js file&lt;/span&gt;

&lt;span class="c1"&gt;//launch url&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;//define selectors&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;homepageElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.borderTodo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.todo-input&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;todoButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.todo-button&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;todoItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.todo .todo-item &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;Now we can implement the individual test steps, like so&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;// todoContext.js file&lt;/span&gt;

&lt;span class="nc"&gt;Given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a user has navigated to the homepage&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// navigate to the app&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="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="c1"&gt;// locate the element in the webUI&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;locator&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="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;homepageElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="c1"&gt;// assert that it's visible&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nc"&gt;When&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;the user adds {string} to the todo list using the webUI&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// fill the item that was input from the feature file , to the inputText field in the UI&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;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoInput&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="c1"&gt;// click the button&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;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoButton&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nc"&gt;Then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;card {string} should be displayed on the webUI&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// get text of the item that is visible in the UI&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&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="nf"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="c1"&gt;// assert that its name is similar to what we provided&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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 find different methods available to interact with UI elements like click, fill and so on in &lt;a href="https://playwright.dev/docs/api/class-playwright" rel="noopener noreferrer"&gt;Playwright's official documentation&lt;/a&gt;, it's very nicely explained how the function works along with the example code.&lt;/p&gt;

&lt;p&gt;We use the &lt;code&gt;page&lt;/code&gt; that we created in the &lt;code&gt;before&lt;/code&gt; hook to interact with various web elements. Playwright performs &lt;a href="https://playwright.dev/docs/actionability" rel="noopener noreferrer"&gt;autowait&lt;/a&gt; and performs a range of actionability checks on elements and ensures that elements are ready to perform the expected operation. This is one of its plus points. &lt;/p&gt;

&lt;p&gt;This is the whole context file&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;// todoContext.js file&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;Given&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;When&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Then&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="s1"&gt;@cucumber/cucumber&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// import expect for assertion&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;expect&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/test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;//launch url&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;//define selectors&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;homepageElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.borderTodo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.todo-input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.todo-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.todo .todo-item &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;


&lt;span class="nc"&gt;Given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a user has navigated to the homepage&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// navigate to the app&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="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="c1"&gt;// locate the element in the webUI&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;homepageElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="c1"&gt;// assert that it's visible&lt;/span&gt;
   &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nc"&gt;When&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;the user adds {string} to the todo list using the webUI&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// fill the item that was input from the feature file , to the inputText field in the UI&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;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoInput&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="c1"&gt;// click the button&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;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoButton&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nc"&gt;Then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;card {string} should be displayed on the webUI&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// get text of the item that is visible in the UI&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&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="nf"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="c1"&gt;// assert that its name is similar to what we provided&lt;/span&gt;
   &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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;
  
  
  Run the test
&lt;/h2&gt;

&lt;p&gt;First of all, you need to run your application, in my case&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm run start


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

&lt;/div&gt;

&lt;p&gt;Now run the test and watch it in the browser&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm run &lt;span class="nb"&gt;test&lt;/span&gt;:e2e tests/acceptance/features/todo.feature


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

&lt;/div&gt;

&lt;p&gt;You should get a log similar to this one.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;Feature: todo #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tests/acceptance/features/todo.feature:1
&lt;span class="go"&gt;
 As a user
 I want to add an item to the todo list
 So that I can organize tasks

&lt;/span&gt;&lt;span class="gp"&gt; Scenario: Add item to the todo list #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tests/acceptance/features/todo.feature:6
&lt;span class="go"&gt;   Given a user has navigated to the homepage
   When the user adds "test" to the todo list using the webUI
   Then card "test" should be displayed on the webUI

1 scenario (1 passed)
3 steps (3 passed)
0m04.266s (executing steps: 0m04.010s)


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

&lt;/div&gt;

&lt;p&gt;Hopefully, your test also passed like mine and you got to learn about a new library.&lt;br&gt;
You can extend the feature file to add more scenarios or add multiple feature files, implement the Page Object Model as per your requirement and it should all work the same.&lt;/p&gt;

&lt;p&gt;You can find the source code of this implementation &lt;a href="https://github.com/SwikritiT/todo/tree/playwright-blog" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>testing</category>
      <category>e2e</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to automate tests for your website using Nightwatch.js? - Part 2- Writing tests</title>
      <dc:creator>Swikriti Tripathi</dc:creator>
      <pubDate>Fri, 03 Dec 2021 12:55:51 +0000</pubDate>
      <link>https://dev.to/swikritit/how-to-automate-tests-for-your-website-using-nightwatchjs-part-2-writing-tests-4h6a</link>
      <guid>https://dev.to/swikritit/how-to-automate-tests-for-your-website-using-nightwatchjs-part-2-writing-tests-4h6a</guid>
      <description>&lt;p&gt;Before starting this blog please make sure you've followed through the previous &lt;a href="https://dev.to/swikritit/how-to-automate-tests-for-your-website-using-nightwatchjs-part-1-setting-up-nightwatch-53m4"&gt;blog&lt;/a&gt;. This blog is the continuation of the previous one.&lt;/p&gt;

&lt;p&gt;We will be using BDD for writing tests meaning we will be writing a feature file first. We will be using &lt;a href="https://cucumber.io/docs/gherkin/reference/" rel="noopener noreferrer"&gt;Gherkin&lt;/a&gt; to write feature files. &lt;/p&gt;

&lt;p&gt;Through &lt;a href="https://cucumber.io/docs/gherkin/reference/" rel="noopener noreferrer"&gt;Gherkin&lt;/a&gt;, we can write test cases in the native language which will be easier to understand by everyone involved in the project whether they're from the technical field or not. I'm going to explain some of the keywords and their usage, that are used in this blog for further information please visit their official &lt;a href="https://cucumber.io/docs/gherkin/reference/" rel="noopener noreferrer"&gt;website&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Keywords
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Feature&lt;/code&gt;: a short description about a software feature&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Scenario&lt;/code&gt;: list of steps to describe a business rule&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Syntax
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Scenario: name of the scenario
Given [Preconditions or initial context of the system ]
When [Event or action]
Then [Expected output]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Write a feature file
&lt;/h2&gt;

&lt;p&gt;Inside &lt;code&gt;tests/acceptance/&lt;/code&gt; folder make a folder name &lt;code&gt;feature&lt;/code&gt;. Inside this folder make a feature file named &lt;code&gt;todo.feature&lt;/code&gt;, we will be writing a scenario in this file. &lt;/p&gt;

&lt;p&gt;The UI of the application that I'm writing test on looks like this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fua3oo7bshnttuyrrfuxd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fua3oo7bshnttuyrrfuxd.png" alt="UI of a todo app with input fileds and add button" width="697" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcgl2e1pia0qnv262xyl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcgl2e1pia0qnv262xyl.png" alt="UI of a todo app with an item added in the list" width="697" height="704"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can simply add todo items and remove them. For this blog, I'm going to write a test scenario for adding the item and checking if the item is displayed in the UI.&lt;/p&gt;

&lt;p&gt;The feature file &lt;code&gt;todo.feature&lt;/code&gt; looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Feature: todo
  As a user
  I want to add an item to the todo list
  So that I can organize task

  Scenario: Add item to the todo list
    Given a user has navigated to the homepage
    When the user adds "test" to the todo list using the webUI
    Then card "test" should be displayed on the webUI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can add multiple scenarios in the same feature file as per your need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step Definitions
&lt;/h2&gt;

&lt;p&gt;After writing scenarios we need to implement them, for this, we will create &lt;code&gt;stepDefinitions&lt;/code&gt;. Create a folder &lt;code&gt;stepDefinitions&lt;/code&gt; inside &lt;code&gt;tests/acceptance/&lt;/code&gt; and inside &lt;code&gt;setDefinitions&lt;/code&gt; create a file &lt;code&gt;todo.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Add the following script in your package.json in the scripts section&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"test:e2e": "cucumber-js --require tests/acceptance/cucumber.conf.js --require tests/acceptance/stepDefinitions"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will use the &lt;code&gt;test:e2e&lt;/code&gt; script for running the test.&lt;/p&gt;

&lt;p&gt;Run selenium server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Using docker:
docker run -d --network="host" -v /dev/shm:/dev/shm selenium/standalone-chrome-debug
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using jar file: (inside the folder where your selenium server and chromedriver files are)
java -jar &amp;lt;name-of-seleniun-server-standalone&amp;gt;.jar -port 4444
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now go to your terminal and from inside the root directory of your project run the feature file using the following script&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run test:e2e &amp;lt;path-to-yourfeature-file&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run test:e2e tests/acceptance/feature/todo.feature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above script will run the whole feature, if you want to run a particular scenario you can and the line number of the scenario at last like so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run test:e2e tests/acceptance/feature/todo.feature:6

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

&lt;/div&gt;



&lt;p&gt;After running the feature file you will see an output similar to this in your terminal&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failures:

1) Scenario: Add item to todo list # tests/acceptance/feature/todo.feature:6
   ? Given a user has navigated to the homepage
       Undefined. Implement with the following snippet:

         Given('a user has navigated to the homepage', function () {
           // Write code here that turns the phrase above into concrete actions
           return 'pending';
         });

   ? When the user adds "clean room" to the todo list using the webUI
       Undefined. Implement with the following snippet:

         When('the user adds {string} to the todo list using the webUI', function (string) {
           // Write code here that turns the phrase above into concrete actions
           return 'pending';
         });

   ? Then card "clean room" should be displayed on the webUI
       Undefined. Implement with the following snippet:

         Then('card {string} should be displayed on the webUI', function (string) {
           // Write code here that turns the phrase above into concrete actions
           return 'pending';
         });


1 scenario (1 undefined)
3 steps (3 undefined)
0m00.001s (executing steps: 0m00.000s)

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

&lt;/div&gt;



&lt;p&gt;You can copy these code templates and paste in your &lt;code&gt;todo.js&lt;/code&gt; file. This is where we will write the implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Given('a user has navigated to the homepage', function () {
           // Write code here that turns the phrase above into concrete actions
           return 'pending';
         });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First of all, import following in the file &lt;code&gt;todo.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const {Given, When, Then} = require('@cucumber/cucumber')
const {client} = require('nightwatch-api')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now find the CSS or XPath selector of various elements that will be involved in the test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//css selectors
const todoInputField = '.todo-input'
const todoCreateButton = '.todo-button'
const todoListItem = '.todo .todo-item'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can do this by using the developer’s tool in the browser and inspecting each element you need. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgv7r75ra46w9spe5s1z5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgv7r75ra46w9spe5s1z5.png" alt="inspecting an element using developer's tool" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also check if the element is correct by using console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fytidl976wkfpygtjjhia.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fytidl976wkfpygtjjhia.png" alt="making sure the element element is correct by printing it in console" width="800" height="449"&gt;&lt;/a&gt;&lt;br&gt;
Now we implement the steps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Given('a user has navigated to the homepage', function () {
    return client.url("http://localhost:3000");
});


When('the user adds {string} to the todo list using the webUI', async function (item) {
    await client.waitForElementVisible(todoInputField)
        .click(todoInputField)
        .setValue(todoInputField, item)
        .click(todoCreateButton)
    return client
});

Then('card {string} should be displayed on the webUI', function (item) {
    return client.getText(todoListItem, function (result) {
        this.assert.equal(result.value, item)
    })
});

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

&lt;/div&gt;



&lt;p&gt;At the &lt;code&gt;Given&lt;/code&gt; step we navigated to the index page of our UI, this is the precondition. Each action that we perform to achieve certain output needs to be specified at the &lt;code&gt;when&lt;/code&gt; step and at the &lt;code&gt;Then&lt;/code&gt; step we check if the &lt;code&gt;expected output&lt;/code&gt; has been achieved or not. The API commands used can be found on the official website of &lt;a href="https://nightwatchjs.org/api/" rel="noopener noreferrer"&gt;nightwatch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The whole code together looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const {Given, When, Then} = require('@cucumber/cucumber')
const {client} = require('nightwatch-api')

//css selectors
const todoInputField = '.todo-input'
const todoCreateButton = '.todo-button'
const todoListItem = '.todo .todo-item'

Given('a user has navigated to the homepage', function () {
    return client.url("http://localhost:3000");
});


When('the user adds {string} to the todo list using the webUI', async function (item) {
    await client.waitForElementVisible(todoInputField)
        .click(todoInputField)
        .setValue(todoInputField, item)
        .click(todoCreateButton)
    return client
});

Then('card {string} should be displayed on the webUI', function (item) {
    return client.getText(todoListItem, function (result) {
        this.assert.equal(result.value, item)
    })
});

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

&lt;/div&gt;



&lt;p&gt;Now you can run the test again and they should pass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run test:e2e tests/acceptance/feature/todo.feature                                

&amp;gt; todo@0.1.0 test:e2e
&amp;gt; cucumber-js --require tests/acceptance/cucumber.conf.js --require tests/acceptance/stepDefinitions "tests/acceptance/feature/todo.feature"

ℹ Connected to localhost on port 4444 (328ms).
  Using: chrome (87.0.4280.66) on linux platform.

..√ Element &amp;lt;.todo-input&amp;gt; was visible after 69 milliseconds.
.√ Passed [equal]: clean room == clean room
..

1 scenario (1 passed)
3 steps (3 passed)
0m06.385s (executing steps: 0m06.353s)


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

&lt;/div&gt;



&lt;p&gt;Voila, you have successfully written and implemented an acceptance test! You can add more tests and features as per your project and need. Hope this blog helped you!&lt;/p&gt;

&lt;p&gt;You can find the source code &lt;a href="https://github.com/SwikritiT/todo/tree/nightwatch-testing" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>qa</category>
      <category>nightwatch</category>
      <category>testing</category>
    </item>
    <item>
      <title>How to automate tests for your website using Nightwatch.js ? - Part 1- Setting up nightwatch</title>
      <dc:creator>Swikriti Tripathi</dc:creator>
      <pubDate>Fri, 03 Dec 2021 12:45:47 +0000</pubDate>
      <link>https://dev.to/swikritit/how-to-automate-tests-for-your-website-using-nightwatchjs-part-1-setting-up-nightwatch-53m4</link>
      <guid>https://dev.to/swikritit/how-to-automate-tests-for-your-website-using-nightwatchjs-part-1-setting-up-nightwatch-53m4</guid>
      <description>&lt;p&gt;Testing is an essential part of any software project, it helps you ensure that your product has the best quality and  avoid regressions. Manual testing can be effective but, it's time-consuming and has less test coverage. Automated testing, on the other hand, can provide a wide range of coverage in less amount of time and is very applicable for large-scale projects with lots of functionality. In this blog, we're going to write end to end tests for a simple &lt;code&gt;todo&lt;/code&gt; app using  &lt;code&gt;Nightwatch.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Powered by Node.js, Nightwatch.js is an open-source end-to-end test automation tool for web-based applications, browser applications, and websites. For further information and guide in Nightwatch.js, you can visit their &lt;a href="https://nightwatchjs.org/" rel="noopener noreferrer"&gt;official website&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;In this blog, we are going to follow Behaviour Driven Development(BDD) approach, if you don't know what it means or want to learn more about it you can read these blogs&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://inviqa.com/blog/bdd-guide" rel="noopener noreferrer"&gt;The beginner's guide to BDD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cucumber.io/docs/bdd/" rel="noopener noreferrer"&gt;Behaviour-Driven Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dannorth.net/introducing-bdd/" rel="noopener noreferrer"&gt;Introducing BDD&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Note: The commands and setup is Ubuntu-specific&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Node.js installed - &lt;code&gt;sudo apt install nodejs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We will be using docker to run the selenium server so you need to set up docker. You can setup docker in ubuntu with the help of this &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04" rel="noopener noreferrer"&gt;blog&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;If you don't want to set up docker, alternatively you can run selenium in the following ways, but I highly recommend using docker and this blog will be more focused on running selenium via docker.

&lt;ul&gt;
&lt;li&gt;install java &lt;code&gt;sudo apt install default-jdk&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.selenium.dev/downloads/" rel="noopener noreferrer"&gt;Download&lt;/a&gt; the latest 
stable version of selenium server.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://chromedriver.chromium.org/" rel="noopener noreferrer"&gt;Download&lt;/a&gt; the latest 
stable version of chrome driver&lt;/li&gt;
&lt;li&gt;Unzip the chromedriver &lt;code&gt;unzip &amp;lt;name-of-zip-file&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Once the file is unzipped you need to place both &lt;code&gt;selenium 
server&lt;/code&gt; and &lt;code&gt;chromedriver&lt;/code&gt; in the same folder.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;For this blog we are going to use a simple react todo app, you can clone it from &lt;a href="https://github.com/SwikritiT/todo" rel="noopener noreferrer"&gt;github&lt;/a&gt; or if you have your own project you can follow this blog to set up tests there too.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go inside your project and install &lt;code&gt;nightwatch,nightwatch-api and cucumber&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    npm install --dev nightwatch-api nightwatch @cucumber/cucumber 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install &lt;code&gt;selenium-server&lt;/code&gt;, &lt;code&gt;chromedriver&lt;/code&gt; . If you're not using docker and using external &lt;code&gt;selenium-server&lt;/code&gt; and &lt;code&gt;chrome-driver&lt;/code&gt; you can opt-out of this step.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install selenium-server --save-dev  
npm install chromedriver --save-dev    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this, your &lt;code&gt;package.json&lt;/code&gt; should look something like this (versions may vary).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; "devDependencies": {
    "@cucumber/cucumber": "^8.0.0-rc.1",
    "chromedriver": "^96.0.0",
    "nightwatch": "^1.7.12",
    "nightwatch-api": "^3.0.2",
    "selenium-server": "^3.141.59"
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In the &lt;code&gt;root&lt;/code&gt; level create a folder &lt;code&gt;tests&lt;/code&gt;. Our folder structure will look something like this. I'll explain more about it later.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tests
 ┗ acceptance
 ┃ ┣ feature
 ┃ ┃ ┗ todo.feature
 ┃ ┣ stepDefinitions
 ┃ ┃ ┗ todoContext.js
 ┃ ┗ cucumber.conf.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a file named &lt;code&gt;nightwatch.conf.js&lt;/code&gt; in the root level. In this file we will be adding our configuration. You can configure it as you like by consulting the official documentation of &lt;a href="https://nightwatchjs.org/guide/configuration/overview.html" rel="noopener noreferrer"&gt;nightwatch&lt;/a&gt; or you can use the configuration below
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const LAUNCH_URL = process.env.LAUNCH_URL || 'http://localhost:3000';
module.exports = {
    src_folders : ['tests'],
    test_settings: {
        default: {
            launch_url: LAUNCH_URL,
            globals: {},
            selenium: {
                start_process: false,
                host: 'localhost',
                port: 4444,
            },
            desiredCapabilities: {
                browserName: 'chrome',
            },
        },
    },
};

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

&lt;/div&gt;



&lt;p&gt;Here &lt;code&gt;LAUNCH_URL&lt;/code&gt; is the index URL of your project, in our case&lt;br&gt;
&lt;code&gt;localhost:3000&lt;/code&gt;, you can pass this as an environment variable too. We need to specify &lt;code&gt;src_folders&lt;/code&gt; which is the folder where your tests reside, in our case &lt;code&gt;tests&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you're not using docker you can use the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const LAUNCH_URL = process.env.LAUNCH_URL || 'http://localhost:3000';
module.exports = {
    src_folders: ['tests'],
    test_settings: {
        default: {
            selenium_host: '127.0.0.1',
            launch_url: LAUNCH_URL,
            globals: {},
            desiredCapabilities: {
                browserName: 'chrome',
                javascriptEnabled: true,
                chromeOptions: {
                    args: ['disable-gpu'],
                    w3c: false
                }
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;cucumber.conf.js&lt;/code&gt; file inside the &lt;code&gt;tests/acceptance&lt;/code&gt; folder and add the following configuration
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const {setDefaultTimeout, BeforeAll, Before, AfterAll, After} = require('@cucumber/cucumber')
const {startWebDriver, stopWebDriver, createSession, closeSession} = require('nightwatch-api')

setDefaultTimeout(60000)

BeforeAll(async function (){
    await startWebDriver({})
})

Before((async function(){
    await createSession({})
}))

AfterAll(async function(){
    await stopWebDriver()
})

After(async function(){
    await closeSession()
})

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

&lt;/div&gt;



&lt;p&gt;In this file, we specify before and after hooks that will run before and after every test scenario.&lt;/p&gt;

&lt;p&gt;We are done setting up &lt;code&gt;nightwatch.js&lt;/code&gt;. In the next blog, we will learn how to write tests, run selenium server, and run tests.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>nightwatch</category>
      <category>javascript</category>
      <category>qa</category>
    </item>
  </channel>
</rss>
