<?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: Mangai Ram</title>
    <description>The latest articles on DEV Community by Mangai Ram (@magi-magificient).</description>
    <link>https://dev.to/magi-magificient</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%2F1252378%2F8d9346ae-ab65-4542-bebf-04f148f0d46a.png</url>
      <title>DEV Community: Mangai Ram</title>
      <link>https://dev.to/magi-magificient</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/magi-magificient"/>
    <language>en</language>
    <item>
      <title>How I Found a Hidden Login Bug in a Predictive Dialer Using Playwright — And What It Taught Me</title>
      <dc:creator>Mangai Ram</dc:creator>
      <pubDate>Mon, 06 Apr 2026 12:25:16 +0000</pubDate>
      <link>https://dev.to/magi-magificient/how-i-found-a-hidden-login-bug-in-a-predictive-dialer-using-playwright-and-what-it-taught-me-4pe2</link>
      <guid>https://dev.to/magi-magificient/how-i-found-a-hidden-login-bug-in-a-predictive-dialer-using-playwright-and-what-it-taught-me-4pe2</guid>
      <description>&lt;p&gt;&lt;em&gt;Testing a complex telephony platform revealed something unexpected: not all user agents are created equal.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  First, What Even Is a Predictive Dialer?
&lt;/h2&gt;

&lt;p&gt;If you're not from a contact center or sales tech background, you might be wondering — what exactly is a predictive dialer?&lt;/p&gt;

&lt;p&gt;I'll give you the short version before we get into the QA story.&lt;/p&gt;

&lt;p&gt;A predictive dialer is a system used in call centers that &lt;strong&gt;automatically dials phone numbers&lt;/strong&gt; from a contact list and connects answered calls to available agents — all in real time. The "predictive" part means it uses algorithms to predict when an agent will be free and starts dialing the next number &lt;em&gt;before&lt;/em&gt; the current call ends, minimizing idle time.&lt;/p&gt;

&lt;p&gt;Modern predictive dialers — like the ones built on platforms such as &lt;a href="https://www.cleartouch.in/" rel="noopener noreferrer"&gt;ClearTouch&lt;/a&gt; — have evolved far beyond simple auto-dialing. Today's &lt;a href="https://www.cleartouch.in/blog/what-is-ai-dialer-and-how-it-works-in-modern-sales/" rel="noopener noreferrer"&gt;AI-powered dialers&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rank leads by likelihood to answer&lt;/li&gt;
&lt;li&gt;Detect voicemail vs live humans in real time&lt;/li&gt;
&lt;li&gt;Adjust dialing pace dynamically based on agent availability&lt;/li&gt;
&lt;li&gt;Feed context to agents &lt;em&gt;before&lt;/em&gt; the call even begins&lt;/li&gt;
&lt;li&gt;Learn from every call outcome to improve future decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essentially, the platform is a decision engine, not just a phone dialer.&lt;/p&gt;

&lt;p&gt;Now imagine testing that.&lt;/p&gt;




&lt;h2&gt;
  
  
  My QA Assignment: Automated Login Testing for a Dialer Platform
&lt;/h2&gt;

&lt;p&gt;As a QA Specialist, I was tasked with setting up automated end-to-end login tests for a web-based predictive dialer application. The goal was straightforward:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Verify that the login flow works reliably across different browsers and user environments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Simple enough, right? I reached for &lt;strong&gt;Playwright&lt;/strong&gt; — my go-to for browser automation — and got started.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;Here's how I structured the test suite:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;USER_AGENTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Chrome Windows&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Firefox Linux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/121.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Safari macOS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Chrome Android (Mobile)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.144 Mobile Safari/537.36&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Legacy Edge (Old)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Headless Chrome (Bot-like)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/120.0.0.0 Safari/537.36&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="k"&gt;for &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;ua&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;USER_AGENTS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Login should succeed - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;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="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userAgent&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://dialer-app.example.com/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testuser@qa.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SecurePass@123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#login-btn&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*dashboard/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.agent-panel&lt;/span&gt;&lt;span class="dl"&gt;'&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean, readable, scalable. I ran it and... most tests passed.&lt;/p&gt;

&lt;p&gt;But not all.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bug: Certain User Agents Silently Blocked Login
&lt;/h2&gt;

&lt;p&gt;Two specific user agents were failing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Legacy Edge (EdgeHTML 18)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Headless Chrome&lt;/strong&gt; (the raw &lt;code&gt;HeadlessChrome/&lt;/code&gt; string in the UA)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The behavior was strange. The tests weren't throwing errors. The login form was submitting. But instead of redirecting to the dashboard, the page was either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refreshing back to the login screen silently, OR&lt;/li&gt;
&lt;li&gt;Showing a blank white page with no error message&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No toast. No &lt;code&gt;401&lt;/code&gt;. No console error visible to the user. Just... nothing.&lt;/p&gt;

&lt;p&gt;Here's the Playwright assertion that caught it:&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;// This was FAILING for the two problematic UAs&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;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*dashboard/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Error output:&lt;/span&gt;
&lt;span class="c1"&gt;// Error: Timed out 10000ms waiting for expect(page).toHaveURL()&lt;/span&gt;
&lt;span class="c1"&gt;// Expected pattern: /.*dashboard/&lt;/span&gt;
&lt;span class="c1"&gt;// Received string:  "https://dialer-app.example.com/login"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added a screenshot capture to understand what the user was actually seeing:&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;// Added to the test for debugging&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`screenshots/login-fail-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.png`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fullPage&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bodyText&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;'&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] Page body text:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bodyText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The screenshots confirmed it: the login page was re-rendering, credentials cleared, no feedback given to the user. A real user in those environments would simply think the app was broken — or worse, assume their credentials were wrong and get locked out.&lt;/p&gt;




&lt;h2&gt;
  
  
  Digging Deeper: Why Were These UAs Blocked?
&lt;/h2&gt;

&lt;p&gt;I added network request logging to trace what was happening at the API level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;response&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] Auth API → Status: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output for the failing cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Legacy Edge (Old)] Auth API → Status: 403
[Headless Chrome (Bot-like)] Auth API → Status: 403
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There it was. The backend was returning &lt;strong&gt;HTTP 403 Forbidden&lt;/strong&gt; for both — but the frontend was swallowing that response and silently redirecting back to login instead of displaying an error.&lt;/p&gt;

&lt;p&gt;Two bugs in one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bug #1 (Backend):&lt;/strong&gt; The server-side logic was blocking login requests based on user agent string pattern matching — likely an anti-bot or browser compatibility check that was too aggressive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bug #2 (Frontend):&lt;/strong&gt; The application wasn't handling &lt;code&gt;403&lt;/code&gt; responses from the auth API correctly. It should have shown a meaningful error message, but it was silently failing.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  How I Documented and Reported the Bug
&lt;/h2&gt;

&lt;p&gt;I raised two separate tickets with the dev team — one for backend, one for frontend:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bug Report #1 — Backend&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Title:&lt;/strong&gt; Login API returns 403 for Legacy Edge and Headless Chrome user agents&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Severity:&lt;/strong&gt; High&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steps to Reproduce:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send POST to &lt;code&gt;/api/auth/login&lt;/code&gt; with valid credentials&lt;/li&gt;
&lt;li&gt;Set User-Agent header to: &lt;code&gt;Mozilla/5.0 (Windows NT 10.0...) Edge/18.17763&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Observe 403 response&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected:&lt;/strong&gt; 200 with auth token&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actual:&lt;/strong&gt; 403 Forbidden — no descriptive response body&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impact:&lt;/strong&gt; Any agent or supervisor using Legacy Edge cannot log in at all. Automated integration tools and certain CI pipelines using headless Chrome may also be blocked.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Bug Report #2 — Frontend&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Title:&lt;/strong&gt; Login form silently reloads on 403 — no error shown to user&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Severity:&lt;/strong&gt; Medium&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steps to Reproduce:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trigger a 403 from the auth API (see Bug #1)&lt;/li&gt;
&lt;li&gt;Observe frontend behavior&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected:&lt;/strong&gt; Display message: "Login failed. Please check your credentials or contact support."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actual:&lt;/strong&gt; Page refreshes silently. No error, no toast, no redirect explanation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impact:&lt;/strong&gt; Users experience a confusing dead end. No guidance to resolve the issue.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why This Matters for Predictive Dialer Platforms Specifically
&lt;/h2&gt;

&lt;p&gt;This wasn't just a generic web app login bug. The context here matters.&lt;/p&gt;

&lt;p&gt;Predictive dialer platforms are used in &lt;strong&gt;high-pressure, real-time environments&lt;/strong&gt;. A contact center agent starts their shift, opens the dialer, and needs to be connected and ready within minutes. They're not developers. They don't check browser consoles.&lt;/p&gt;

&lt;p&gt;If login silently fails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Calls don't get made.&lt;/strong&gt; The dialer can't pace outbound calls without agents marked as available.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SLAs are missed.&lt;/strong&gt; Outbound campaigns have timing windows — miss them and you lose the opportunity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supervisors can't monitor.&lt;/strong&gt; Real-time dashboards depend on agents being logged into the system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The AI logic breaks.&lt;/strong&gt; Modern dialers like the ones described by ClearTouch learn from agent availability signals. If agents can't log in, the prediction engine is working with incomplete data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What looks like a minor auth bug has cascading effects in a telephony platform.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Future of QA in Predictive Dialer Testing
&lt;/h2&gt;

&lt;p&gt;This experience got me thinking about where QA is heading for platforms like this.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. User Agent Testing Isn't Optional Anymore
&lt;/h3&gt;

&lt;p&gt;Enterprise tools get accessed from aging hardware. Legacy browsers exist in the real world — especially in BPO environments where desktops aren't frequently upgraded. &lt;strong&gt;Testing a matrix of user agents should be standard&lt;/strong&gt;, not an afterthought.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Silent Failures Are the Worst Failures
&lt;/h3&gt;

&lt;p&gt;A crash is obvious. A silent failure hides in production until a real user is hurt by it. Playwright is exceptional at catching these because it makes assertions about &lt;em&gt;final state&lt;/em&gt;, not just the absence of errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. AI Dialers Will Demand AI-Level QA
&lt;/h3&gt;

&lt;p&gt;As predictive dialers become more AI-driven — scoring leads, adapting pacing, feeding pre-call context to agents — the QA surface explodes. You're no longer just testing a UI. You're testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model output consistency&lt;/li&gt;
&lt;li&gt;Real-time API decision latency&lt;/li&gt;
&lt;li&gt;CRM integration data accuracy&lt;/li&gt;
&lt;li&gt;Compliance rule enforcement &lt;em&gt;before&lt;/em&gt; calls are placed&lt;/li&gt;
&lt;li&gt;Voice detection accuracy (human vs voicemail)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright alone won't cut it for all of this. The future is &lt;strong&gt;AI-assisted QA&lt;/strong&gt; — using ML models to generate test cases, detect anomalies in response patterns, and flag regressions in model behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Observability Is a QA Concern
&lt;/h3&gt;

&lt;p&gt;The frontend silently swallowing a 403 was partly a monitoring failure. Future QA pipelines for dialer platforms should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structured logging of all auth events&lt;/li&gt;
&lt;li&gt;Alerting on unexpected 4xx rates at the API layer&lt;/li&gt;
&lt;li&gt;Real user monitoring (RUM) to catch frontend error handling gaps&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Shift-Left on Browser Compatibility
&lt;/h3&gt;

&lt;p&gt;The user agent bug should have been caught in development, not in QA. The lesson: &lt;strong&gt;browser/UA compatibility checks belong in PR review pipelines&lt;/strong&gt;, not just in manual or exploratory testing.&lt;/p&gt;




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

&lt;p&gt;I started this test run expecting to validate a login flow. I ended up discovering two bugs — one backend, one frontend — that would have silently blocked real users in production.&lt;/p&gt;

&lt;p&gt;Playwright gave me the visibility to find what a human tester easily would have missed: a page that &lt;em&gt;looked&lt;/em&gt; like it was doing something, but was actually failing quietly.&lt;/p&gt;

&lt;p&gt;If you're working on a predictive dialer platform — or any enterprise telephony product — don't underestimate the value of testing across user agents. The platform's intelligence means nothing if the agent can't get through the front door.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Are you also doing QA on contact center or telephony platforms? I'd love to hear about the edge cases you've found. Drop them in the comments below.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; &lt;code&gt;#qa&lt;/code&gt; &lt;code&gt;#playwright&lt;/code&gt; &lt;code&gt;#testing&lt;/code&gt; &lt;code&gt;#javascript&lt;/code&gt; &lt;code&gt;#webdev&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;References:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;ClearTouch: &lt;a href="https://www.cleartouch.in/blog/what-is-ai-dialer-and-how-it-works-in-modern-sales/" rel="noopener noreferrer"&gt;AI Dialer — What It Is, How It Works, and Where It Fits in Modern Sales&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Playwright Documentation: &lt;a href="https://playwright.dev/docs/emulation" rel="noopener noreferrer"&gt;Browser Contexts &amp;amp; User Agent&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Hidden Problem in Contact Centers: Broken Customer Journeys (And How Playwright Fixed It)</title>
      <dc:creator>Mangai Ram</dc:creator>
      <pubDate>Wed, 18 Mar 2026 10:50:19 +0000</pubDate>
      <link>https://dev.to/magi-magificient/the-hidden-problem-in-contact-centers-broken-customer-journeys-and-how-playwright-fixed-it-681</link>
      <guid>https://dev.to/magi-magificient/the-hidden-problem-in-contact-centers-broken-customer-journeys-and-how-playwright-fixed-it-681</guid>
      <description>&lt;p&gt;Let’s talk about a problem most teams don’t notice… until customers start complaining.&lt;/p&gt;

&lt;p&gt;Not server crashes.&lt;br&gt;&lt;br&gt;
Not downtime.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Broken customer journeys.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  😬 The Real Issue: Everything Works… But Nothing Works Together
&lt;/h2&gt;

&lt;p&gt;In a modern &lt;a href="https://www.cleartouch.in/cloud-contact-center-solutions/?utm_source=organic&amp;amp;utm_medium=backlink&amp;amp;utm_campaign=seo&amp;amp;utm_content=devto_mangai_2026_03_18" rel="noopener noreferrer"&gt;cloud contact center software&lt;/a&gt;, multiple systems are involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CRM
&lt;/li&gt;
&lt;li&gt;Ticketing tools
&lt;/li&gt;
&lt;li&gt;Call handling systems
&lt;/li&gt;
&lt;li&gt;Chat interfaces
&lt;/li&gt;
&lt;li&gt;Payment or collections systems
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Individually?&lt;br&gt;&lt;br&gt;
✅ They work perfectly.&lt;/p&gt;

&lt;p&gt;But together?&lt;/p&gt;

&lt;p&gt;❌ Things break in between.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚠️ A Real Scenario
&lt;/h2&gt;

&lt;p&gt;Here’s something that actually happens in many support teams:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Customer raises a query via chat
&lt;/li&gt;
&lt;li&gt;Ticket gets created
&lt;/li&gt;
&lt;li&gt;Agent calls the customer
&lt;/li&gt;
&lt;li&gt;After the call → status should update to “Resolved”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But instead 👇&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ticket stays “Open”
&lt;/li&gt;
&lt;li&gt;Follow-up gets triggered again
&lt;/li&gt;
&lt;li&gt;Customer gets duplicate calls
&lt;/li&gt;
&lt;li&gt;Agent gets confused
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Result: Bad customer experience + wasted effort&lt;/p&gt;




&lt;h2&gt;
  
  
  🤯 Why This Happens
&lt;/h2&gt;

&lt;p&gt;Because most testing focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Individual features
&lt;/li&gt;
&lt;li&gt;APIs
&lt;/li&gt;
&lt;li&gt;UI components
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But not on:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔴 &lt;strong&gt;End-to-end user journeys across systems&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that’s where the gap is.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Enter Playwright
&lt;/h2&gt;

&lt;p&gt;Playwright is an end-to-end testing framework that allows teams to test applications the way real users interact with them.&lt;/p&gt;

&lt;p&gt;Instead of testing isolated components, you can validate the entire workflow across systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 How Playwright Helped Fix This
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ 1. End-to-End Workflow Testing
&lt;/h3&gt;

&lt;p&gt;We simulated the full journey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chat initiated
&lt;/li&gt;
&lt;li&gt;Ticket created
&lt;/li&gt;
&lt;li&gt;Agent action
&lt;/li&gt;
&lt;li&gt;Status update
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All in one automated test.&lt;/p&gt;

&lt;p&gt;👉 This exposed exactly where the workflow was breaking.&lt;/p&gt;




&lt;h3&gt;
  
  
  ✅ 2. Cross-System Validation
&lt;/h3&gt;

&lt;p&gt;Playwright helped validate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI updates
&lt;/li&gt;
&lt;li&gt;Backend responses
&lt;/li&gt;
&lt;li&gt;Data consistency across systems
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No more guessing where things failed.&lt;/p&gt;




&lt;h3&gt;
  
  
  ✅ 3. Reliable Automation (No Flaky Tests)
&lt;/h3&gt;

&lt;p&gt;With features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto-waiting
&lt;/li&gt;
&lt;li&gt;Network interception
&lt;/li&gt;
&lt;li&gt;Stable selectors
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tests became consistent and reliable.&lt;/p&gt;




&lt;h3&gt;
  
  
  ✅ 4. Faster Debugging
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;Screenshots
&lt;/li&gt;
&lt;li&gt;Videos
&lt;/li&gt;
&lt;li&gt;Detailed logs
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So developers can quickly identify and fix issues.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 The Impact
&lt;/h2&gt;

&lt;p&gt;After implementing Playwright:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Duplicate follow-ups reduced
&lt;/li&gt;
&lt;li&gt;✅ Ticket statuses updated correctly
&lt;/li&gt;
&lt;li&gt;⏱️ Debugging time decreased
&lt;/li&gt;
&lt;li&gt;😊 Customer experience improved
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 What Changed?
&lt;/h2&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Each system works fine, so the issue must be random.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“We can clearly see where the journey breaks—and fix it fast.”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🚀 Final Thought
&lt;/h2&gt;

&lt;p&gt;Most teams focus on testing features.&lt;/p&gt;

&lt;p&gt;But users don’t experience features.&lt;/p&gt;

&lt;p&gt;👉 They experience &lt;strong&gt;journeys&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And when that journey breaks, it impacts trust.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔥 Key Takeaway
&lt;/h2&gt;

&lt;p&gt;If you're only testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIs
&lt;/li&gt;
&lt;li&gt;UI components
&lt;/li&gt;
&lt;li&gt;Individual modules
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You're missing the bigger picture.&lt;/p&gt;

&lt;p&gt;Start testing like a real user.&lt;/p&gt;

&lt;p&gt;That’s where end-to-end tools like Playwright make a real difference.&lt;/p&gt;




</description>
      <category>ai</category>
      <category>programming</category>
      <category>playwright</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Stop Selenium Tests from Clicking Before the Page is Ready</title>
      <dc:creator>Mangai Ram</dc:creator>
      <pubDate>Thu, 06 Nov 2025 11:30:00 +0000</pubDate>
      <link>https://dev.to/magi-magificient/how-to-stop-selenium-tests-from-clicking-before-the-page-is-ready-1pin</link>
      <guid>https://dev.to/magi-magificient/how-to-stop-selenium-tests-from-clicking-before-the-page-is-ready-1pin</guid>
      <description>&lt;h2&gt;
  
  
  Selenium: Using Explicit Waits for Spinners, Pop-ups &amp;amp; Loading Screens
&lt;/h2&gt;




&lt;h3&gt;
  
  
  Why This Matters
&lt;/h3&gt;

&lt;p&gt;One of the most common problems in Selenium testing is:&lt;br&gt;
Your test tries to click a button, type in a field, or read data &lt;strong&gt;before the page finishes loading.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This leads to errors like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ElementNotInteractableException&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NoSuchElementException&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StaleElementReferenceException&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In simple words → &lt;strong&gt;Selenium is faster than your website.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The browser is still loading, but Selenium is already trying to interact.&lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;Explicit Waits&lt;/strong&gt; help.&lt;/p&gt;


&lt;h3&gt;
  
  
  What Are Explicit Waits?
&lt;/h3&gt;

&lt;p&gt;An &lt;strong&gt;Explicit Wait&lt;/strong&gt; tells Selenium:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Wait until a certain condition is true before moving to the next step.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You control &lt;em&gt;what&lt;/em&gt; to wait for and &lt;em&gt;how long&lt;/em&gt; to wait.&lt;/p&gt;

&lt;p&gt;Unlike general fixed waits (like &lt;code&gt;Thread.sleep(5000)&lt;/code&gt;),&lt;br&gt;
explicit waits are &lt;strong&gt;smart waits&lt;/strong&gt; — they wait &lt;strong&gt;only until needed&lt;/strong&gt;, not longer.&lt;/p&gt;


&lt;h3&gt;
  
  
  Example: Waiting for a Loading Spinner to Disappear
&lt;/h3&gt;

&lt;p&gt;Many modern web apps show a &lt;strong&gt;spinner&lt;/strong&gt; or &lt;strong&gt;loading animation&lt;/strong&gt; while content loads.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Without Explicit Wait&lt;/strong&gt;
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checkout"&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If spinner is still visible → test fails.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;With Explicit Wait&lt;/strong&gt;
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;WebDriverWait&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebDriverWait&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;until&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExpectedConditions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invisibilityOfElementLocated&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cssSelector&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;".spinner"&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checkout"&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now Selenium waits &lt;strong&gt;until the spinner disappears&lt;/strong&gt; before clicking.&lt;/p&gt;


&lt;h3&gt;
  
  
  Example: Waiting for a Modal Popup to Appear
&lt;/h3&gt;

&lt;p&gt;Sometimes you need to wait &lt;em&gt;for&lt;/em&gt; something to show up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;WebDriverWait&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebDriverWait&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;until&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExpectedConditions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visibilityOfElementLocated&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"login-modal"&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures the modal is fully loaded before interacting.&lt;/p&gt;




&lt;h3&gt;
  
  
  Waiting for Network / AJAX Load
&lt;/h3&gt;

&lt;p&gt;Some pages update data &lt;strong&gt;in the background&lt;/strong&gt; (no full page refresh).&lt;br&gt;
Instead of checking time manually, we wait until the page appears “stable.”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebDriverWait&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;until&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webDriver&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nc"&gt;JavascriptExecutor&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;webDriver&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeScript&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"return document.readyState"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"complete"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Wait until page loading is finished.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Why Explicit Waits Are Better Than &lt;code&gt;Thread.sleep()&lt;/code&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Thread.sleep()&lt;/th&gt;
&lt;th&gt;Explicit Wait&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Always waits full time&lt;/td&gt;
&lt;td&gt;Waits only as long as needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slows tests&lt;/td&gt;
&lt;td&gt;Makes tests faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Doesn’t care if page is ready&lt;/td&gt;
&lt;td&gt;Responds to real conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not reliable&lt;/td&gt;
&lt;td&gt;Very reliable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So instead of blindly waiting → Selenium waits &lt;strong&gt;intelligently&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  When to Use Explicit Waits
&lt;/h3&gt;

&lt;p&gt;Use explicit waits when your app has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loading spinners&lt;/li&gt;
&lt;li&gt;Popup messages&lt;/li&gt;
&lt;li&gt;Slow dropdowns&lt;/li&gt;
&lt;li&gt;AJAX-based content loads&lt;/li&gt;
&lt;li&gt;React/Angular dynamic UI updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basically → Anytime the page takes time to update.&lt;/p&gt;




&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Good Practice&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wait for disappearing elements&lt;/td&gt;
&lt;td&gt;Prevent premature clicks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wait for clickable element&lt;/td&gt;
&lt;td&gt;Ensures stability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wait for text/value change&lt;/td&gt;
&lt;td&gt;Validates real-time updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avoid long timeouts&lt;/td&gt;
&lt;td&gt;Speed + accuracy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Real-Life Example
&lt;/h3&gt;

&lt;p&gt;Before waits:&lt;br&gt;
Testers complained that tests passed sometimes and failed sometimes (flaky tests).&lt;/p&gt;

&lt;p&gt;After using explicit waits:&lt;br&gt;
&lt;strong&gt;Stable results&lt;/strong&gt;, fewer failures, and faster execution.&lt;/p&gt;

&lt;p&gt;This is one of the &lt;strong&gt;biggest improvements&lt;/strong&gt; QA leads notice.&lt;/p&gt;




&lt;h3&gt;
  
  
  In Simple Words
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Websites don’t load instantly.&lt;/li&gt;
&lt;li&gt;Selenium is very fast.&lt;/li&gt;
&lt;li&gt;If Selenium tries to interact before the page is ready → the test fails.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Explicit waits fix this by making Selenium pause &lt;em&gt;only&lt;/em&gt; until the page is ready.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  FAQs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Q1:&lt;/strong&gt; Do waits slow down tests?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No. They actually make tests faster because Selenium doesn’t have to retry failed steps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Q2:&lt;/strong&gt; Should we remove &lt;code&gt;Thread.sleep()&lt;/code&gt; completely?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yes — use explicit waits instead. They are smarter and more reliable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Q3:&lt;/strong&gt; Is it hard to convert to explicit waits?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No — just replace direct actions with wait-and-action patterns.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br&gt;
Using &lt;strong&gt;explicit waits&lt;/strong&gt; makes Selenium automation &lt;strong&gt;much more stable&lt;/strong&gt;.&lt;br&gt;
You avoid flaky tests, reduce failures, and let Selenium interact only when the page is ready.&lt;/p&gt;




&lt;p&gt;Still &lt;a href="https://www.testleaf.com/blog/why-automation-testing-with-selenium-is-still-the-1-choice-in-2026/" rel="noopener noreferrer"&gt;Selenium rule the Automation industry?&lt;/a&gt;&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>ai</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Playwright Interview Questions and Answers (My Personal Experience)</title>
      <dc:creator>Mangai Ram</dc:creator>
      <pubDate>Thu, 06 Nov 2025 10:32:27 +0000</pubDate>
      <link>https://dev.to/magi-magificient/playwright-interview-questions-and-answers-my-personal-experience-1l03</link>
      <guid>https://dev.to/magi-magificient/playwright-interview-questions-and-answers-my-personal-experience-1l03</guid>
      <description>&lt;h2&gt;
  
  
  &lt;em&gt;Interview Series — JavaScript Fundamentals for Test Automation&lt;/em&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Q1. What is the difference between &lt;code&gt;slice()&lt;/code&gt; and &lt;code&gt;substring()&lt;/code&gt; in JavaScript?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Both &lt;code&gt;slice()&lt;/code&gt; and &lt;code&gt;substring()&lt;/code&gt; are used to extract part of a string. At first, they look like they work the same — but they behave differently when &lt;strong&gt;negative values&lt;/strong&gt; or &lt;strong&gt;reversed start–end values&lt;/strong&gt; are involved.&lt;/p&gt;

&lt;p&gt;Let’s use one example string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&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;h3&gt;
  
  
  &lt;strong&gt;Basic Extraction&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;     &lt;span class="c1"&gt;// "Play"&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// "Play"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In normal cases, both give the same output.&lt;/p&gt;




&lt;h3&gt;
  
  
  Key Difference 1: &lt;strong&gt;Handling Negative Indices&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;       &lt;span class="c1"&gt;// "right"&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;   &lt;span class="c1"&gt;// "Playwright"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;slice(-5)&lt;/code&gt; → Counts from end&lt;/td&gt;
&lt;td&gt;✅ Returns &lt;code&gt;"right"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;substring(-5)&lt;/code&gt; → Converts &lt;code&gt;-5&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;❌ Returns entire string&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;slice()&lt;/code&gt; &lt;strong&gt;understands negative indexing&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;substring()&lt;/code&gt; &lt;strong&gt;ignores negative indexing&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Key Difference 2: &lt;strong&gt;If Start &amp;gt; End&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;      &lt;span class="c1"&gt;// "" (empty string)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;  &lt;span class="c1"&gt;// "ayw"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;slice()&lt;/code&gt; returns empty when start &amp;gt; end&lt;/td&gt;
&lt;td&gt;Keeps order exactly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;substring()&lt;/code&gt; automatically &lt;strong&gt;swaps the values&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Makes it work anyway&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Comparison Table
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;&lt;code&gt;slice(start, end)&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;substring(start, end)&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Negative index support&lt;/td&gt;
&lt;td&gt;✅ Yes (counts from end)&lt;/td&gt;
&lt;td&gt;❌ No (converted to 0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;If start &amp;gt; end&lt;/td&gt;
&lt;td&gt;❌ Returns empty&lt;/td&gt;
&lt;td&gt;✅ Automatically swaps values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works on&lt;/td&gt;
&lt;td&gt;Strings + Arrays&lt;/td&gt;
&lt;td&gt;Strings only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best Use&lt;/td&gt;
&lt;td&gt;Flexible slicing&lt;/td&gt;
&lt;td&gt;Safe string extraction when values are uncertain&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Internal Explanation (Simple Words)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;slice()&lt;/strong&gt; adjusts negative index by adding it to string length.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;substring()&lt;/strong&gt; treats any negative value as &lt;strong&gt;0&lt;/strong&gt;, and auto-reorders parameters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt;  
&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nx"&gt;treats&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nx"&gt;returns&lt;/span&gt; &lt;span class="nx"&gt;whole&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  How to Answer in an Interview (Simple Version)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"&lt;code&gt;slice()&lt;/code&gt; supports negative indexing and keeps start/end order.&lt;br&gt;
&lt;code&gt;substring()&lt;/code&gt; does not support negative indexing and swaps start and end if needed.&lt;br&gt;
So &lt;code&gt;slice()&lt;/code&gt; is more flexible, while &lt;code&gt;substring()&lt;/code&gt; is safer for simple extractions."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Real QA Usage (Why Testers Should Know This)
&lt;/h3&gt;

&lt;p&gt;When parsing API responses, log messages, or dynamic text:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;slice()&lt;/strong&gt; when taking values from &lt;strong&gt;end&lt;/strong&gt; of string&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;substring()&lt;/strong&gt; when taking values from &lt;strong&gt;beginning range&lt;/strong&gt; safely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: Extract last 4 digits of an ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Related Interview Follow-Up Questions (Highly Asked)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;What is the difference between &lt;code&gt;slice()&lt;/code&gt; and &lt;code&gt;splice()&lt;/code&gt;?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How is &lt;code&gt;substr()&lt;/code&gt; different from &lt;code&gt;slice()&lt;/code&gt;? Why is &lt;code&gt;substr()&lt;/code&gt; deprecated?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How do you reverse a string without using built-in reverse() method?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How do you extract numbers from a string?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What happens if &lt;code&gt;slice()&lt;/code&gt; receives floating number arguments?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Quick Summary (Easy to Remember)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ask Yourself&lt;/th&gt;
&lt;th&gt;Which One to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Need to slice from the end?&lt;/td&gt;
&lt;td&gt;✅ Use &lt;code&gt;slice()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Index values might be reversed?&lt;/td&gt;
&lt;td&gt;✅ Use &lt;code&gt;substring()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Working with arrays?&lt;/td&gt;
&lt;td&gt;✅ Use &lt;code&gt;slice()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple safe string cut?&lt;/td&gt;
&lt;td&gt;✅ Use &lt;code&gt;substring()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




</description>
      <category>playwright</category>
      <category>interview</category>
      <category>career</category>
    </item>
    <item>
      <title>AI Testing Tool : How AI Fixes Broken Test Scripts Automatically</title>
      <dc:creator>Mangai Ram</dc:creator>
      <pubDate>Wed, 05 Nov 2025 12:30:00 +0000</pubDate>
      <link>https://dev.to/magi-magificient/ai-testing-tool-how-ai-fixes-broken-test-scripts-automatically-2j90</link>
      <guid>https://dev.to/magi-magificient/ai-testing-tool-how-ai-fixes-broken-test-scripts-automatically-2j90</guid>
      <description>&lt;p&gt;When people test websites using tools like Selenium or Playwright, their scripts often break whenever the website changes — for example, if a button name or layout changes.&lt;/p&gt;

&lt;p&gt;This blog shows how GenAI can now repair those broken tests on its own — without anyone opening the script to fix it manually.&lt;/p&gt;

&lt;p&gt;It’s about the new wave of “smart automation,” where AI helps testers save time and reduce repetitive work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Common Problem
&lt;/h2&gt;

&lt;p&gt;Imagine this:&lt;br&gt;
You’ve written a test that clicks the “Login” button.&lt;br&gt;
But one day, the developer changes that button to say “Sign In.”&lt;/p&gt;

&lt;p&gt;Now your test fails — because it’s still looking for “Login.”&lt;/p&gt;

&lt;p&gt;Normally, a tester must:&lt;/p&gt;

&lt;p&gt;Find the error,&lt;/p&gt;

&lt;p&gt;Open the test code, and&lt;/p&gt;

&lt;p&gt;Update it manually.&lt;/p&gt;

&lt;p&gt;When there are hundreds of tests, this becomes a nightmare.&lt;/p&gt;

&lt;h2&gt;
  
  
  How AI Changes This
&lt;/h2&gt;

&lt;p&gt;AI tools are now smart enough to “guess” what changed.&lt;br&gt;
They learn what your website looks like — the layout, colors, labels, and positions of elements.&lt;/p&gt;

&lt;p&gt;So when something changes, AI can say:&lt;/p&gt;

&lt;p&gt;“This looks like the new version of that button you used before. I’ll use it automatically.”&lt;/p&gt;

&lt;p&gt;And just like that — the test heals itself and continues running.&lt;/p&gt;

&lt;p&gt;That’s why it’s called “self-healing automation.”&lt;/p&gt;

&lt;p&gt;How AI Knows What to Fix&lt;/p&gt;

&lt;p&gt;Here’s how it works (in simple steps):&lt;/p&gt;

&lt;p&gt;Observe: AI watches all your tests and remembers patterns — like “this button always appears next to the password field.”&lt;/p&gt;

&lt;p&gt;Compare: When the test fails, AI checks what changed on the page.&lt;/p&gt;

&lt;p&gt;Predict: It uses logic and past data to find the new version of that element.&lt;/p&gt;

&lt;p&gt;Heal: It fixes the broken part temporarily or updates it automatically for you.&lt;/p&gt;

&lt;p&gt;Tools That Already Do This&lt;/p&gt;

&lt;p&gt;Testron.ai- Fixes broken locators automatically in Selenium or Playwright.&lt;br&gt;
Testim.io-Uses machine learning to find new elements when names change.&lt;br&gt;
Mabl-Predicts failures and repairs test paths before they break.&lt;br&gt;
Functionize-Lets testers describe tests in plain English; AI writes the code.&lt;/p&gt;

&lt;p&gt;These tools don’t replace your existing test setups — they add intelligence on top of them.&lt;/p&gt;

&lt;p&gt;Real-World Example&lt;/p&gt;

&lt;p&gt;Before AI, at least 10–15 of them broke daily due to small website updates.&lt;br&gt;
After adding AI-assisted healing, the number of failures dropped by 70%.&lt;/p&gt;

&lt;p&gt;That means testers spent more time writing new tests instead of fixing old ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It’s a Game-Changer
&lt;/h2&gt;

&lt;p&gt;✅ Saves time — less manual fixing.&lt;br&gt;
✅ Learns automatically — gets smarter after every test run.&lt;br&gt;
✅ Prevents test breaks — adapts to website changes.&lt;br&gt;
✅ Integrates easily — works with existing tools like Selenium and Playwright.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple Analogy
&lt;/h2&gt;

&lt;p&gt;Think of it like Face ID on your phone.&lt;br&gt;
Even if you get a haircut or wear glasses, your phone still recognizes you.&lt;/p&gt;

&lt;p&gt;Similarly, AI-powered testing recognizes buttons and fields even if they look slightly different after an update.&lt;/p&gt;

&lt;p&gt;In Short&lt;/p&gt;

&lt;p&gt;Old tests break when a website changes.&lt;/p&gt;

&lt;p&gt;AI can now detect and fix those broken parts on its own.&lt;/p&gt;

&lt;p&gt;You get smarter, self-repairing automation that saves huge time.&lt;/p&gt;

&lt;p&gt;It’s the next step after Selenium and Playwright — the future of testing.&lt;/p&gt;

&lt;p&gt;🔗 If You Missed the Previous Blogs&lt;/p&gt;

&lt;p&gt;👉 Part A: &lt;a href="https://dev.to/magi-magificient/a-simple-trick-to-fix-broken-selenium-tests-data-testids-2dkc"&gt;Stop Your Selenium Tests from Breaking — Use data-testids&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 Part B: &lt;a href="https://dev.to/magi-magificient/how-playwright-finds-buttons-and-text-more-smartly-than-selenium-129"&gt;How Playwright Finds Buttons Smartly with Roles &amp;amp; TestIds&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Together, these three blogs show how automation is evolving:&lt;/p&gt;

&lt;p&gt;Selenium (Manual Fixing) → Playwright (Smart Locators) → AI (Self-Healing)&lt;/p&gt;

&lt;p&gt;❓ FAQs&lt;/p&gt;

&lt;p&gt;Q1: Do I need to know AI or coding for this?&lt;br&gt;
No — the tools handle it automatically. You just connect them to your existing tests.&lt;/p&gt;

&lt;p&gt;Q2: Does AI replace human testers?&lt;br&gt;
Not at all. It only removes repetitive fixing work so testers can focus on creative testing.&lt;/p&gt;

&lt;p&gt;Q3: Can I use this with my current Selenium or Playwright setup?&lt;br&gt;
Yes — these tools sit on top of your current system.&lt;/p&gt;

&lt;p&gt;Q4: Is it reliable?&lt;br&gt;
Mostly yes. AI uses “confidence levels” to ensure accuracy and shows a report of what it changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Final Summary
&lt;/h2&gt;

&lt;p&gt;AI in automation testing is like having an assistant who keeps your tests running smoothly, even when your website changes.&lt;/p&gt;

&lt;p&gt;It saves time, prevents frustration, and keeps your testing pipeline strong — 24/7.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
      <category>career</category>
    </item>
    <item>
      <title>How Playwright Finds Buttons and Text More Smartly Than Selenium</title>
      <dc:creator>Mangai Ram</dc:creator>
      <pubDate>Tue, 04 Nov 2025 12:30:00 +0000</pubDate>
      <link>https://dev.to/magi-magificient/how-playwright-finds-buttons-and-text-more-smartly-than-selenium-129</link>
      <guid>https://dev.to/magi-magificient/how-playwright-finds-buttons-and-text-more-smartly-than-selenium-129</guid>
      <description>&lt;p&gt;This post talks about another testing tool called Playwright.&lt;/p&gt;

&lt;p&gt;Do you want to &lt;a href="https://www.testleaf.com/course/playwright.html/?utm_source=MgBklnk" rel="noopener noreferrer"&gt;learn playwright&lt;/a&gt; contact &lt;a href="https://www.testleaf.com/?utm_source=MgBklnk" rel="noopener noreferrer"&gt;testleaf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Think of Playwright as a modern version of Selenium — it does the same job (checking whether a website works) but uses smarter ways to find things on the screen.&lt;/p&gt;

&lt;p&gt;Instead of depending on long, fragile paths like Selenium used to, Playwright looks for meaningful labels such as:&lt;/p&gt;

&lt;p&gt;the role of an element (like button, textbox, link)&lt;/p&gt;

&lt;p&gt;or a special tag (called a testId) that developers can add.&lt;/p&gt;

&lt;p&gt;These new ways make tests more stable, readable, and future-proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple Example
&lt;/h2&gt;

&lt;p&gt;Let’s imagine a “Login” button again.&lt;/p&gt;

&lt;p&gt;With Selenium, a tester might have written:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// old fragile way
page.$("//div[3]/button").click();

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

&lt;/div&gt;



&lt;p&gt;If the page design changes, this line fails.&lt;/p&gt;

&lt;p&gt;With Playwright, you can write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// smart new way
await page.getByRole('button', { name: 'Login' }).click();

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

&lt;/div&gt;



&lt;p&gt;Here Playwright automatically finds the visible button that says “Login.”&lt;br&gt;
Even if the developer changes its position or layout, your test still works.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why It’s Better
&lt;/h2&gt;

&lt;p&gt;Fewer failures when the website changes.&lt;/p&gt;

&lt;p&gt;Easy to read — the code looks almost like plain English.&lt;/p&gt;

&lt;p&gt;No extra waiting — Playwright automatically waits until the button is clickable.&lt;/p&gt;

&lt;p&gt;Works with modern websites built in React, Angular, or Vue.&lt;/p&gt;

&lt;p&gt;What is getByRole() and getByTestId()?&lt;/p&gt;

&lt;p&gt;getByRole() looks for elements based on their function — for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;page.getByRole('textbox', { name: 'Email' });

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

&lt;/div&gt;



&lt;p&gt;means “Find the text box called Email.”&lt;/p&gt;

&lt;p&gt;getByTestId() uses a tag added by developers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input data-testid="email-input" /&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;and in Playwright:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;page.getByTestId('email-input').fill('user@testleaf.com');

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

&lt;/div&gt;



&lt;p&gt;It’s like giving every element a name tag that never changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mixing Both for Best Results
&lt;/h2&gt;

&lt;p&gt;Many testers use both together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const loginBtn = page.getByRole('button', { name: /login/i });
await expect(loginBtn).toBeEnabled();
await loginBtn.click();

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

&lt;/div&gt;



&lt;p&gt;This makes tests extra strong and human-friendly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto-Wait Feature
&lt;/h2&gt;

&lt;p&gt;A big win with Playwright is that it waits automatically for things to load or become clickable — no need to tell it to “wait 5 seconds.”&lt;br&gt;
That means fewer timing errors and faster scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration from Selenium to Playwright:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Selenium Way                           | Playwright Way                                |
| -------------------------------------- | --------------------------------------------- |
| `By.xpath("//button[text()='Login']")` | `page.getByRole('button', { name: 'Login' })` |
| `By.id("submit")`                      | `page.getByTestId('submit')`                  |

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  In Short
&lt;/h2&gt;

&lt;p&gt;Playwright is faster and more intelligent.&lt;/p&gt;

&lt;p&gt;It finds buttons and fields using roles or tags, not messy paths.&lt;/p&gt;

&lt;p&gt;Tests are cleaner, shorter, and more reliable.&lt;/p&gt;

&lt;p&gt;Great for any company using modern web apps.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>selenium</category>
      <category>programming</category>
      <category>ai</category>
    </item>
    <item>
      <title>A Simple Trick to Fix Broken Selenium Tests: "data-testids"</title>
      <dc:creator>Mangai Ram</dc:creator>
      <pubDate>Mon, 03 Nov 2025 12:23:41 +0000</pubDate>
      <link>https://dev.to/magi-magificient/a-simple-trick-to-fix-broken-selenium-tests-data-testids-2dkc</link>
      <guid>https://dev.to/magi-magificient/a-simple-trick-to-fix-broken-selenium-tests-data-testids-2dkc</guid>
      <description>&lt;p&gt;This article talks about how software testers find and click buttons, links, or text on a website automatically using a tool called Selenium.&lt;/p&gt;

&lt;p&gt;But there’s a problem — sometimes those automatic tests break when the website’s design changes even slightly.&lt;/p&gt;

&lt;p&gt;The blog teaches how to avoid that by using something called data-testid — a small label that developers can add to buttons or fields to make them easy for the test scripts to find every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple Example
&lt;/h2&gt;

&lt;p&gt;Let’s say you have a website with a “Login” button.&lt;br&gt;
Normally, a tester would write something like:&lt;/p&gt;

&lt;p&gt;“Click the 3rd button inside that box.”&lt;/p&gt;

&lt;p&gt;But if the page layout changes, that 3rd button might move — and the test breaks.&lt;/p&gt;

&lt;p&gt;Instead, developers can add a label:&lt;/p&gt;

&lt;p&gt;Login&lt;/p&gt;

&lt;p&gt;Now the test can just say:&lt;/p&gt;

&lt;p&gt;“Click the button with label login-button.”&lt;/p&gt;

&lt;p&gt;No matter how the design changes, the test still works.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Learn
&lt;/h2&gt;

&lt;p&gt;Old-style testing used complicated “paths” (called XPaths) to find elements.&lt;/p&gt;

&lt;p&gt;These paths often break when the website changes.&lt;/p&gt;

&lt;p&gt;Using data-testids makes your tests much more stable and future-proof.&lt;/p&gt;

&lt;p&gt;It also helps testers and developers work together better, since both agree on the labels to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Example
&lt;/h2&gt;

&lt;p&gt;Imagine an e-commerce site with 100 buttons and forms.&lt;br&gt;
Earlier, every small UI change broke many test scripts.&lt;br&gt;
After switching to data-testids, testers didn’t have to fix broken tests every day — saving hours of time.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Short
&lt;/h2&gt;

&lt;p&gt;Old method = fragile, keeps breaking&lt;/p&gt;

&lt;p&gt;New method (data-testid) = strong, stable, easy to manage&lt;/p&gt;

&lt;p&gt;Helps testers find website elements easily, even after design changes&lt;/p&gt;

&lt;p&gt;Makes the testing process faster and more reliable&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>java</category>
      <category>automation</category>
    </item>
    <item>
      <title>How I helped 200+ manual testers move into automation testing</title>
      <dc:creator>Mangai Ram</dc:creator>
      <pubDate>Tue, 28 Oct 2025 06:44:31 +0000</pubDate>
      <link>https://dev.to/magi-magificient/how-i-helped-200-manual-testers-move-into-automation-testing-54i3</link>
      <guid>https://dev.to/magi-magificient/how-i-helped-200-manual-testers-move-into-automation-testing-54i3</guid>
      <description>&lt;p&gt;Body:&lt;br&gt;
When I started mentoring testers, I noticed one pattern — most were stuck not because of a lack of knowledge, but because they didn’t know where to start.&lt;/p&gt;

&lt;p&gt;So, we created a roadmap that worked — from manual to automation, step by step.&lt;/p&gt;

&lt;p&gt;Here’s what it includes:&lt;br&gt;
✅ The right order to learn tools (Selenium → Playwright → API → AI-assisted testing)&lt;br&gt;
✅ How to make your resume reflect automation skills&lt;br&gt;
✅ How to build small projects that attract recruiters&lt;/p&gt;

&lt;p&gt;If you’re exploring the same path, I’ve shared the detailed guide here 👉&lt;a href="https://www.testleaf.com/blog/career-roadmap-for-functional-test-automation-developers/" rel="noopener noreferrer"&gt;https://www.testleaf.com/blog/career-roadmap-for-functional-test-automation-developers/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>javascript</category>
      <category>playwright</category>
    </item>
    <item>
      <title>Playwright Interview Questions and Answers (My Personal Experience)</title>
      <dc:creator>Mangai Ram</dc:creator>
      <pubDate>Wed, 22 Oct 2025 10:25:56 +0000</pubDate>
      <link>https://dev.to/magi-magificient/playwright-interview-questions-and-answers-my-personal-experience-4enl</link>
      <guid>https://dev.to/magi-magificient/playwright-interview-questions-and-answers-my-personal-experience-4enl</guid>
      <description>&lt;p&gt;If you’re preparing for a QA automation interview, mastering Playwright can help you stand out from the crowd. This 2025 updated guide covers the most common and advanced Playwright interview questions — including real-world coding scenarios, differences from Selenium, and hands-on best practices. &lt;/p&gt;

&lt;p&gt;Whether you’re a beginner or an experienced automation engineer, this article will help you gain clarity and confidence for your next interview. &lt;/p&gt;

&lt;p&gt;Beginner-Level Playwright Interview Questions &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is Playwright? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answer:&lt;br&gt;
Playwright is a modern end-to-end testing framework developed by Microsoft, designed to automate browsers (Chromium, Firefox, WebKit).&lt;br&gt;
It supports cross-browser, cross-platform, and multi-language testing (JS/TS, Python, Java, .NET). &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you handle waiting / synchronization issues in Playwright? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answer:&lt;br&gt;
Playwright auto-waits for elements to become actionable before performing any action.&lt;br&gt;
Still, you can use: &lt;/p&gt;

&lt;p&gt;await page.waitForSelector('#submit');&lt;br&gt;
await expect(page.locator('#success')).toBeVisible();&lt;br&gt;
await page.waitForLoadState('networkidle');&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Types: &lt;/p&gt;

&lt;p&gt;Auto-wait → built-in for actions like .click(), .fill() &lt;br&gt;
Explicit wait → page.waitForSelector(), expect(locator).toBeVisible() &lt;br&gt;
No implicit waits like Selenium (Playwright avoids flakiness by design). &lt;/p&gt;

&lt;p&gt;Other Recommended Reads: api automation interview questions&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you run tests in parallel or in multiple browsers/devices/contexts? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answer: &lt;/p&gt;

&lt;p&gt;Parallel Execution: Each test runs in an isolated worker. &lt;br&gt;
Multiple Browsers/Devices: Defined via projects in playwright.config.ts &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;export default defineConfig({ &lt;/p&gt;

&lt;p&gt;  fullyParallel: true, &lt;/p&gt;

&lt;p&gt;  workers: 4, &lt;/p&gt;

&lt;p&gt;  projects: [ &lt;/p&gt;

&lt;p&gt;    { name: 'chromium', use: { browserName: 'chromium' } }, &lt;/p&gt;

&lt;p&gt;    { name: 'firefox', use: { browserName: 'firefox' } }, &lt;/p&gt;

&lt;p&gt;    { name: 'webkit', use: { browserName: 'webkit' } }, &lt;/p&gt;

&lt;p&gt;  ], &lt;/p&gt;

&lt;p&gt;}); &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you run Playwright tests headlessly vs headed? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answer: &lt;/p&gt;

&lt;p&gt;Headless: For CI/CD environments (default). &lt;br&gt;
Headed: For debugging or local runs. &lt;/p&gt;

&lt;p&gt;npx playwright test --headed&lt;br&gt;
npx playwright test --headless&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Or in config: &lt;/p&gt;

&lt;p&gt;use: { headless: false } &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you manage environment configuration (dev/stage/prod)? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answer: &lt;/p&gt;

&lt;p&gt;Use .env files or JSON config files. &lt;br&gt;
Access variables via process.env. &lt;/p&gt;

&lt;p&gt;Example: &lt;/p&gt;

&lt;p&gt;BASE_URL=&lt;a href="https://staging.example.com" rel="noopener noreferrer"&gt;https://staging.example.com&lt;/a&gt;&lt;br&gt;
 export default defineConfig({&lt;br&gt;
 use: { baseURL: process.env.BASE_URL }&lt;br&gt;
}); &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What makes a “good API”? What best practices do you follow? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answer:&lt;br&gt;
A good API is consistent, predictable, secure, and well-versioned. &lt;/p&gt;

&lt;p&gt;Best Practices: &lt;/p&gt;

&lt;p&gt;Proper HTTP verbs (GET, POST, PUT, DELETE) &lt;br&gt;
Correct status codes (200, 201, 400, 404, 500) &lt;br&gt;
Consistent naming conventions &lt;br&gt;
Pagination &amp;amp; filtering &lt;br&gt;
Versioning (/v1/users) &lt;br&gt;
Proper authentication (OAuth/JWT) &lt;br&gt;
Meaningful error messages &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you handle authentication &amp;amp; authorization in APIs? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answer: &lt;/p&gt;

&lt;p&gt;Use JWT tokens, API keys, or OAuth2. &lt;br&gt;
Fetch token via a login request, reuse it across tests. &lt;/p&gt;

&lt;p&gt;const api = await request.newContext({&lt;br&gt;
 extraHTTPHeaders: { Authorization: &lt;code&gt;Bearer ${token}&lt;/code&gt; }&lt;br&gt;
}); &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you ensure your test automation code is maintainable and scalable? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answer: &lt;/p&gt;

&lt;p&gt;Use Page Object Model (POM). &lt;br&gt;
Centralize locators and common logic. &lt;br&gt;
Use fixtures for setup/teardown. &lt;br&gt;
Parameterize environment/config. &lt;br&gt;
Implement clear naming conventions and folder structure. &lt;br&gt;
Continuous refactoring, DRY principle, and TypeScript typings. &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you build custom fixtures in Playwright? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answer:&lt;br&gt;
Fixtures let you share setup/teardown logic. &lt;/p&gt;

&lt;p&gt;import { test as base } from '@playwright/test';&lt;/p&gt;

&lt;p&gt;type MyFixtures = { loginPage: LoginPage };&lt;br&gt;
const test = base.extend({&lt;br&gt;
 loginPage: async ({ page }, use) =&amp;gt; {&lt;br&gt;
   const login = new LoginPage(page);&lt;br&gt;
   await use(login);&lt;br&gt;
 },&lt;br&gt;
});&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Intermediate-Level Playwright Interview Questions &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is the difference between browser, context, and page in Playwright? 
Object     Description 
Browser    Top-level instance (e.g., Chromium). 
Context    Isolated incognito-like session within a browser. 
Page   Represents a single tab in a browser context. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you select elements? What are locators? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answer:&lt;br&gt;
Locators are Playwright’s recommended way to find elements.&lt;br&gt;
They are lazy-evaluated and auto-wait. &lt;/p&gt;

&lt;p&gt;const loginBtn = page.locator('button:has-text("Login")');&lt;br&gt;
await loginBtn.click(); &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is a fixture in Playwright Test? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answer:&lt;br&gt;
A fixture is a reusable setup resource (like browser context, page, API client).&lt;br&gt;
Playwright provides built-in fixtures like page, context, request.&lt;br&gt;
You can extend them for custom object &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you handle file uploads or downloads? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Upload: &lt;/p&gt;

&lt;p&gt;await page.setInputFiles('input[type="file"]', 'tests/data/sample.pdf');&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Download: &lt;/p&gt;

&lt;p&gt;const [download] = await Promise.all([&lt;br&gt;
 page.waitForEvent('download'),&lt;br&gt;
 page.click('text=Download File'),&lt;br&gt;
]);&lt;br&gt;
await download.saveAs('downloads/file.pdf'); &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Difference between page.locator(selector).click() and page.click(selector) 
Method     Behavior 
page.locator().click()     Uses Locator API (auto-wait, retries). ✅ 
page.click() 
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Executes immediately (can be &lt;/p&gt;

&lt;p&gt; flaky). ⚠️ &lt;/p&gt;

&lt;p&gt;Additional Resources: product based companies in bangalore&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How to assert something in Playwright Test? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use expect() API: &lt;/p&gt;

&lt;p&gt;await expect(page).toHaveURL(/dashboard/);&lt;br&gt;
await expect(locator).toBeVisible();&lt;br&gt;
await expect(locator).toHaveText('Welcome'); &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Difference between page.waitForLoadState('networkidle') and waitForNavigation() 
Method     Purpose 
waitForLoadState('networkidle')    Waits for no network connections for 500ms. 
waitForNavigation()    Waits for a navigation event triggered by an action. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use waitForLoadState when waiting for background requests, not full navigation. &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How to use test hooks and where to place them? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hooks manage setup/cleanup: &lt;/p&gt;

&lt;p&gt;test.beforeAll(...)&lt;br&gt;
test.beforeEach(...)&lt;br&gt;
test.afterEach(...)&lt;br&gt;
test.afterAll(...)&lt;/p&gt;

&lt;p&gt;Placed inside test.describe() or globally in test files.&lt;br&gt;
Best practice: use hooks for test data setup and teardown. &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you generate and publish HTML / Allure / custom reports? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;HTML report: &lt;/p&gt;

&lt;p&gt;npx playwright test --reporter=html&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Allure report: &lt;/p&gt;

&lt;p&gt;npm install --save-dev allure-playwright&lt;br&gt;
npx playwright test --reporter=allure-playwright&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Publishing in GitHub Actions: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run: npx playwright test --reporter=html&lt;/li&gt;
&lt;li&gt;uses: actions/upload-artifact@v4
 with:
   name: report
   path: playwright-report/
 &lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Can Playwright be used for API testing? How? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Yes.&lt;br&gt;
Playwright provides APIRequestContext to perform REST API testing. &lt;/p&gt;

&lt;p&gt;const api = await request.newContext();&lt;br&gt;
const res = await api.get('&lt;a href="https://api.example.com/users'" rel="noopener noreferrer"&gt;https://api.example.com/users'&lt;/a&gt;);&lt;br&gt;
expect(res.status()).toBe(200);&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Advanced Playwright Interview Questions for Experienced Testers &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you create an API request context with authentication? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;const api = await request.newContext({&lt;br&gt;
 baseURL: '&lt;a href="https://api.example.com" rel="noopener noreferrer"&gt;https://api.example.com&lt;/a&gt;',&lt;br&gt;
 extraHTTPHeaders: {&lt;br&gt;
   Authorization: &lt;code&gt;Bearer ${token}&lt;/code&gt;,&lt;br&gt;
   'Content-Type': 'application/json',&lt;br&gt;
 },&lt;br&gt;
});&lt;br&gt;
 &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you chain API calls (use one response to feed another)? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;const createUser = await api.post('/users', { data: { name: 'John' } });&lt;br&gt;
const id = (await createUser.json()).id;&lt;br&gt;
const getUser = await api.get(&lt;code&gt;/users/${id}&lt;/code&gt;);&lt;br&gt;
expect(getUser.status()).toBe(200);&lt;br&gt;
 &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is Playwright’s APIRequestContext and how is it used for API testing? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It represents an isolated API session that can: &lt;/p&gt;

&lt;p&gt;Send REST requests &lt;br&gt;
Store cookies, headers, and auth data &lt;br&gt;
Reuse between tests &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do you architect a Playwright test suite for a large codebase? &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Best practice structure: &lt;/p&gt;

&lt;p&gt;├── pages/&lt;br&gt;
├── api/&lt;br&gt;
├── fixtures/&lt;br&gt;
└── utils/&lt;br&gt;
├── tests/&lt;br&gt;
│   ├── ui/&lt;br&gt;
│   └── api/&lt;br&gt;
├── playwright.config.ts&lt;br&gt;
└── package.json&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Principles: &lt;/p&gt;

&lt;p&gt;Reusable page &amp;amp; API classes &lt;br&gt;
Configurable environments &lt;br&gt;
Modular utilities &amp;amp; fixtures &lt;br&gt;
Parallel-safe test data setup &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How to parallelize tests safely when they share resources? 
Avoid shared mutable data. 
Use unique test data per worker (uuid, timestamp). 
Isolate environments using fixtures. 
Use locks or queues only when unavoidable (e.g., shared DB). &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Basic JS: What’s the difference between == and ===? 
Operator   Meaning 
==     Equality with type coercion 
===    Strict equality, no type conversion &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example: &lt;/p&gt;

&lt;p&gt;'5' == 5   // true&lt;br&gt;
'5' === 5  // false&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;26 : How do you handle dropdowns with  and  in Playwright? &lt;/p&gt;

&lt;p&gt;Answer:&lt;br&gt;
In Playwright, you handle  dropdowns using the selectOption() method. It allows selecting an option by value, label, or index. For multi-select dropdowns, you can pass an array of values. &lt;/p&gt;

&lt;p&gt;Example: &lt;/p&gt;

&lt;p&gt;import { test, expect } from '@playwright/test';&lt;/p&gt;

&lt;p&gt;test('Select dropdown option', async ({ page }) =&amp;gt; {&lt;br&gt;
 await page.goto('&lt;a href="https://example.com'" rel="noopener noreferrer"&gt;https://example.com'&lt;/a&gt;);&lt;/p&gt;

&lt;p&gt; // Select by value&lt;br&gt;
 await page.selectOption('#country', 'ca'); // Canada&lt;/p&gt;

&lt;p&gt; // Select by label&lt;br&gt;
 await page.selectOption('#country', { label: 'United Kingdom' });&lt;/p&gt;

&lt;p&gt; // Select by index&lt;br&gt;
 await page.selectOption('#country', { index: 0 }); // United States&lt;/p&gt;

&lt;p&gt; // Verify selection&lt;br&gt;
 const selectedValue = await page.$eval('#country', el =&amp;gt; (el as HTMLSelectElement).value);&lt;br&gt;
 expect(selectedValue).toBe('us');&lt;br&gt;
});&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Key Points: &lt;/p&gt;

&lt;p&gt;Works only with  elements. &lt;br&gt;
Can select by value, label, or index. &lt;br&gt;
Supports multi-select: await page.selectOption('#multi', ['us', 'uk']). &lt;br&gt;
Playwright automatically handles the dropdown; no manual click needed. &lt;/p&gt;

&lt;p&gt;27 : How do you handle a dropdown that dynamically loads options based on previous selection? &lt;/p&gt;

&lt;p&gt;Answer: &lt;/p&gt;

&lt;p&gt;First, select the parent dropdown option using selectOption(). &lt;br&gt;
Wait for the child dropdown to be populated using page.waitForSelector() or locator.waitFor(). &lt;br&gt;
Then, select the desired child option. &lt;/p&gt;

&lt;p&gt;Example: &lt;/p&gt;

&lt;p&gt;await page.selectOption('#country', 'us');&lt;br&gt;
await page.waitForSelector('#state option:not(:empty)');&lt;br&gt;
await page.selectOption('#state', 'ca'); // Select California &lt;/p&gt;

&lt;p&gt;28: Difference between var, let, and const &lt;br&gt;
Feature    var    let    const &lt;br&gt;
Scope  Function-scoped    Block-scoped   Block-scoped &lt;br&gt;
Redeclaration  Allowed    Not allowed    Not allowed &lt;br&gt;
Re-assignment  Allowed    Allowed    Not allowed (must be initialized) &lt;br&gt;
Hoisting   Yes (initialized as undefined)     Yes (temporal dead zone)   Yes (temporal dead zone) &lt;br&gt;
Use case   Legacy code    Modern variables   Constants / immutable references &lt;/p&gt;

&lt;p&gt;Key points: &lt;/p&gt;

&lt;p&gt;Use let for variables that change. &lt;br&gt;
Use const for values that shouldn’t change. &lt;br&gt;
Avoid var in modern JavaScript due to scoping issues. &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;29: How do Promises to differ from callbacks? &lt;br&gt;
Feature    Callbacks  Promises &lt;br&gt;
Syntax     Function passed as argument    Object with .then() / .catch() &lt;br&gt;
Handling async     Nested or “callback hell”  Chainable, avoids nested callbacks &lt;br&gt;
Error handling     Needs try/catch inside callback    Built-in .catch() for errors &lt;br&gt;
Execution  Executes immediately when function runs    Executes asynchronously and resolves later &lt;/p&gt;

&lt;p&gt;Example: &lt;/p&gt;

&lt;p&gt;// Callback&lt;br&gt;
doSomething(function(result) {&lt;br&gt;
 doSomethingElse(result, function(newResult) {&lt;br&gt;
   console.log(newResult);&lt;br&gt;
 });&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;// Promise&lt;br&gt;
doSomething()&lt;br&gt;
 .then(result =&amp;gt; doSomethingElse(result))&lt;br&gt;
 .then(newResult =&amp;gt; console.log(newResult))&lt;br&gt;
 .catch(error =&amp;gt; console.error(error));&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Summary for Interview: &lt;/p&gt;

&lt;p&gt;Promises provide better readability and error handling than callbacks. &lt;br&gt;
Helps avoid “callback hell” for complex async code. &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;30: What is Hoisting? &lt;/p&gt;

&lt;p&gt;Definition:&lt;br&gt;
Hoisting is JavaScript’s behavior of moving variable and function declarations to the top of their scope during compilation. &lt;/p&gt;

&lt;p&gt;Key Points: &lt;/p&gt;

&lt;p&gt;Only declarations are hoisted, not initializations. &lt;br&gt;
var variables are hoisted with value undefined. &lt;br&gt;
let and const are hoisted but in a temporal dead zone (TDZ) until initialization. &lt;br&gt;
Function declarations are fully hoisted, but function expressions are not. &lt;/p&gt;

&lt;p&gt;Example: &lt;/p&gt;

&lt;p&gt;console.log(a); // undefined&lt;br&gt;
var a = 10;&lt;/p&gt;

&lt;p&gt;console.log(b); // ReferenceError&lt;br&gt;
let b = 20;&lt;/p&gt;

&lt;p&gt;foo(); // Works&lt;br&gt;
function foo() { console.log('Hello'); }&lt;/p&gt;

&lt;p&gt;bar(); // Error&lt;br&gt;
const bar = function() { console.log('Hi'); } &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Playwright vs Selenium — Key Differences &lt;br&gt;
Feature    Playwright     Selenium &lt;br&gt;
Auto-waiting   ✅ Built-in   ❌ Manual waits &lt;br&gt;
Browser Contexts   ✅ Yes    ❌ Needs multiple drivers &lt;br&gt;
Network Mocking    ✅ Easy   ⚠️ Limited &lt;br&gt;
Speed  ⚡ Fast (CDP protocol)    🐢 Slower (HTTP JSON wire) &lt;br&gt;
API Testing    ✅ Built-in   ❌ External tools needed &lt;/p&gt;

&lt;p&gt;💡 Pro Tip: Prefer page.locator() over page.click() for more reliable element handling. &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Bonus: Real-Time Playwright Coding Questions &lt;br&gt;
Write a Playwright script to log into a website and verify the title. &lt;br&gt;
How do you capture screenshots and videos in Playwright? &lt;br&gt;
Demonstrate API testing with Playwright using APIRequestContext. &lt;br&gt;
How do you handle multiple tabs and pop-ups? &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Playwright Framework Best Practices &lt;br&gt;
Use Page Object Model (POM) for scalability. &lt;br&gt;
Integrate with CI/CD tools like Jenkins or GitHub Actions. &lt;br&gt;
Implement test tagging for selective execution. &lt;br&gt;
Maintain reusable test data and environment configs. &lt;/p&gt;

&lt;p&gt;Playwright Interview Preparation Tips &lt;/p&gt;

&lt;p&gt;✅ Practice automation challenges on GitHub repositories. &lt;/p&gt;

&lt;p&gt;✅ Review Playwright’s official documentation weekly. &lt;/p&gt;

&lt;p&gt;✅ Mock interviews help improve confidence. &lt;/p&gt;

&lt;p&gt;✅ Learn Playwright with real projects — not just syntax. &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Conclusion: How to Prepare for a Playwright Interview &lt;/p&gt;

&lt;p&gt;Playwright is redefining automation with speed, reliability, and built-in testing features. Whether you’re transitioning from Selenium or starting fresh, understanding these questions and concepts will make you interview-ready. &lt;/p&gt;

&lt;p&gt;If you want hands-on mastery, explore a Playwright course online that blends practical frameworks and interview guidance — the key to building your automation career in 2025. &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;FAQs &lt;/p&gt;

&lt;p&gt;Q1. What is Playwright used for? &lt;/p&gt;

&lt;p&gt;Playwright is used for end-to-end web automation testing across browsers and devices. &lt;/p&gt;

&lt;p&gt;Q2. Is Playwright better than Selenium? &lt;/p&gt;

&lt;p&gt;Yes. Playwright offers faster execution, auto-waiting, and API testing support. &lt;/p&gt;

&lt;p&gt;Q3. What are the most asked Playwright topics in interviews? &lt;/p&gt;

&lt;p&gt;Locators, waits, fixtures, POM, API testing, and Playwright vs Selenium. &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

</description>
      <category>playwright</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>testing</category>
    </item>
    <item>
      <title>Use Case: Simulating a Payment Gateway Timeout with Playwright</title>
      <dc:creator>Mangai Ram</dc:creator>
      <pubDate>Tue, 19 Aug 2025 09:34:45 +0000</pubDate>
      <link>https://dev.to/magi-magificient/use-case-simulating-a-payment-gateway-timeout-with-playwright-253k</link>
      <guid>https://dev.to/magi-magificient/use-case-simulating-a-payment-gateway-timeout-with-playwright-253k</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;During testing, the payment flow needed to be validated for failure scenarios like gateway timeout. But the actual payment service was managed by a third-party provider, and modifying or slowing it down for testing was not possible due to compliance and contractual restrictions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge
&lt;/h2&gt;

&lt;p&gt;Couldn’t directly trigger timeouts in the real payment system.&lt;/p&gt;

&lt;p&gt;Needed to ensure the application displayed the correct error messages, handled retries, and logged the failure properly.&lt;/p&gt;

&lt;p&gt;Manually waiting for rare real-time outages was not practical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution (with Playwright)
&lt;/h2&gt;

&lt;p&gt;Using Playwright’s network mocking capability, the payment API call was intercepted and programmed to:&lt;/p&gt;

&lt;p&gt;Simulate a 408 timeout response.&lt;/p&gt;

&lt;p&gt;Delay the response beyond the configured threshold.&lt;/p&gt;

&lt;p&gt;Return a controlled failure payload for consistency across tests.&lt;/p&gt;

&lt;p&gt;This way, the timeout scenario could be reproduced on-demand without touching the live payment gateway.&lt;/p&gt;

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

&lt;p&gt;Application’s error handling and retry logic were successfully validated.&lt;/p&gt;

&lt;p&gt;Saved significant time by avoiding dependency on external payment provider outages.&lt;/p&gt;

&lt;p&gt;Enabled QA team to cover edge cases that are critical for user trust and compliance.&lt;/p&gt;

&lt;p&gt;Would like you to know more use case related to Playwright , one of my mentor trained this in &lt;a href="//testleaf.com?utm-source=mgbklng"&gt;Testleaf &lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;you want to &lt;a href="https://www.testleaf.com/course/playwright.html?utm-source=mgbklng" rel="noopener noreferrer"&gt;learn Playwright&lt;/a&gt; with me or you need &lt;a href="https://www.testleaf.com/course/playwright.html?utm-source=mgbklng" rel="noopener noreferrer"&gt;playwright training&lt;/a&gt; reach me .&lt;/p&gt;

</description>
      <category>testing</category>
      <category>automation</category>
      <category>playwright</category>
    </item>
    <item>
      <title>Why I Switched to Playwright? ans: Playwright Assertions</title>
      <dc:creator>Mangai Ram</dc:creator>
      <pubDate>Tue, 22 Jul 2025 05:53:25 +0000</pubDate>
      <link>https://dev.to/magi-magificient/why-i-switched-to-playwright-ans-playwright-assertions-1cb3</link>
      <guid>https://dev.to/magi-magificient/why-i-switched-to-playwright-ans-playwright-assertions-1cb3</guid>
      <description>&lt;p&gt;I didn’t switch to Playwright just because it’s the “new cool tool.” &lt;/p&gt;

&lt;p&gt;I switched because I was tired of writing flaky tests that passed locally and failed on CI. &lt;/p&gt;

&lt;p&gt;Selenium, for all its legacy strengths, often left me guessing:&lt;/p&gt;

&lt;p&gt;Is the element visible yet? &lt;/p&gt;

&lt;p&gt;Did the page fully load? Should I throw in a sleep() just to be safe?&lt;/p&gt;

&lt;p&gt;That uncertainty wastes time. That’s where Playwright came in—and more specifically, its built-in assertions.&lt;/p&gt;

&lt;p&gt;What hooked me first was auto-waiting. &lt;/p&gt;

&lt;p&gt;Playwright’s expect() API waits for an element to meet a condition before throwing an error.&lt;/p&gt;

&lt;p&gt;No more chaining complex waits or debugging why a test randomly fails in staging. &lt;/p&gt;

&lt;p&gt;Whether I’m checking for toBeVisible(), toHaveText(), or even toHaveAttribute(), Playwright handles the timing internally. &lt;/p&gt;

&lt;p&gt;It felt like the framework finally had my back.&lt;/p&gt;

&lt;p&gt;Then came soft assertions. This was a game changer. &lt;/p&gt;

&lt;p&gt;With Selenium, one failed assertion meant the whole test bailed out. But Playwright allows expect.&lt;/p&gt;

&lt;p&gt;soft()—so I can validate multiple things and get a complete report of what failed. Especially in form validation or checkout flows, this saved me hours of reruns.&lt;/p&gt;

&lt;p&gt;And the failure diagnostics? Chef’s kiss. &lt;/p&gt;

&lt;p&gt;The error messages are clean, show expected vs received values, and I can trace test steps visually. It’s not just a failure—it’s a clue to a fix.&lt;/p&gt;

&lt;p&gt;Switching to Playwright wasn’t about hype. It was about stability, clarity, and speed. The assertion engine alone made the move worth it. &lt;/p&gt;

&lt;p&gt;If your tests are still a guessing game, you might want to give Playwright a try—not for the name, but for the confidence it brings to every single test.&lt;/p&gt;

&lt;p&gt;Referred from my own written blog : &lt;a href="https://www.testleaf.com/blog/free-playwright-tutorial-on-assertions-and-validations-for-automation-testers/" rel="noopener noreferrer"&gt;Playwright Assertions Free Tutorial&lt;/a&gt; &amp;amp; Why It Matters&lt;/p&gt;

</description>
      <category>playwright</category>
    </item>
    <item>
      <title>Selenium Quiz: Test Your Skills from Beginner to Pro</title>
      <dc:creator>Mangai Ram</dc:creator>
      <pubDate>Fri, 14 Mar 2025 07:27:21 +0000</pubDate>
      <link>https://dev.to/magi-magificient/selenium-quiz-test-your-skills-from-beginner-to-pro-1pj2</link>
      <guid>https://dev.to/magi-magificient/selenium-quiz-test-your-skills-from-beginner-to-pro-1pj2</guid>
      <description>&lt;p&gt;Selenium is one of the most widely used tools for &lt;strong&gt;web automation testing&lt;/strong&gt;, and mastering it requires a strong understanding of its concepts, from basic to advanced levels. Whether you're a &lt;strong&gt;beginner&lt;/strong&gt;, an &lt;strong&gt;intermediate&lt;/strong&gt;, or a &lt;strong&gt;pro&lt;/strong&gt;, testing your knowledge through quizzes is a great way to improve your skills.  &lt;/p&gt;

&lt;p&gt;In this article, we have curated &lt;strong&gt;three Selenium quiz questions&lt;/strong&gt;, each representing a different difficulty level. Let’s see where you stand!  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;1️⃣ Beginner-Level Selenium Question&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is the purpose of Selenium WebDriver?&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;a) To perform manual testing&lt;br&gt;&lt;br&gt;
b) To automate web applications&lt;br&gt;&lt;br&gt;
c) To test mobile applications&lt;br&gt;&lt;br&gt;
d) To manage databases  &lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Answer:&lt;/strong&gt; &lt;strong&gt;b) To automate web applications&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Explanation:&lt;/strong&gt; Selenium WebDriver is primarily used to &lt;strong&gt;automate the interaction with web applications&lt;/strong&gt;, enabling testers to perform browser-based UI tests efficiently.  &lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;2️⃣ Intermediate-Level Selenium Question&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How can you handle dropdowns in Selenium WebDriver?&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;a) Using the &lt;code&gt;click()&lt;/code&gt; method&lt;br&gt;&lt;br&gt;
b) Using the &lt;code&gt;Select&lt;/code&gt; class in Selenium&lt;br&gt;&lt;br&gt;
c) Using JavaScriptExecutor only&lt;br&gt;&lt;br&gt;
d) Selenium does not support dropdown handling  &lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Answer:&lt;/strong&gt; &lt;strong&gt;b) Using the &lt;code&gt;Select&lt;/code&gt; class in Selenium&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Explanation:&lt;/strong&gt; The &lt;strong&gt;&lt;code&gt;Select&lt;/code&gt; class&lt;/strong&gt; in Selenium is specifically designed for handling &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; dropdown elements. It allows you to select options by &lt;strong&gt;index, visible text, or value&lt;/strong&gt;. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Select&lt;/span&gt; &lt;span class="n"&gt;dropdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dropdownID"&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
&lt;span class="n"&gt;dropdown&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selectByVisibleText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Option1"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;3️⃣ Pro-Level Selenium Question&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is the best approach to handle dynamically changing web elements in Selenium?&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;a) Use hardcoded waits (&lt;code&gt;Thread.sleep()&lt;/code&gt;)&lt;br&gt;&lt;br&gt;
b) Use explicit waits like &lt;code&gt;WebDriverWait&lt;/code&gt; with &lt;code&gt;ExpectedConditions&lt;/code&gt;&lt;br&gt;&lt;br&gt;
c) Refresh the page repeatedly until the element appears&lt;br&gt;&lt;br&gt;
d) Switch to a different browser  &lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Answer:&lt;/strong&gt; &lt;strong&gt;b) Use explicit waits like &lt;code&gt;WebDriverWait&lt;/code&gt; with &lt;code&gt;ExpectedConditions&lt;/code&gt;&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Explanation:&lt;/strong&gt; Web elements often take time to load due to &lt;strong&gt;AJAX calls&lt;/strong&gt; or &lt;strong&gt;dynamic page updates&lt;/strong&gt;. Using &lt;strong&gt;explicit waits&lt;/strong&gt; ensures Selenium waits until the element is available before interacting with it. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;WebDriverWait&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebDriverWait&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="nc"&gt;WebElement&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;until&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExpectedConditions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visibilityOfElementLocated&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dynamicElement"&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Where Do You Stand?&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;0-1 correct answers:&lt;/strong&gt; Time to &lt;a href="https://www.testleaf.com/course/selenium-automation-certification-training-course.html" rel="noopener noreferrer"&gt;start learning Selenium&lt;/a&gt;! 🚀
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2 correct answers:&lt;/strong&gt; You’re on the right track—keep practicing! 💡
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3 correct answers:&lt;/strong&gt; You’re a Selenium Pro! 🎯
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Want more Selenium quizzes and automation tips? Stay tuned for our next article!  &lt;/p&gt;

&lt;p&gt;Would you like a &lt;strong&gt;full-length Selenium quiz&lt;/strong&gt; with more questions? Let me know! 😊&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>programming</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
