<?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>Forem: Sebastian Clavijo Suero</title>
    <description>The latest articles on Forem by Sebastian Clavijo Suero (@sebastianclavijo).</description>
    <link>https://forem.com/sebastianclavijo</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%2F1453622%2F5e79b768-40f4-411c-be62-94b82d72af43.jpg</url>
      <title>Forem: Sebastian Clavijo Suero</title>
      <link>https://forem.com/sebastianclavijo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sebastianclavijo"/>
    <language>en</language>
    <item>
      <title>The Cypress i18n Mistake: Testing Words Instead of Meaning - i18next is your partner</title>
      <dc:creator>Sebastian Clavijo Suero</dc:creator>
      <pubDate>Sun, 10 May 2026 20:42:28 +0000</pubDate>
      <link>https://forem.com/sebastianclavijo/the-cypress-i18n-mistake-testing-words-instead-of-meaning-i18next-is-your-partner-k8n</link>
      <guid>https://forem.com/sebastianclavijo/the-cypress-i18n-mistake-testing-words-instead-of-meaning-i18next-is-your-partner-k8n</guid>
      <description>&lt;h3&gt;
  
  
  A Better Way to Test Multilanguage Apps in Cypress
&lt;/h3&gt;

&lt;p&gt;Table of contents&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ACT 1: EXPOSITION&lt;/li&gt;
&lt;li&gt;
ACT 2: CONFRONTATION

&lt;ul&gt;
&lt;li&gt;The Problem With Copy-Pasting Translations&lt;/li&gt;
&lt;li&gt;What if Cypress Could Be Polyglot?&lt;/li&gt;
&lt;li&gt;Let Me Introduce You to i18next, If You Haven’t Met It Yet&lt;/li&gt;
&lt;li&gt;Where Should the Translations Live?&lt;/li&gt;
&lt;li&gt;Creating a Small &lt;code&gt;cy.init()&lt;/code&gt; and &lt;code&gt;cy.t()&lt;/code&gt; Commands&lt;/li&gt;
&lt;li&gt;What About Fallbacks?&lt;/li&gt;
&lt;li&gt;Running the Same Test Across Multiple Languages&lt;/li&gt;
&lt;li&gt;A Small Example With a Language Switcher: &lt;code&gt;cy.changeLanguage()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Should We Use Translated Text to Click Buttons?&lt;/li&gt;
&lt;li&gt;When Exact Text Actually Matters&lt;/li&gt;
&lt;li&gt;A Healthier Mental Model&lt;/li&gt;
&lt;li&gt;Interpolation: When Translations Need Dynamic Values&lt;/li&gt;
&lt;li&gt;Namespaces: When One Translation File Is Not Enough&lt;/li&gt;
&lt;li&gt;i18next Can Do More Than Just Interpolation&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;ACT 3: RESOLUTION&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  ACT 1: EXPOSITION
&lt;/h2&gt;

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

&lt;p&gt;There is a very common situation that appears when testing applications that support multiple languages.&lt;/p&gt;

&lt;p&gt;At first, everything looks innocent.&lt;br&gt;
You have an application in English, so you write something like this:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&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&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome back&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Easy enough, right?&lt;/p&gt;

&lt;p&gt;Then, one day, the Product team says: “&lt;strong&gt;We are going international!&lt;/strong&gt;”&lt;br&gt;
And suddenly your application also supports &lt;em&gt;Spanish&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So now you write:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Iniciar sesión&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bienvenido de nuevo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Easy enough!&lt;/p&gt;

&lt;p&gt;Then &lt;em&gt;French&lt;/em&gt; arrives.&lt;br&gt;
Then &lt;em&gt;Portuguese&lt;/em&gt;.&lt;br&gt;
Then &lt;em&gt;German&lt;/em&gt;.&lt;br&gt;
And how about &lt;em&gt;Mandarin&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;After that someone from UX changes "Welcome back" to "Good to see you again". &lt;/p&gt;

&lt;p&gt;And of course, the Spanish translator decides that "Bienvenido de nuevo" should actually be "Qué bueno verte de nuevo".&lt;/p&gt;

&lt;p&gt;So your Cypress test fails, and then you ask yourself the existential automation question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is my test failing because the application is broken, or because somebody "improved" the copy?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And that, my QA friend, is where the fun begins.&lt;/p&gt;

&lt;p&gt;There are some articles about internationalization in Cypress, but surprisingly, not that many.&lt;/p&gt;

&lt;p&gt;And as a multicultural and multilingual person, I have always been interested in how, a well-designed multilingual website, can connect with people across languages, cultures, and contexts, and in doing so, create a much bigger impact.&lt;/p&gt;


&lt;h2&gt;
  
  
  ACT 2: CONFRONTATION
&lt;/h2&gt;

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

&lt;p&gt;The problem is not testing multilingual applications. Obviously, we absolutely should test them.&lt;/p&gt;

&lt;p&gt;The problem (and big mistake) starts when we copy-paste translated text directly into our Cypress tests and pretend that this is a stable strategy.&lt;/p&gt;

&lt;p&gt;This could be a typical test for creating a new project while also validating the messages shown to the user:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Nuevo proyecto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Nombre del proyecto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mi proyecto Cypress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Crear proyecto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Proyecto creado correctamente&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="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks simple!&lt;br&gt;
But what are we really testing?&lt;/p&gt;

&lt;p&gt;Are we testing that the login flow works?&lt;br&gt;
Are we testing that the _Spanish _translation exists?&lt;br&gt;
Are we testing that the exact copy has not changed?&lt;br&gt;
Are we testing that the translator did not open the thesaurus with too much confidence?&lt;/p&gt;

&lt;p&gt;Maybe all of the above?&lt;br&gt;
That is the problem.&lt;/p&gt;

&lt;p&gt;Do not get me wrong.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;cy.contains()&lt;/code&gt; with visible text is not evil at all. Actually, I like it a lot when it is used with intention. It makes tests readable, and in many cases, it reflects how users experience the application.&lt;/p&gt;

&lt;p&gt;But in a multilingual app, hardcoded translated strings can quickly become a maintenance nightmare.&lt;/p&gt;

&lt;p&gt;The test is no longer expressing the meaning of the user journey. It is expressing one particular version of the translated words at one particular moment in time.&lt;/p&gt;

&lt;p&gt;In other words: &lt;strong&gt;We are testing words instead of meaning.&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  The Problem With Copy-Pasting Translations
&lt;/h3&gt;

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

&lt;p&gt;A very common Cypress approach for testing the login user journey would look something like this:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&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?lng=es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Iniciar sesión&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bienvenido de nuevo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works.&lt;br&gt;
Until it does not! 🤔&lt;/p&gt;

&lt;p&gt;The moment the &lt;em&gt;Spanish&lt;/em&gt; translation changes, the test breaks, even if the application is working perfectly.&lt;/p&gt;

&lt;p&gt;Of course, sometimes this is exactly what we want. If the purpose of the test is to validate a very specific legal disclaimer, marketing text, warning message, or compliance-related copy, then yes, the exact words matter.&lt;/p&gt;

&lt;p&gt;But for most application flows, the exact words are not the real behavior.&lt;br&gt;
The behavior looks something like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The app loads in Spanish.&lt;/li&gt;
&lt;li&gt;The login button is translated.&lt;/li&gt;
&lt;li&gt;The user can log in.&lt;/li&gt;
&lt;li&gt;The welcome message is shown in the selected language.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the meaning. The translated string is only the visible representation of that meaning.&lt;/p&gt;

&lt;p&gt;So the real question is: &lt;strong&gt;How can Cypress verify the meaning without copy-pasting every translated word?&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  What if Cypress Could Be Polyglot?
&lt;/h3&gt;

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

&lt;p&gt;Imagine this.&lt;/p&gt;

&lt;p&gt;Instead of telling Cypress to find one exact &lt;em&gt;Spanish&lt;/em&gt; sentence, we ask Cypress to find whatever &lt;code&gt;welcome&lt;/code&gt; means in &lt;em&gt;Spanish&lt;/em&gt;, and then we assert that the application shows it.&lt;/p&gt;

&lt;p&gt;Something like this:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.welcome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A command like &lt;code&gt;cy.t('auth.welcome', { lng: 'es' })&lt;/code&gt; would get the &lt;em&gt;Spanish&lt;/em&gt; translation for the auth welcome message, and then we can use that value in the assertion.&lt;/p&gt;

&lt;p&gt;Now the test is not responsible for knowing the final translated sentence. The test only knows the meaning, and that, after all, is what is actually relevant for the test.&lt;/p&gt;

&lt;p&gt;The key:&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;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;welcome&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;represents the meaning.&lt;/p&gt;

&lt;p&gt;The language:&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;es&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;represents the locale we want.&lt;/p&gt;

&lt;p&gt;And the translation system takes care of the rest. Definitely that would be a much better contract.&lt;/p&gt;

&lt;p&gt;The test would no longer be saying find "Bienvenido de nuevo", but find whatever &lt;code&gt;auth.welcome&lt;/code&gt; means in Spanish.&lt;/p&gt;




&lt;h3&gt;
  
  
  Let Me Introduce You to i18next, If You Haven’t Met It Yet
&lt;/h3&gt;

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

&lt;p&gt;If your application already uses &lt;a href="https://www.i18next.com/" rel="noopener noreferrer"&gt;&lt;code&gt;i18next&lt;/code&gt;&lt;/a&gt;, then you probably already have the concepts we need.&lt;/p&gt;

&lt;p&gt;But, at a very basic level, &lt;code&gt;i18next&lt;/code&gt; gives us two important things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A way to initialize the translation system.&lt;/li&gt;
&lt;li&gt;A way to resolve a translation key into actual text.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The initialization happens with the &lt;code&gt;i18next.init()&lt;/code&gt; method. This method receives a &lt;strong&gt;configuration object&lt;/strong&gt; where we define things like the current language, fallback language, and translation resources:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;i18next.init(options, callback) // returns a Promise&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Something like this:&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;i18next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Current language&lt;/span&gt;
  &lt;span class="na"&gt;fallbackLng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Fallback language to use if message key not found&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// English translations&lt;/span&gt;
      &lt;span class="na"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// "translation" is the default namespace (we will see namespaces later)&lt;/span&gt;
        &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Message auth.login in 'en'&lt;/span&gt;
          &lt;span class="na"&gt;welcome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome back&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Message auth.welcome in 'en'&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;es&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// Spanish translations&lt;/span&gt;
      &lt;span class="na"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Iniciar sesión&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Message auth.login in 'en'&lt;/span&gt;
          &lt;span class="na"&gt;welcome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bienvenido de nuevo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Message auth.welcome in 'en'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The callback function can be used for example to inform in the console if something went wrong loading:&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;i18next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fallbackLng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Something went wrong loading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; same as i18next.t&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can use the &lt;code&gt;i18next.t()&lt;/code&gt; method to resolve a translation key into the corresponding text. In simple terms, the method &lt;code&gt;t()&lt;/code&gt; receives a &lt;strong&gt;message key&lt;/strong&gt; and an optional &lt;strong&gt;configuration object&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;i18next.t(key, options)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For example:&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;i18next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.welcome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the idea is very simple: &lt;strong&gt;Let Cypress use the same translation mechanism that the application might already use&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Where Should the Translations Live?
&lt;/h3&gt;

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

&lt;p&gt;Writing all translations directly inside &lt;code&gt;i18next.init()&lt;/code&gt; could be very ugly, specially if we have a very large number of messages.&lt;/p&gt;

&lt;p&gt;Something like the &lt;code&gt;.init&lt;/code&gt; example we used above may be fine for a tiny demo, but in a real application, it makes much more sense for translations to live in separate files.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;src/locales/en.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;English&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;file&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"login"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Login"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"welcome"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Welcome back"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;src/locales/es.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Spanish&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;file&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"login"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Iniciar sesión"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"welcome"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bienvenido de nuevo"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is cleaner for a few reasons:&lt;/p&gt;

&lt;p&gt;First, maintenance: Translations change often. Keeping them in JSON files makes them easier to update without touching test logic.&lt;/p&gt;

&lt;p&gt;Second, abstraction: The test does not need to know every word in every language. It only needs to know the key that represents the meaning.&lt;/p&gt;

&lt;p&gt;Third, it works like a dictionary. To adding a third, fourth, or fifth language becomes much easier, and your tests can still work like a charm.&lt;/p&gt;

&lt;p&gt;You are basically saying "For this language, this key means this sentence." And that is exactly what we want.&lt;/p&gt;

&lt;p&gt;A translation file is our dictionary.&lt;br&gt;
Cypress should ask the dictionary.&lt;br&gt;
&lt;strong&gt;Cypress should NOT become the dictionary.&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Creating a Small cy.init() and cy.t() Commands
&lt;/h3&gt;

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

&lt;p&gt;Now let’s make this easier to use inside Cypress, of course, using custom commands!&lt;/p&gt;

&lt;p&gt;We can create two commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cy.initI18n()&lt;/code&gt; to initialize the translation system.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cy.t()&lt;/code&gt; to translate a key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They something like this:&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;// cypress/support/commands.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;i18next&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;i18next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Do not forget "npm install i18next" first :)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;enMsgs&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;../../src/locales/en.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// English "dictionary"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;esMsgs&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;../../src/locales/es.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// The Spanish one&lt;/span&gt;

&lt;span class="c1"&gt;// cy.init() command will receive the exact same arguments as i18next.init()&lt;/span&gt;
&lt;span class="nx"&gt;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;initI18n&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;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;i18next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Use English as the default language if no language is provided&lt;/span&gt;
      &lt;span class="na"&gt;fallbackLng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// The fallback language will be English&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;enMsgs&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// Provide the English translations&lt;/span&gt;
        &lt;span class="na"&gt;es&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;esMsgs&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// And the Spanish translations&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Pass along any other options supported by i18next.init()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Do not show the wrap() command in the Cypress Log&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="nx"&gt;will&lt;/span&gt; &lt;span class="nx"&gt;receive&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;exact&lt;/span&gt; &lt;span class="nx"&gt;same&lt;/span&gt; &lt;span class="nx"&gt;arguments&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;i18next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t&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;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Get the translation for the provided key in the currently configured&lt;/span&gt;
  &lt;span class="c1"&gt;// language, and apply any i18next options if provided.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i18next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our tests can use this:&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Login page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Initialize our polyglot Cypress system and set Spanish as current language&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initI18n&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&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?lng=es&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Shows the login experience in Spanish&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;loginText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginText&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.welcome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;welcomeText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;welcomeText&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much better!&lt;/p&gt;

&lt;p&gt;And if the Spanish translation changes from &lt;code&gt;"welcome": "Bienvenido de nuevo"&lt;/code&gt; to &lt;code&gt;"welcome": "Qué bueno verte de nuevo"&lt;/code&gt;, then no drama.&lt;/p&gt;

&lt;p&gt;We change the message in the corresponding translation file, and &lt;strong&gt;the test itself does not need to change because the meaning did not change.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Only the wording changed, and wording is exactly what translation files are supposed to manage.&lt;/p&gt;




&lt;h3&gt;
  
  
  What About Fallbacks?
&lt;/h3&gt;

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

&lt;p&gt;Another nice thing about using &lt;code&gt;i18next&lt;/code&gt; is that we can also use its language &lt;strong&gt;fallback&lt;/strong&gt; behavior. A fallback is basically what happens when the translation you asked for does not exist in the selected language.&lt;/p&gt;

&lt;p&gt;For example, when we initialize our message system like this:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initI18n&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Selected language is Spanish&lt;/span&gt;
  &lt;span class="na"&gt;fallbackLng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Fallback language is English&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that if the Spanish translation is missing, try English.&lt;/p&gt;

&lt;p&gt;This can be useful when the application itself behaves this way. And that last sentence is important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your Cypress configuration should reflect your application behavior. If your app falls back to English, your Cypress translation helper should also fall back to English.&lt;/li&gt;
&lt;li&gt;If your app does not allow missing translations, your test should probably be stricter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can also resolve the key using a &lt;strong&gt;specific language&lt;/strong&gt; directly from the &lt;code&gt;cy.t()&lt;/code&gt; command, regardless of the language currently initialized:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.forgotPassword&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use a &lt;strong&gt;default value&lt;/strong&gt; when the key cannot be resolved:&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;// Passing as a second argument&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my.key&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;This is the default value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Or within the options object&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.forgotPassword&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Forgot password?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or even better, instead of hardcoding a default value in the test, you can provide an array of fallback keys as the first argument. If the first key cannot be resolved, &lt;code&gt;i18next&lt;/code&gt; will try the next one, and so on:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.forgotPassword&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;common.help&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that our custom &lt;code&gt;cy.t()&lt;/code&gt; command does not need to know every possible option.&lt;/p&gt;

&lt;p&gt;It simply passes the options to &lt;code&gt;i18next.t()&lt;/code&gt;, because it has been defined like this:&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;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t&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;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i18next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;We can use the power of &lt;code&gt;i18next&lt;/code&gt; without reinventing it inside Cypress&lt;/strong&gt;, which is always nice.&lt;/p&gt;

&lt;p&gt;Because reinventing things inside Cypress tests is how many horror stories begin.&lt;/p&gt;




&lt;h3&gt;
  
  
  Running the Same Test Across Multiple Languages
&lt;/h3&gt;

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

&lt;p&gt;Once we have this setup, testing multiple languages becomes much cleaner.&lt;/p&gt;

&lt;p&gt;Check out this code:&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;// Supported languages&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;languages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&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;es&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;fr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;// Iterate over all supported languages&lt;/span&gt;
&lt;span class="nx"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;lng&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Login page in &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lng&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initI18n&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/login?lng=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lng&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Shows the translated login experience for language &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lng&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;loginText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginText&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;

      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.welcome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;welcomeText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;welcomeText&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are not duplicating the same test with different hardcoded strings.&lt;/p&gt;

&lt;p&gt;Instead we are expressing the real intention:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For each supported language, the login page should render the expected translated content.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is cleaner.&lt;br&gt;
It is less code.&lt;br&gt;
It is more scalable.&lt;/p&gt;

&lt;p&gt;And most importantly, that is easier to maintain when your application inevitably keeps changing. Because it will.&lt;/p&gt;

&lt;p&gt;Applications always change.&lt;br&gt;
That is their favorite hobby.&lt;/p&gt;


&lt;h3&gt;
  
  
  A Small Example With a Language Switcher: cy.changeLanguage()
&lt;/h3&gt;

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

&lt;p&gt;To change the current language in &lt;code&gt;i18next&lt;/code&gt; without re-initializing, we can use the &lt;code&gt;i18next.changeLanguage()&lt;/code&gt; method. This method is designed to switch languages at runtime and will automatically trigger a re-render of components if you are using bindings like &lt;code&gt;react-i18next&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;i18next.changeLanguage(lng, callback) // returns a Promise&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can use &lt;code&gt;i18next.changeLanguage()&lt;/code&gt; through a new Cypress custom command, &lt;code&gt;cy.changeLanguage()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// cy.changeLanguage() command will receive the exact same arguments as i18next.changeLanguage()&lt;/span&gt;
&lt;span class="nx"&gt;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;changeLanguage&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;lng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;i18next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;changeLanguage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="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;Then running the same tests across multiple languages would be something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;languages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&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;es&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;fr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initI18n&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// Initialize i18next once, using English by default&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;lng&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="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;changeLanguage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/login?lng=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lng&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;``&lt;/span&gt;&lt;span class="nx"&gt;Shows&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;translated&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="nx"&gt;experience&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`e`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;loginText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginText&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.welcome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;welcomeText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;welcomeText&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No need to execute the full &lt;code&gt;cy.initI18n()&lt;/code&gt; process for each language.&lt;/p&gt;

&lt;p&gt;We initialize it once in the &lt;code&gt;before()&lt;/code&gt; hook, and then we simply switch the current language in the &lt;code&gt;beforeEach()&lt;/code&gt; hook before each test.&lt;/p&gt;




&lt;h3&gt;
  
  
  Should We Use Translated Text to Click Buttons?
&lt;/h3&gt;

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

&lt;p&gt;Well...&lt;/p&gt;

&lt;p&gt;It depends.&lt;/p&gt;

&lt;p&gt;I know, I know. That is the classic "let me sit comfortably on the fence" answer.&lt;/p&gt;

&lt;p&gt;But hear me out.&lt;/p&gt;

&lt;p&gt;If your intention is to verify that the translated button text appears on the page, then using the translated value makes sense:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;loginText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginText&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if your intention is simply to interact with the login button and continue the test flow, then a stable selector like &lt;code&gt;data-cy&lt;/code&gt; or &lt;code&gt;data-testid&lt;/code&gt;, IMO, is usually a better option:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="login-button"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the distinction I personally like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use selectors to interact with the elements in the DOM.
&lt;/li&gt;
&lt;li&gt;Use translations to verify the localized user experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this case:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="login-button"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.welcome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;welcomeText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;welcomeText&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the test does not depend on the button text to perform the click. But it still verifies that the expected translated welcome message appears.&lt;/p&gt;

&lt;p&gt;That is a nice &lt;strong&gt;balance&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And as we know, balance is usually where maintainable test automation lives. &lt;strong&gt;Somewhere between chaos and overengineering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Remember: &lt;strong&gt;Do not hide bad selectors behind i18n!&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  When Exact Text Actually Matters
&lt;/h3&gt;

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

&lt;p&gt;Now, before someone sharpens their keyboard in the comments, let me clarify something.&lt;/p&gt;

&lt;p&gt;I never said: &lt;em&gt;never&lt;/em&gt; assert exact translated text. That would be too extreme. And extreme rules in testing usually age like milk.&lt;/p&gt;

&lt;p&gt;There are cases where exact text absolutely matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Legal disclaimers&lt;/li&gt;
&lt;li&gt;Error messages with regulatory requirements&lt;/li&gt;
&lt;li&gt;Payment warnings&lt;/li&gt;
&lt;li&gt;Accessibility instructions&lt;/li&gt;
&lt;li&gt;Medical, financial, or security-related messages&lt;/li&gt;
&lt;li&gt;Marketing copy that must be approved&lt;/li&gt;
&lt;li&gt;Any text where the exact wording is the actual requirement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In those cases, exact matching is not only valid, it is absolutely a must.&lt;br&gt;
But then make it intentional.&lt;/p&gt;

&lt;p&gt;If this is the approved legal text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expectedLegalText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;By continuing, you agree to the Terms and Conditions.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we should explicitly verify the full text:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="legal-disclaimer"]&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="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;have.text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expectedLegalText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test is clearly saying and the exact wording matters here.&lt;/p&gt;

&lt;p&gt;And if you still want that text to come from the translation system, you can combine both ideas:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;legal.termsAndConditions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;expectedLegalText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="legal-disclaimer"]&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="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;have.text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expectedLegalText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is the intention.&lt;br&gt;
The problem is not text assertions.&lt;br&gt;
The problem is accidental text assertions.&lt;/p&gt;

&lt;p&gt;There is a big difference.&lt;/p&gt;


&lt;h3&gt;
  
  
  A Healthier Mental Model
&lt;/h3&gt;

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

&lt;p&gt;A multilingual Cypress test should not try to prove every word in every language on every screen.&lt;/p&gt;

&lt;p&gt;That sounds heroic, but it usually becomes slow, noisy, and painful to maintain.&lt;/p&gt;

&lt;p&gt;A better multilingual test answers questions like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Did the application load the expected language?&lt;/li&gt;
&lt;li&gt;Did the important user-facing messages come from the right translation keys?&lt;/li&gt;
&lt;li&gt;Can the user complete the main flow in that language?&lt;/li&gt;
&lt;li&gt;Does the app still behave correctly when the locale changes?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is much more valuable than copy-pasting 200 translated strings into a spec file and hoping nobody touches them.&lt;/p&gt;

&lt;p&gt;Because they will be touched. They always are!&lt;br&gt;
Again... that is their favorite hobby. 😄&lt;/p&gt;


&lt;h3&gt;
  
  
  Interpolation: When Translations Need Dynamic Values
&lt;/h3&gt;

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

&lt;p&gt;So far, our translations have been static, but real applications love dynamic text.&lt;/p&gt;

&lt;p&gt;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;'Welcome, Sebastian'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invoice 1234 was created successfully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;i18next&lt;/code&gt;, this is usually handled with &lt;strong&gt;interpolation&lt;/strong&gt;. That means the translation has placeholders, and we provide the values later.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"welcomeUser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Welcome, {{name}}"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"invoice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invoice {{invoiceId}} was created successfully"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in Cypress, we do not need to change our custom command. Since &lt;code&gt;cy.t()&lt;/code&gt; already passes the options object directly to &lt;code&gt;i18next.t()&lt;/code&gt;, any interpolation values we provide are handled by &lt;code&gt;i18next&lt;/code&gt; automatically.&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.welcomeUser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sebastian&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That resolves to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Welcome, Sebastian
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you can also pass the desired language along with the interpolation values:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invoice.created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;invoiceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It would resolve to whatever the Spanish translation for invoice.created is, with {{invoiceId}} replaced by 1234.&lt;/p&gt;

&lt;p&gt;It is also possible to provide positional values if your app also uses them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checkout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"step"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Step {{0}} of {{1}}"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkout.step&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="mi"&gt;0&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="mi"&gt;1&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That resolves to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Step 2 of 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The nice part is that our &lt;code&gt;cy.t()&lt;/code&gt; command does not need to know if the translation is static, interpolated, simple, or complex.&lt;/p&gt;

&lt;p&gt;It simply delegates to &lt;code&gt;i18next&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the translation library for translation logic.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Use Cypress for testing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everyone stays in their lane.&lt;/p&gt;


&lt;h3&gt;
  
  
  Namespaces: When One Translation File Is Not Enough
&lt;/h3&gt;

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

&lt;p&gt;As applications grow, one giant translation file can become painful. Very painful.&lt;/p&gt;

&lt;p&gt;For that &lt;code&gt;i18next&lt;/code&gt; supports namespaces. A namespace lets you split translations by area, domain, or feature.&lt;/p&gt;

&lt;p&gt;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;src/locales/en/auth.json
src/locales/en/common.json
src/locales/es/auth.json
src/locales/es/common.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;English &lt;code&gt;auth.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"login"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Login"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"welcome"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Welcome back"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;English &lt;code&gt;common.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"save"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Save"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cancel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cancel"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can initialize our 'polyglot Cypress' like this:&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="nx"&gt;enAuth&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;../../src/locales/en/auth.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;enCommon&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;../../src/locales/en/common.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;esAuth&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;../../src/locales/es/auth.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;esCommon&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;../../src/locales/es/common.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;en&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;enAuth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// namespace auth (English)&lt;/span&gt;
      &lt;span class="na"&gt;common&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;enCommon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// namespace common (English)&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; 
    &lt;span class="na"&gt;es&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;esAuth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// namespace auth (Spanish)&lt;/span&gt;
      &lt;span class="na"&gt;common&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;esCommon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// namespace common (Spanish)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// This tells which namespace to by default when not provided one in .t()&lt;/span&gt;
  &lt;span class="nl"&gt;defaultNS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 

  &lt;span class="c1"&gt;// It is also good practice to list all available namespaces&lt;/span&gt;
  &lt;span class="na"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth&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;common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;auth&lt;/code&gt; is the default namespace, that means:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&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&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will look in the &lt;code&gt;auth&lt;/code&gt; namespace by default.&lt;/p&gt;

&lt;p&gt;But if we want something from &lt;code&gt;common&lt;/code&gt; namespace instead, we will do this:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the namespace prefix format:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;common:save&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although both approaches can work, personally, I like using the namespace prefix style:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the most important thing is consistency.&lt;/p&gt;

&lt;p&gt;Pick one style.&lt;br&gt;
And stick to the plan!&lt;/p&gt;

&lt;p&gt;Future you will be grateful... probably. 😉&lt;/p&gt;




&lt;h3&gt;
  
  
  i18next Can Do More Than Just Interpolation
&lt;/h3&gt;

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

&lt;p&gt;Interpolation is only one part of what &lt;code&gt;i18next&lt;/code&gt; can do. It also supports other features that can be useful in real applications, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Formatting&lt;/strong&gt;: useful for numbers, and values that need locale-specific formatting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plurals&lt;/strong&gt;: when text changes depending on quantity, like "1 item" vs "5 items".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context&lt;/strong&gt;: useful when the translation changes depending on additional context, such as tone, audience, or grammatical differences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;And I will repeat one more time: The nice thing is that our Cypress command does not need special logic for each one, since we pass the options directly to &lt;code&gt;i18next.t()&lt;/code&gt;!&lt;/strong&gt; 😉&lt;/p&gt;

&lt;p&gt;If you want to become an &lt;code&gt;i18next&lt;/code&gt; master and explore all its options and methods (there are a lot!) check the official site: &lt;a href="https://www.i18next.com/" rel="noopener noreferrer"&gt;https://www.i18next.com/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ACT 3: RESOLUTION
&lt;/h2&gt;

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

&lt;p&gt;Testing multilingual applications is not just about running the same test in different languages.&lt;/p&gt;

&lt;p&gt;It is about deciding what your test should actually care about.&lt;/p&gt;

&lt;p&gt;If your test hardcodes every translated string, you may end up with a suite that fails every time copy changes, even when the product works perfectly.&lt;/p&gt;

&lt;p&gt;But if your test uses the same &lt;code&gt;i18next&lt;/code&gt; translation keys as the application, your assertions become closer to the real meaning of the product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key idea is simple: Test the meaning, not the hardcoded words.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use stable selectors for interactions.&lt;br&gt;
Use translation keys for localization assertions.&lt;br&gt;
Use exact text only when exact text is truly the requirement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use interpolation, namespaces, fallbacks, plurals, formatting, and context through &lt;code&gt;i18next&lt;/code&gt;, not through custom Cypress reinventions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That small shift will make your multilingual Cypress tests cleaner, less repetitive, and much easier to maintain.&lt;/p&gt;

&lt;p&gt;So, the next time you are about to copy-paste a &lt;em&gt;Spanish&lt;/em&gt;, &lt;em&gt;French&lt;/em&gt;, &lt;em&gt;Portuguese&lt;/em&gt;, &lt;em&gt;German&lt;/em&gt;, or &lt;em&gt;Klingon&lt;/em&gt; translation into your Cypress spec, pause for a second and ask yourself:&lt;/p&gt;

&lt;p&gt;Am I testing the product behavior, or am I just testing today’s wording?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Because in multilingual testing, words change. Meaning should not.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;strong&gt;I'd love to hear from you! Please don't forget to follow me, leave a comment, or a reaction if you found this article useful or insightful.&lt;/strong&gt;&lt;/em&gt; ❤️ 🦄 🤯 🙌 🔥&lt;/p&gt;

&lt;p&gt;👉 My LinkedIn: &lt;a href="https://www.linkedin.com/in/sebastianclavijosuero/" rel="noopener noreferrer"&gt;linkedin.com/in/sebastianclavijosuero&lt;/a&gt;&lt;br&gt;
👉 My projects and plugin repos: &lt;a href="https://github.com/sclavijosuero" rel="noopener noreferrer"&gt;github.com/sclavijosuero&lt;/a&gt;&lt;br&gt;
👉 You can also connect with me on my new &lt;strong&gt;YouTube channel&lt;/strong&gt;: &lt;a href="https://www.youtube.com/@SebastianClavijoSuero" rel="noopener noreferrer"&gt;youtube.com/@SebastianClavijoSuero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are feeling especially generous and enjoy my articles, you can buy me a coffee or contribute to a training session. In both cases, my brain will definitely thank you for it! ☕😄 &lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/sclavijosuero" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>cypress</category>
      <category>qa</category>
      <category>intl</category>
    </item>
    <item>
      <title>WICK-DOM-OBSERVER: The Deterministic Cypress Plugin for Fast Spinners, Blinking Toasts, Optional Overlays, and UI’s Most Wanted</title>
      <dc:creator>Sebastian Clavijo Suero</dc:creator>
      <pubDate>Sun, 22 Mar 2026 07:21:22 +0000</pubDate>
      <link>https://forem.com/sebastianclavijo/wick-dom-observer-the-deterministic-cypress-plugin-for-fast-spinners-blinking-toasts-optional-4e59</link>
      <guid>https://forem.com/sebastianclavijo/wick-dom-observer-the-deterministic-cypress-plugin-for-fast-spinners-blinking-toasts-optional-4e59</guid>
      <description>&lt;p&gt;&lt;em&gt;Because the most dangerous UI behaviors are the ones that leave almost no trace.&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT 1: EXPOSITION
&lt;/h1&gt;

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

&lt;p&gt;There are some UI elements that seem to exist for one noble purpose only: to make your Cypress tests miserable.&lt;/p&gt;

&lt;p&gt;I am talking about the spinner that appears and disappears so absurdly fast that by the time Cypress gets there, it is already gone.&lt;br&gt;&lt;br&gt;
The toast that politely informs the user that something happened, but only for a tiny and inconvenient window of time.&lt;br&gt;&lt;br&gt;
And, of course, the annoying modal or overlay that shows up on page load only when it feels like it, blocks the whole page, and leaves your test suite wondering whether it should close it... or pretend it never existed.&lt;/p&gt;

&lt;p&gt;These are not exotic edge cases. These are real UI behaviors we deal with all the time.&lt;/p&gt;

&lt;p&gt;And the worst part is not even testing them. The worst part is testing them &lt;strong&gt;reliably&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because traditional approaches usually push you toward one of these painful paths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cy.wait(500)&lt;/code&gt; and pray.&lt;/li&gt;
&lt;li&gt;Force the UI to behave unnaturally just for the test.&lt;/li&gt;
&lt;li&gt;Add network intercepts and cross your fingers, hoping that somehow will be enough.&lt;/li&gt;
&lt;li&gt;Write fragile logic that still flakes the moment the DOM decides to move just a little faster than usual.&lt;/li&gt;
&lt;li&gt;And do not even get me started on what happens when all that reaches your project’s notoriously unpredictable CI pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is exactly why I built &lt;strong&gt;&lt;a href="https://github.com/sclavijosuero/wick-dom-observer" rel="noopener noreferrer"&gt;wick-dom-observer&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;⭐⭐⭐⭐⭐ Its mission is simple: help Cypress detect and validate those rebel UI elements that appear too fast, disappear too fast, or appear only sometimes, without turning your tests into a dramatic mess. ⭐⭐⭐⭐⭐&lt;/p&gt;


&lt;h1&gt;
  
  
  ACT 2: CONFRONTATION
&lt;/h1&gt;

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

&lt;p&gt;Two commands. Same mission. Different entry point.&lt;/p&gt;

&lt;p&gt;When the UI feedback is triggered &lt;strong&gt;after a click&lt;/strong&gt;, use &lt;code&gt;clickAndWatchForElement()&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
When the element may appear &lt;strong&gt;on its own&lt;/strong&gt;, use &lt;code&gt;watchForElement()&lt;/code&gt;.&lt;br&gt;
 &lt;/p&gt;
&lt;h3&gt;
  
  
  1) &lt;code&gt;clickAndWatchForElement()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Use this one when the testing event starts with a user action: clicking a button, link, icon, or anything else that kicks off a spinner, toast, modal, or overlay.&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;clickAndWatchForElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;clickAndWatchForElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;clickAndWatchForElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;clickAndWatchForElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;clickAndWatchForElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;clickAndWatchForElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2) &lt;code&gt;watchForElement()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Use this one when there is &lt;strong&gt;no click involved&lt;/strong&gt; and the element may show up by itself, like page-load overlays, announcements, promos, or optional blocking panels.&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;watchForElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The shared &lt;code&gt;config&lt;/code&gt; object
&lt;/h3&gt;

&lt;p&gt;Both commands use the same &lt;code&gt;config&lt;/code&gt; shape:&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&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="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&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="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;      &lt;span class="c1"&gt;// optional&lt;/span&gt;
  &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                 &lt;span class="c1"&gt;// optional&lt;/span&gt;
  &lt;span class="nx"&gt;appear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;optional&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;// 'optional' | 'required'&lt;/span&gt;
  &lt;span class="nx"&gt;disappear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;// optional&lt;/span&gt;
  &lt;span class="nx"&gt;pollingInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;// optional&lt;/span&gt;
  &lt;span class="nx"&gt;mustLast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                 &lt;span class="c1"&gt;// optional&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What each option does
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;selector&lt;/code&gt; → the element you want to watch in the DOM
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;assert&lt;/code&gt; → synchronous assertion callback executed when the element is found
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;action&lt;/code&gt; → optional synchronous callback to perform side effects after &lt;code&gt;assert&lt;/code&gt; passes
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;timeout&lt;/code&gt; → max time to wait for appear / disappear phases
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;appear: 'optional'&lt;/code&gt; → valid if the element appears or not
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;appear: 'required'&lt;/code&gt; → fail if the element never shows up
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;disappear: false&lt;/code&gt; → validate appearance only; do not wait for disappearance&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;disappear: true&lt;/code&gt; → also wait for the element to go away
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pollingInterval&lt;/code&gt; → how often the DOM is checked
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mustLast&lt;/code&gt; → minimum time the element must remain visible in the DOM
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; &lt;code&gt;assert()&lt;/code&gt; and &lt;code&gt;action()&lt;/code&gt; must stay synchronous.&lt;br&gt;&lt;br&gt;
No &lt;code&gt;cy.get()&lt;/code&gt;, no &lt;code&gt;cy.wrap()&lt;/code&gt;, no Cypress commands inside them.&lt;br&gt;&lt;br&gt;
This is plain JavaScript / jQuery territory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The mental model
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;clickAndWatchForElement()&lt;/code&gt;&lt;/strong&gt; = &lt;em&gt;trigger the event, then watch the DOM&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;watchForElement()&lt;/code&gt;&lt;/strong&gt; = &lt;em&gt;just watch the DOM&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;mustLast&lt;/code&gt;&lt;/strong&gt; = &lt;em&gt;make sure the element did not vanish too quickly&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;disappear: true&lt;/code&gt;&lt;/strong&gt; = &lt;em&gt;validate the full lifecycle, not just the appearance&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Case 1: The super-fast spinner that mocks your test
&lt;/h2&gt;

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

&lt;h3&gt;
  
  
  The Mark
&lt;/h3&gt;

&lt;p&gt;You click a button to load data.&lt;/p&gt;

&lt;p&gt;A spinner appears, a loading label is shown, the button is disabled for a moment, the spinner disappears, and then the data is displayed.&lt;/p&gt;

&lt;p&gt;Sounds easy enough to test, right?&lt;/p&gt;

&lt;p&gt;What if the spinner only shows for 200ms? Or 100ms? Better yet, &lt;strong&gt;25ms&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Because if that spinner is fast enough, this classic pattern can betray you:&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shows the loading spinner and then displays the data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/your-page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="load-data-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="service-spinner"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="loading-label"]&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="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&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="nf"&gt;and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contain&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;Fetching latest campaign analytics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="load-data-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.disabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="service-spinner"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not.exist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="loading-label"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not.exist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="load-data-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.enabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="campaign-table"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="campaign-row"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;have.length.greaterThan&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why? Because Cypress starts looking &lt;strong&gt;after&lt;/strong&gt; the click has already happened, and if the spinner had a very short life in the DOM, you may miss it entirely.&lt;/p&gt;

&lt;p&gt;So now you have a flaky test for a UI behavior that absolutely happens.&lt;br&gt;
 &lt;/p&gt;
&lt;h3&gt;
  
  
  The Takedown
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assertLoadCampaignDataSpinner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// The spinner must be visible when observed.&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;$el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// Move to a stable parent scope so we can inspect related elements&lt;/span&gt;
  &lt;span class="c1"&gt;// that participate in the same loading flow.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;$root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;closest&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="c1"&gt;// Validate the loading label shown while data is being fetched.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadingLabel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="loading-row"] .loading-label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&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;loadingLabel&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&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;loadingLabel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fetching latest campaign analytics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// Validate that the trigger button is temporarily disabled&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadDataBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="load-data-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&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;loadDataBtn&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&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;loadDataBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shows the loading spinner and then displays the data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/modal-table-demo.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// Click the trigger button and start observing the DOM beforehand to catch the spinner&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="load-data-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;clickAndWatchForElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;// Element to observe after the click.&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="service-spinner"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// The spinner is expected to appear in this flow.&lt;/span&gt;
    &lt;span class="na"&gt;appear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// The spinner is also expected to disappear before the command finishes.&lt;/span&gt;
    &lt;span class="na"&gt;disappear&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="c1"&gt;// Max time (ms) to wait for the spinner to appear or disappear.&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;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Run the custom assertions while the spinner is present.&lt;/span&gt;
    &lt;span class="na"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;assertLoadCampaignDataSpinner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="loading-label"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not.be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="load-data-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.enabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="campaign-table"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="campaign-row"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;have.length.greaterThan&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Why the Hit Lands 👊
&lt;/h3&gt;

&lt;p&gt;The key trick is that &lt;code&gt;clickAndWatchForElement()&lt;/code&gt; starts observing &lt;strong&gt;before&lt;/strong&gt; the click happens, and an additional &lt;em&gt;secret sauce&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And that matters a lot.&lt;/p&gt;

&lt;p&gt;Cypress is already playing its own game here: its command queue runs asynchronously and processes commands at its own pace, while plain JavaScript, jQuery, and the DOM keep moving on a different timeline. So if you click first and only then ask Cypress to check for the spinner, there is a very real chance the DOM already did its thing and moved on.&lt;/p&gt;

&lt;p&gt;That is why &lt;strong&gt;wick-dom-observer&lt;/strong&gt; flips the sequence around: it prepares the observation flow first, performs the click, and then keeps observing the DOM to catch the exact moment the spinner appears and the exact moment it disappears.&lt;/p&gt;

&lt;p&gt;What is the &lt;em&gt;secret sauce&lt;/em&gt;? The &lt;strong&gt;MutationObserver&lt;/strong&gt; interface.&lt;/p&gt;

&lt;p&gt;It is the browser API designed to watch for changes in the DOM tree, which makes it a perfect fit for tracking fast UI elements.&lt;/p&gt;

&lt;p&gt;In other words, this is not just about watching earlier. It is about watching on the right timeline.&lt;/p&gt;

&lt;p&gt;So the example above using &lt;code&gt;clickAndWatchForElement()&lt;/code&gt; validates the full user-facing behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the spinner appears,&lt;/li&gt;
&lt;li&gt;the loading label is visible and contains the expected text,&lt;/li&gt;
&lt;li&gt;the action button is temporarily disabled,&lt;/li&gt;
&lt;li&gt;and finally, the spinner disappears and the button is enabled again.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;That is much closer to testing the &lt;strong&gt;real visual completion of the interaction&lt;/strong&gt; than simply stubbing a network call and assuming everything on screen behaved correctly.&lt;/p&gt;

&lt;p&gt;If you want to inspect how Cypress queued and executed everything around that flow, the &lt;strong&gt;&lt;a href="https://github.com/sclavijosuero/cypress-flaky-test-audit" rel="noopener noreferrer"&gt;cypress-flaky-test-audit&lt;/a&gt;&lt;/strong&gt; plugin is a very good partner in crime.&lt;/p&gt;

&lt;p&gt;If you feel even more reckless and want to understand how those two timelines actually interact — the Cypress queue and plain JavaScript — then you may want to venture into &lt;a href="https://dev.to/sebastianclavijo/the-async-nature-of-cypress-dont-mess-with-the-timelines-in-your-cypress-tests-dual-verse-3ehh"&gt;The Async Nature of Cypress: Don't Mess with the Timelines in Your Cypress Tests 'Dual-Verse'&lt;/a&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Case 2: The toast that tries to disappear before the witness sees it
&lt;/h2&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  The Mark
&lt;/h3&gt;

&lt;p&gt;A toast is supposed to confirm that something important happened.&lt;/p&gt;

&lt;p&gt;But if it appears and disappears too quickly, then from a user perspective it might as well have whispered the message into the void.&lt;/p&gt;

&lt;p&gt;And from a testing perspective, this creates a second problem: it is not enough to prove the toast &lt;strong&gt;appeared&lt;/strong&gt;. You may also want to prove it stayed visible &lt;strong&gt;long enough to actually matter&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is exactly where &lt;code&gt;mustLast&lt;/code&gt; enters the room.&lt;br&gt;
 &lt;/p&gt;
&lt;h3&gt;
  
  
  The Takedown
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Assertion helper executed when the toast is detected.&lt;/span&gt;
&lt;span class="c1"&gt;// It validates the toast visibility and its success message.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assertSuccessToast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// The toast must be visible while it is being observed.&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;$el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// The toast should contain the expected success message.&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;$el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Campaign saved successfully&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;validates that the success toast stays visible long enough&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/your-page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// Click the button and start observing the DOM beforehand&lt;/span&gt;
  &lt;span class="c1"&gt;// so the plugin can catch the toast as soon as it appears.&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="save-campaign-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;clickAndWatchForElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;// Element to observe after the click.&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="success-toast"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// The toast is expected to appear in this flow.&lt;/span&gt;
    &lt;span class="na"&gt;appear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// The toast is also expected to disappear before the command finishes.&lt;/span&gt;
    &lt;span class="na"&gt;disappear&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="c1"&gt;// Maximum time allowed for the full toast lifecycle.&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;8000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Check the DOM very frequently so short-lived toasts are not missed.&lt;/span&gt;
    &lt;span class="na"&gt;pollingInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Ensure the toast remains visible for at least 3 seconds.&lt;/span&gt;
    &lt;span class="c1"&gt;// This helps validate meaningful user-visible feedback, not just a blink.&lt;/span&gt;
    &lt;span class="na"&gt;mustLast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Run the custom assertions while the toast is present.&lt;/span&gt;
    &lt;span class="na"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;assertSuccessToast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="c1"&gt;// After the lifecycle is complete, the toast should no longer exist in the DOM.&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="success-toast"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not.exist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Why the Hit Lands 🔨
&lt;/h3&gt;

&lt;p&gt;This one is different from the spinner case.&lt;/p&gt;

&lt;p&gt;With a spinner, the challenge is often just to catch it at all before it disappears.&lt;/p&gt;

&lt;p&gt;With a toast, the challenge is more subtle: you also want to prove it did not vanish &lt;strong&gt;too soon&lt;/strong&gt;. In other words, the problem is not merely appearance, but &lt;strong&gt;meaningful visibility&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is exactly what &lt;code&gt;mustLast&lt;/code&gt; is for.&lt;/p&gt;

&lt;p&gt;So this test does not just verify that the toast showed up. It verifies something far more useful: that it stayed visible for at least a minimum amount of time before disappearing.&lt;/p&gt;

&lt;p&gt;That is important, because a toast that flashes too fast may technically exist in the DOM, but from a UX perspective it is almost the same as not being there at all.&lt;/p&gt;

&lt;p&gt;So in this case, &lt;code&gt;clickAndWatchForElement()&lt;/code&gt; validates the full behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the toast appears,&lt;/li&gt;
&lt;li&gt;it contains the expected message,&lt;/li&gt;
&lt;li&gt;it remains visible for at least the required duration,&lt;/li&gt;
&lt;li&gt;and then it disappears.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;That is far more aligned with actual user-facing feedback than just confirming that a success event happened somewhere in the background.&lt;/p&gt;


&lt;h2&gt;
  
  
  Case 3: The optional page-load overlay that shows up only when it feels like it
&lt;/h2&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  The Mark
&lt;/h3&gt;

&lt;p&gt;Now let’s talk about one of the most annoying UI patterns ever created:&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;modal&lt;/em&gt; / &lt;em&gt;overlay&lt;/em&gt; / &lt;em&gt;promo banner&lt;/em&gt; / &lt;em&gt;“helpful” announcement panel&lt;/em&gt; that appears on page load only &lt;strong&gt;some&lt;/strong&gt; of the time.&lt;/p&gt;

&lt;p&gt;Sometimes it shows up.&lt;br&gt;&lt;br&gt;
Sometimes it does not.&lt;br&gt;&lt;br&gt;
When it does, it blocks the page, and you need to discard.&lt;br&gt;
When it does not, your test should continue normally.&lt;/p&gt;

&lt;p&gt;This is exactly the kind of behavior that makes tests nondeterministic if you handle it with normal Cypress assertions.&lt;/p&gt;

&lt;p&gt;Because this will fail when the overlay does not exist:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="ad-overlay"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is also not enough by itself:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="ad-overlay"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="close-ad-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That kind of conditional logic often becomes ugly, fragile, and inconsistent very quickly.&lt;br&gt;
 &lt;/p&gt;
&lt;h3&gt;
  
  
  The Takedown
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This overlay is one of those annoying guests that may or may not show up.&lt;/span&gt;
&lt;span class="c1"&gt;// If it appears, handle it. If it does not, move on without failing the test.&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handles an optional startup overlay without flaking&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/modal-table-demo.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// Observe startup banner modal that may or may not appear on page load (about 50%)&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;watchForElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;// Element to observe in the DOM.&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="ad-overlay"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// The overlay may or may not appear, and both outcomes are valid&lt;/span&gt;
    &lt;span class="na"&gt;appear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;optional&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// We do not require the plugin itself to wait for the overlay to disappear&lt;/span&gt;
    &lt;span class="c1"&gt;// as part of the command lifecycle&lt;/span&gt;
    &lt;span class="na"&gt;disappear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Maximum time to wait for the optional overlay to show up&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;1500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Check the DOM very frequently so the plugin can catch the overlay&lt;/span&gt;
    &lt;span class="c1"&gt;// as soon as it appears&lt;/span&gt;
    &lt;span class="na"&gt;pollingInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// If the overlay appears, validate its visible state and confirm&lt;/span&gt;
    &lt;span class="c1"&gt;// the close button is present&lt;/span&gt;
    &lt;span class="na"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// If the overlay appears, validate its visible state and confirm&lt;/span&gt;
      &lt;span class="c1"&gt;// the close button is present&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;$el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&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;$el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="close-ad-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Close the overlay via DOM click and verify the same it is no longer visible&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;closeBtnEl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="close-ad-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;closeBtnEl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;closeBtnEl&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="c1"&gt;// Rest of your test code&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="load-data-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// [...]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Why the Hit Lands 🪏
&lt;/h3&gt;

&lt;p&gt;This is where &lt;code&gt;watchForElement()&lt;/code&gt; shines.&lt;/p&gt;

&lt;p&gt;There is no click involved here. The command simply starts observing the DOM and waits to see whether the target element appears within the configured timeout.&lt;/p&gt;

&lt;p&gt;And the crucial part is:&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;appear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;optional&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means &lt;strong&gt;both outcomes are valid&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if the overlay appears, the plugin runs the &lt;code&gt;assert&lt;/code&gt; callback and then the &lt;code&gt;action&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;if the overlay never appears, the test still passes and continues normally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is huge, because it lets you model a truly real-world UI behavior without lying to the test and without introducing flaky branching logic.&lt;/p&gt;

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

&lt;p&gt;Also, notice the &lt;code&gt;action&lt;/code&gt; callback. That is very convenient here because when the overlay is actually found and validated, you can immediately close it in a deterministic way and unblock the rest of the page.&lt;/p&gt;

&lt;p&gt;So instead of asking &lt;em&gt;“will the overlay appear this time?”&lt;/em&gt;, your test says:&lt;br&gt;&lt;br&gt;
&lt;em&gt;“If it appears, validate it and close it. If it does not, fine, move on.”&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Case 4: The full real-world mess — optional overlay + required spinner flow
&lt;/h2&gt;

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

&lt;h3&gt;
  
  
  The Mark
&lt;/h3&gt;

&lt;p&gt;Now let’s combine some of these problems into one realistic test flow.&lt;/p&gt;

&lt;p&gt;You open a page.&lt;/p&gt;

&lt;p&gt;Half of the time, an annoying overlay appears and blocks the page.&lt;br&gt;&lt;br&gt;
After dealing with that, the user clicks a button that triggers a loading spinner and a temporary loading state.&lt;/p&gt;

&lt;p&gt;In other words:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one UI element is &lt;strong&gt;optional&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;another one is &lt;strong&gt;required&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;one appears automatically,&lt;/li&gt;
&lt;li&gt;the other appears after a click,&lt;/li&gt;
&lt;li&gt;and both need to be handled in one deterministic test.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the kind of scenario where many tests start becoming fragile little monsters.&lt;br&gt;
 &lt;/p&gt;

&lt;h3&gt;
  
  
  The Takedown
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Assertion helper for the spinner/loading state.&lt;/span&gt;
&lt;span class="c1"&gt;// It validates the spinner itself plus the related UI signals.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assertLoadCampaignDataSpinner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&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;$root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;closest&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;labelFetching&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="loading-row"] .loading-label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&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;labelFetching&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&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;labelFetching&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fetching latest campaign analytics&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;loadDataBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="load-data-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&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;loadDataBtn&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&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;loadDataBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handles optional overlay and validates the spinner flow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Open the demo page that contains both behaviors:&lt;/span&gt;
  &lt;span class="c1"&gt;// an optional blocking overlay on load and a spinner triggered by a button click.&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/modal-table-demo.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// First, watch for the optional overlay that may or may not appear on page load.&lt;/span&gt;
  &lt;span class="c1"&gt;// If it appears, validate it and close it so the rest of the test can continue.&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;watchForElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="ad-overlay"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;appear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;optional&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;disappear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pollingInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&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;$el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="close-ad-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&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;closeBtnEl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="close-ad-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;closeBtnEl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;closeBtnEl&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="c1"&gt;// Then, trigger the load-data action and observe the spinner lifecycle.&lt;/span&gt;
  &lt;span class="c1"&gt;// The spinner is required to appear and later disappear, while the custom&lt;/span&gt;
  &lt;span class="c1"&gt;// assertion helper validates the loading state during that window.&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="load-data-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;clickAndWatchForElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="service-spinner"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;appear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;disappear&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;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="na"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;assertLoadCampaignDataSpinner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="c1"&gt;// Finally, confirm the loading flow completed successfully&lt;/span&gt;
  &lt;span class="c1"&gt;// and the UI returned to its interactive state.&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.loading-label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not.be.visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="load-data-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.enabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  Why the Hit Lands 🪦
&lt;/h3&gt;

&lt;p&gt;This example is the one I like the most because it represents the kind of ugly, imperfect, wonderfully realistic UI flow we often have to automate.&lt;/p&gt;

&lt;p&gt;The plugin allows each moving part to be described according to its real behavior:&lt;/p&gt;

&lt;h4&gt;
  
  
  1) The overlay is treated as optional
&lt;/h4&gt;

&lt;p&gt;It may appear or not, and the test remains stable in both cases.&lt;/p&gt;

&lt;h4&gt;
  
  
  2) The spinner is treated as required
&lt;/h4&gt;

&lt;p&gt;Once the user clicks the button, the spinner must show up, pass the assertions, and disappear correctly.&lt;/p&gt;

&lt;h4&gt;
  
  
  3) The test still reflects what the user experiences
&lt;/h4&gt;

&lt;p&gt;This is not a fake synchronization trick.&lt;br&gt;&lt;br&gt;
It is validating actual DOM behavior that the user would perceive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a blocking overlay may interrupt the flow,&lt;/li&gt;
&lt;li&gt;the user closes it,&lt;/li&gt;
&lt;li&gt;the user click a button&lt;/li&gt;
&lt;li&gt;then a loading state appears,&lt;/li&gt;
&lt;li&gt;then it goes away,&lt;/li&gt;
&lt;li&gt;then the UI becomes interactive again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You are testing that the interface behaved properly under realistic conditions.&lt;/p&gt;

&lt;p&gt;And yes, &lt;strong&gt;that includes those fast DOM transitions that traditional Cypress chaining can miss&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT3: RESOLUTION
&lt;/h1&gt;

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

&lt;p&gt;Some UI elements are simply built like tiny rebels.&lt;/p&gt;

&lt;p&gt;They show up too fast.&lt;br&gt;&lt;br&gt;
They disappear too fast.&lt;br&gt;&lt;br&gt;
They block your page unexpectedly.&lt;br&gt;&lt;br&gt;
They appear only half the time.&lt;br&gt;&lt;br&gt;
And then they sit there, very proudly, making your tests flaky and your patience shorter.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;&lt;code&gt;wick-dom-observer&lt;/code&gt;&lt;/strong&gt; turns those flaky little ghosts into observable DOM evidence.&lt;/p&gt;

&lt;p&gt;It gives Cypress a much better chance to deal with these troublesome UI behaviors in a deterministic and elegant way, without forcing you into &lt;code&gt;cy.wait(...)&lt;/code&gt; nonsense, awkward conditionals, intersects, or synthetic workarounds that do not really test real user experience.&lt;/p&gt;

&lt;p&gt;So if your application contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fast spinners,&lt;/li&gt;
&lt;li&gt;short-lived toasts,&lt;/li&gt;
&lt;li&gt;startup overlays,&lt;/li&gt;
&lt;li&gt;optional modals,&lt;/li&gt;
&lt;li&gt;or any UI element that behaves like it does not respect authority...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;then &lt;strong&gt;this plugin was made for you&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because sometimes what your test suite really needs is not more waiting, not more retries, and definitely not more hope.&lt;/p&gt;

&lt;p&gt;It needs a proper DOM observer.&lt;/p&gt;

&lt;p&gt;You can find the plugin here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub repo:&lt;/strong&gt; &lt;a href="https://github.com/sclavijosuero/wick-dom-observer" rel="noopener noreferrer"&gt;https://github.com/sclavijosuero/wick-dom-observer&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;npm:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/wick-dom-observer" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/wick-dom-observer&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;strong&gt;I'd love to hear from you! Please don't forget to follow me, leave a comment, or a reaction if you found this article useful or insightful.&lt;/strong&gt;&lt;/em&gt; ❤️ 🦄 🤯 🙌 🔥&lt;/p&gt;

&lt;p&gt;You can also connect with me on my new &lt;strong&gt;YouTube channel&lt;/strong&gt;: &lt;a href="https://www.youtube.com/@SebastianClavijoSuero" rel="noopener noreferrer"&gt;https://www.youtube.com/@SebastianClavijoSuero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to support my work, consider buying me a coffee or contributing to a training session, so I can keep learning and sharing cool stuff with all of you.&lt;br&gt;
Thank you for your support!&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/sclavijosuero" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cypress</category>
      <category>qa</category>
      <category>automation</category>
      <category>testing</category>
    </item>
    <item>
      <title>WICK-A11Y v3.0.1: Cypress v16 Ready - Upgrade Without Fear (Fully Backward Compatible)</title>
      <dc:creator>Sebastian Clavijo Suero</dc:creator>
      <pubDate>Sun, 15 Mar 2026 00:15:52 +0000</pubDate>
      <link>https://forem.com/sebastianclavijo/wick-a11y-v301-cypress-v16-ready-upgrade-without-fear-fully-backward-compatible-77m</link>
      <guid>https://forem.com/sebastianclavijo/wick-a11y-v301-cypress-v16-ready-upgrade-without-fear-fully-backward-compatible-77m</guid>
      <description>&lt;h1&gt;
  
  
  ACT 1: EXPOSITION
&lt;/h1&gt;

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

&lt;p&gt;Ok, Cypress v16 is coming, and our always-friendly Cypress environment variables are going to be revamped.&lt;/p&gt;

&lt;p&gt;Why? Security reasons. &lt;code&gt;Cypress.env()&lt;/code&gt; variables are exposed in the browser, so they could potentially be accessed by "anyone."&lt;/p&gt;

&lt;p&gt;Starting in Cypress v15.10.0, two new types of environment variables have been introduced: &lt;code&gt;cy.env()&lt;/code&gt; (for private stuff) and &lt;code&gt;Cypress.expose()&lt;/code&gt; (for non-sensitive information). The almighty &lt;code&gt;Cypress.env()&lt;/code&gt; has been deprecated and &lt;strong&gt;will be gone starting in Cypress v16&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This article is not meant to explain what the change is in detail, or how you can migrate your existing Cypress framework to the new environment model. For that, you can (and will) find extremely helpful information in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cypress official documentation: &lt;a href="https://docs.cypress.io/app/references/migration-guide" rel="noopener noreferrer"&gt;Migrating away from Cypress.env()&lt;/a&gt; and &lt;a href="https://docs.cypress.io/app/guides/environment-variables" rel="noopener noreferrer"&gt;Environment Variables &amp;amp; Secrets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Gleb Bahmutov's article: &lt;a href="https://glebbahmutov.com/blog/cypress-expose/" rel="noopener noreferrer"&gt;Migrating From Cypress.env To cy.env and Cypress.expose Methods&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, because of this change, many open-source community plugins that rely on env variables for configuration or usage will need to be updated for the new environment variable model.&lt;/p&gt;

&lt;p&gt;And the powerful and beloved plugin WICK-A11Y for accessibility in Cypress is no different, because it uses a couple of environment variables for configuration: &lt;code&gt;enableAccessibilityVoice&lt;/code&gt; and &lt;code&gt;generateReport&lt;/code&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT 2: CONFRONTATION
&lt;/h1&gt;

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

&lt;p&gt;Since both of those environment variables are not storing sensitive information, they totally could be exposed ones. No biggie.&lt;/p&gt;

&lt;p&gt;However, in your &lt;strong&gt;wick-a11y&lt;/strong&gt; configuration inside your Cypress framework, you might have configured those variables in your &lt;code&gt;cypress.env.json&lt;/code&gt; file, just like you may have done with all the other env variables in your system. Yeah, why not!&lt;/p&gt;

&lt;p&gt;The thing is, &lt;strong&gt;in Cypress’s new env variable model, anything configured in cypress.env.json is automatically considered a private env variable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That means it would need to be read with &lt;code&gt;cy.env()&lt;/code&gt;, which is actually a Cypress command that can only be executed within a hook or a test.&lt;/p&gt;

&lt;p&gt;So yeah, forget about accessing anything you defined in your &lt;code&gt;cypress.env.json&lt;/code&gt; synchronously at the beginning of your test suite using the old &lt;code&gt;Cypress.env()&lt;/code&gt; like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apiUrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Will disappear&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And no, you cannot do this either:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apiUrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Ouch. Right, it is a Cypress command!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can only access environment variables asynchronously if they were defined as exposed variables in &lt;code&gt;cypress.config.js&lt;/code&gt; under the &lt;code&gt;expose&lt;/code&gt; property, or passed through the CLI with the &lt;code&gt;--expose&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;Yeah... it makes sense, but this is not clearly warned anywhere in the Cypress documentation, so I learned it the hard way while migrating wick-a11y to be ready for Cypress v16.&lt;/p&gt;

&lt;p&gt;And that was... interesting.&lt;/p&gt;

&lt;p&gt;The thing is, &lt;strong&gt;wick-a11y&lt;/strong&gt;, to do its wicked magic, accesses those two environment variables (&lt;code&gt;enableAccessibilityVoice&lt;/code&gt; and &lt;code&gt;generateReport&lt;/code&gt;) inside a Cypress event, not inside a Cypress hook or test.&lt;/p&gt;

&lt;p&gt;This means they cannot be accessed using the &lt;code&gt;cy.env()&lt;/code&gt; command, and instead have to be accessed synchronously with &lt;code&gt;Cypress.expose()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Again, no biggie. I can make them exposed variables, but this might become an little inconvenience for &lt;strong&gt;YOU&lt;/strong&gt;, my friend, using wick-a11y.&lt;/p&gt;

&lt;p&gt;If you have configured those env variables in your wick-a11y implementation inside &lt;code&gt;cypress.env.json&lt;/code&gt; and/or passed them through the CLI as &lt;code&gt;--env&lt;/code&gt; parameters, then for wick-a11y to use them as exposed variables, you would need to move them into &lt;code&gt;cypress.config.js&lt;/code&gt; or pass them through the CLI as &lt;code&gt;--expose&lt;/code&gt; parameters instead.&lt;/p&gt;

&lt;p&gt;And that means changing your CLI script definitions, messing around with your Cypress configuration, and cleaning up your &lt;code&gt;cypress.env.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To avoid that, I prepared the wick-a11y accessibility plugin to accept both environment variables (&lt;code&gt;enableAccessibilityVoice&lt;/code&gt; and &lt;code&gt;generateReport&lt;/code&gt;) as either exposed or non-exposed. And that’s totally fine, because these are just configuration variables, no secrets hiding in the shadows here.&lt;/p&gt;

&lt;p&gt;That means... &lt;strong&gt;you can migrate your project to Cypress v15.10.0 (or v16.0 when it comes out) while using the &lt;code&gt;wick-a11y&lt;/code&gt; plugin WITHOUT ANY CHANGE in your Cypress framework.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;WICK-A11Y v3.0.1 is fully backward compatible for Cypress v15.10.0 projects moving forward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No fear!&lt;/strong&gt; 🙂&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that wick-a11y v3.0.1 is not intended for Cypress versions prior to 15.10.0.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  ACT3: RESOLUTION
&lt;/h1&gt;

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

&lt;p&gt;So, if you are already on Cypress v15.10.0+ and using the open-source wick-a11y accessibility plugin for Cypress... come on... install wick-a11y v3.0.1!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More cool features are coming soon in wick-a11y v3.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I also have updated the &lt;a href="https://github.com/sclavijosuero/wick-a11y-sample-project" rel="noopener noreferrer"&gt;wick-a11y-sample-project&lt;/a&gt; to use Cypress v15.11.0 and wick-a11y v3.0.1&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;strong&gt;I'd love to hear from you! Please don't forget to follow me, leave a comment, or a reaction if you found this article useful or insightful.&lt;/strong&gt;&lt;/em&gt; ❤️ 🦄 🤯 🙌 🔥&lt;/p&gt;

&lt;p&gt;You can also connect with me on my new &lt;strong&gt;YouTube channel&lt;/strong&gt;: &lt;a href="https://www.youtube.com/@SebastianClavijoSuero" rel="noopener noreferrer"&gt;https://www.youtube.com/@SebastianClavijoSuero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to support my work, consider buying me a coffee or contributing to a training session, so I can keep learning and sharing cool stuff with all of you.&lt;br&gt;
Thank you for your support!&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/sclavijosuero" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cypress</category>
      <category>qa</category>
      <category>a11y</category>
      <category>automation</category>
    </item>
    <item>
      <title>Cypress.Promise.all() and cy.mapChain(): Two Hidden Gems for Cypress You Won’t Let Go Once You Try Them</title>
      <dc:creator>Sebastian Clavijo Suero</dc:creator>
      <pubDate>Wed, 21 Jan 2026 03:55:43 +0000</pubDate>
      <link>https://forem.com/sebastianclavijo/cypresspromiseall-and-cymapchain-two-hidden-gems-for-cypress-you-wont-let-go-once-you-try-20mj</link>
      <guid>https://forem.com/sebastianclavijo/cypresspromiseall-and-cymapchain-two-hidden-gems-for-cypress-you-wont-let-go-once-you-try-20mj</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Cleaner async patterns for sharper Cypress tests!&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT 1: EXPOSITION
&lt;/h1&gt;

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

&lt;p&gt;Most of us (if not all) are familiar with the JavaScript Array &lt;code&gt;map()&lt;/code&gt; method. There is hardly anything better when you want to process or transform every element of an array into a new one based on a transformation function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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;7&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;newArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&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;// Expected output: Array [3, 11, 27, 51]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example is trivial, of course, but that transformation function can be anything, from a simple calculation to something like a 90° clockwise matrix rotation. You name it!&lt;/p&gt;

&lt;p&gt;I am sure many of us are also familiar with a very similar concept, but this time in the &lt;strong&gt;Cypress&lt;/strong&gt; world. I am talking about the fantastic &lt;code&gt;cy.map()&lt;/code&gt; command from the multifaceted &lt;a href="https://github.com/bahmutov/cypress-map" rel="noopener noreferrer"&gt;cypress-map&lt;/a&gt; plugin by &lt;a href="https://www.linkedin.com/in/bahmutov" rel="noopener noreferrer"&gt;Gleb Bahmutov&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You pass a Cypress chainable (yielding an array or a jQuery object) as the subject, apply a JavaScript function to its yielded value, and it returns a new chainable whose elements are the result of that transformation. It can also map each object directly to one of its properties.&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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;7&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&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;// Expected output: Cypress Chainable [3, 11, 27, 51]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if what you want to apply to all the elements of the subject chainable is not a function, but another Cypress command? Or worse… what if you want to merge the results of multiple custom commands into a single output?&lt;/p&gt;

&lt;p&gt;Yeah… now we’re talking. 😏&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT 2: CONFRONTATION
&lt;/h1&gt;

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

&lt;p&gt;A few months ago, I was working on a new plugin to handle internationalized messages in a Cypress framework (I will probably write an article about it in the future).&lt;/p&gt;

&lt;p&gt;Without going into too much detail, it leverages the powerful JavaScript library &lt;strong&gt;i18next&lt;/strong&gt; to manage multilingual message files, fallback languages, fallback translations, and many other features that simplify the often cumbersome task of multilingual management.&lt;/p&gt;

&lt;p&gt;To provide a bit of context, translations for a given message ID and its arguments were returned by a Cypress custom command in the plugin, along with support and helper utilities for multilingual assertions.&lt;/p&gt;

&lt;p&gt;You define all the translations of a message for each supported language, and then use a command like &lt;code&gt;cy.getIntlMessage()&lt;/code&gt; to retrieve the appropriate one from those resources for the desired language.&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;// Messages definition&lt;/span&gt;
&lt;span class="nx"&gt;i18next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My age… {{age}} years!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tell.me.your.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How old are you, {{name}}?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es-ES&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hola.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mi edad… ¡{{age}} años!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tell.me.your.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;¿Cual es tu edad, {{name}}?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;el-GR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Γεια σου.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Η ηλικία μου… {{age}} χρονών!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tell.me.your.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Πόσων χρονών είσαι, {{name}};&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="c1"&gt;// Rest of languages [...]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Rest of i18next properties [...]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So a call to &lt;code&gt;cy.getIntlMessage()&lt;/code&gt; like this:&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;// "my.age" is the message key, age is the interpolation key&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;es-ES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It would yield a Cypress chainable holding the value &lt;strong&gt;&lt;code&gt;"Mi edad… ¡100 años!"&lt;/code&gt;&lt;/strong&gt; &lt;em&gt;(in plain English: "My age… 100 years!")&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And something like:&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;// "tell.me.your.age" is the message key, name is he interpolation key&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tell.me.your.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;el-GR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toúla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It would yield a Cypress chainable holding the text &lt;strong&gt;&lt;code&gt;"Πόσο χρονών είσαι, Toúla;"&lt;/code&gt;&lt;/strong&gt; &lt;em&gt;(or in English: "What's your age, Toúla?")&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Nice, right?&lt;/p&gt;

&lt;p&gt;Now let’s say we need to concatenate multiple internationalized messages into a single string when the translations come from a custom command, maybe to make an assertion or to fill an input field.&lt;/p&gt;

&lt;p&gt;How would you do it?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Hmmm..."&lt;/em&gt; 🤔&lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Using The All Times "Classic" Way
&lt;/h2&gt;

&lt;p&gt;Given that Cypress code runs asynchronously in its own command queue, the chainable returned by a Cypress command &lt;strong&gt;cannot&lt;/strong&gt; be stored in a regular JavaScript variables like:&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;// Forget about it! 🚫&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myAge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;es-ES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tellMeYourAge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tell.me.your.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;el-GR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toúla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This is a big &lt;strong&gt;no-no&lt;/strong&gt; in Cypress, and if you somehow still don’t believe me, check out my article "&lt;a href="https://dev.to/sebastianclavijo/the-async-nature-of-cypress-dont-mess-with-the-timelines-in-your-cypress-tests-dual-verse-3ehh"&gt;The Async Nature of Cypress: Don't Mess with the Timelines in Your Cypress Tests 'Dual-Verse'&lt;/a&gt;".&lt;/p&gt;

&lt;p&gt;Sure, you can store the value in a Cypress alias or yield it to the next chained command:&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;// Stored in an alias (a single message)&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;es-ES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myAgeMsg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But how can you combine two messages returned by &lt;code&gt;cy.getIntlMessage()&lt;/code&gt; into a single one?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Why, you ask? That should be pretty easy-peasy!"&lt;/em&gt; 🥱&lt;/p&gt;

&lt;p&gt;Yeah… fair enough. I bet most Cypress automation folks would instinctively write something like this:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;es-ES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message1&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tell.me.your.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;el-GR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toúla&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message2&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;message1&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;message2&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cy&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;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;// Output in Cypress log: "Mi edad… ¡100 años! Πόσων χρονών είσαι, Toúla;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The classic way!&lt;/p&gt;

&lt;p&gt;Or, if you decide to go the alias route:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;es-ES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myAgeMsg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tell.me.your.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;el-GR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toúla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tellMeYourName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myAgeMsg&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message1&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tellMeYourName&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message2&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;message1&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;message2&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cy&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;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;// Output in Cypress log: "Mi edad… ¡100 años! Πόσων χρονών είσαι, Toúla;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We could even create a custom Cypress command to simplify the concatenation, something like:&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;// Command to concat intl messages&lt;/span&gt;
&lt;span class="nx"&gt;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;concatIntlMessages_classic&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;arg1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arg2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg1&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;arg2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg2&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;msg1&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;msg2&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;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concatIntlMessages_classic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;es-ES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tell.me.your.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;el-GR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toúla&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cy&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;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;// Output in Cypress log: "Mi edad… ¡100 años! Πόσων χρονών είσαι, Toúla;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the Cypress command, the test code looks &lt;strong&gt;cleaner&lt;/strong&gt;. Very nice!&lt;/p&gt;

&lt;p&gt;Ehhh… I’d say &lt;strong&gt;cleaner-ish&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Why is that?"&lt;/em&gt; 🤨&lt;/p&gt;

&lt;p&gt;And here is where I become the buzzkill…&lt;br&gt;
What if it is not two messages, but three?&lt;br&gt;
Four?&lt;br&gt;
Five?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Hmmm… just keep going. Nest as many .then() calls as you need!"&lt;/em&gt; 🥱🥱&lt;/p&gt;

&lt;p&gt;So… let me get this straight.&lt;br&gt;
You are suggesting we create a command chain that just &lt;strong&gt;keeps going&lt;/strong&gt;…?&lt;br&gt;
Something that, for &lt;em&gt;four&lt;/em&gt; messages, looks like this:&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;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;concatIntlMessages_classic_fantastic4&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;arg1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arg2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arg3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arg4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg1&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;arg2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg2&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;arg3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg3&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
              &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;arg4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg4&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;msg1&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;msg2&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;msg3&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;msg4&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;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concatIntlMessages_classic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en-US&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; 🙂 &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;es-ES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tell.me.your.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;el-GR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toúla&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cy&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;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;// Output in Cypress log: "Hello. 🙂 Mi edad… ¡100 años! Πόσων χρονών είσαι, Toúla;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I do not think so. I’m pretty sure we can do better than that.&lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Using Cypress.Promise.all()
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Cypress.Promise.all&lt;/code&gt; is Cypress’s version of &lt;code&gt;Promise.all&lt;/code&gt;, built on Bluebird. It lets you run multiple asynchronous operations at the same time and waits until &lt;strong&gt;all&lt;/strong&gt; of them finish (or stops as soon as &lt;strong&gt;one&lt;/strong&gt; fails).&lt;/p&gt;

&lt;p&gt;You can learn about &lt;code&gt;Cypress.Promise&lt;/code&gt; utility in the Cypress product documentation &lt;a href="https://docs.cypress.io/api/utilities/promise" rel="noopener noreferrer"&gt;https://docs.cypress.io/api/utilities/promise&lt;/a&gt;. However, you will not find dedicated section showing &lt;code&gt;Cypress.Promise.all&lt;/code&gt;, so let’s talk about that here.&lt;/p&gt;

&lt;p&gt;At its core, Bluebird’s &lt;code&gt;Promise.all()&lt;/code&gt; takes an iterable (usually an array) of Promises and returns one single Promise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If &lt;strong&gt;every&lt;/strong&gt; Promise resolves, the returned Promise resolves with an array of results (same order as the input).&lt;/li&gt;
&lt;li&gt;If &lt;strong&gt;any&lt;/strong&gt; Promise rejects, the returned Promise immediately rejects with the first error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that is &lt;strong&gt;exactly&lt;/strong&gt; what we need to concatenate multiple translated messages returned by a Cypress command: gather them all, then merge them into a single string. After all, Cypress chainables &lt;em&gt;walk&lt;/em&gt;, &lt;em&gt;talk&lt;/em&gt;, &lt;em&gt;and quack like promises&lt;/em&gt;. And that is precisely the universe in which &lt;code&gt;Cypress.Promise.all&lt;/code&gt; was born. It takes an array of Cypress commands invocations, and returns a Premise that resolves to an array containing all resolved results, in order.&lt;/p&gt;

&lt;p&gt;So let’s summon a custom command that does what we need:&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;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;concatIntlMessages_promiseAll&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;commands&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the test code that uses it ends up looking something like this:&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concatIntlMessages_promiseAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en-US&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; 🙂 &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;es-ES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tell.me.your.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;el-GR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toúla&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cy&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;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;// Output in Cypress log: "Hello. 🙂 Mi edad… ¡100 años! Πόσων χρονών είσαι, Toúla;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break this thing down plain and simple.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;In the test code, we pass to our custom command &lt;code&gt;cy.concatIntlMessages_promiseAll()&lt;/code&gt; a mix of inputs: several &lt;code&gt;cy.getIntlMessage()&lt;/code&gt; chainables and even plain synchronous values (like [" 🙂 "] - why not?).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Cypress.Promise.all(commands)&lt;/code&gt; waits until every item has "settled" into a resolved value, then yields an array of results in the same order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, our &lt;code&gt;.then((text) =&amp;gt; text.join(' '))&lt;/code&gt; concatenates that array into a single string, which is yielded as the result of the custom command.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;"There you go!"&lt;/p&gt;

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

&lt;p&gt;DISCLAIMER: I borrowed the line from &lt;em&gt;My Big Fat Greek Wedding&lt;/em&gt; and the image from &lt;em&gt;The New York Times&lt;/em&gt; &lt;a href="https://www.nytimes.com/2021/09/08/arts/michael-constantine-father-in-my-big-fat-greek-wedding-dies-at-94.html" rel="noopener noreferrer"&gt;article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Anything else?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Please, be patient, we are not quite done yet.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Using cy.mapChain()
&lt;/h2&gt;

&lt;p&gt;This Cypress command, &lt;code&gt;cy.mapChain()&lt;/code&gt;, is available in the &lt;a href="https://github.com/bahmutov/cypress-map" rel="noopener noreferrer"&gt;cypress-map&lt;/a&gt; plugin, and it was not familiar to me until quite recently. Once I stumbled upon it, I was pretty upset I had not discovered it earlier. That is what happens when &lt;strong&gt;you don’t read a plugin’s documentation all the way to the end&lt;/strong&gt;. You miss these little gems!&lt;/p&gt;

&lt;p&gt;After this "mea culpa", we are going to know what this command ("&lt;em&gt;not a query!&lt;/em&gt;" as the own author stated) does:&lt;/p&gt;

&lt;p&gt;It processes each element of the provided array-like chainable (an array or a jQuery object) individually, applies &lt;em&gt;&lt;strong&gt;Cypress command&lt;/strong&gt; functions&lt;/em&gt; to each one, and returns an array-like chainable with the same number of elements.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"What?! Apply a Cypress command? Not a regular function?"&lt;/em&gt; 🫨&lt;/p&gt;

&lt;p&gt;Actually, you can also use &lt;em&gt;synchronous&lt;/em&gt;, or even &lt;em&gt;asynchronous&lt;/em&gt;, functions as well! 🤯&lt;/p&gt;

&lt;p&gt;Cool, right? So, in a few words, it is like a &lt;code&gt;cy.map()&lt;/code&gt; command whose mapping function can run Cypress commands, and it does not yield a result until all elements have been processed.&lt;/p&gt;

&lt;p&gt;And to me, it seems like we could use this precisely to concatenate the results of our multiple &lt;code&gt;cy.getIntlMessage()&lt;/code&gt; calls.&lt;/p&gt;

&lt;p&gt;So the new custom command, and its invocation in a test, could look something like this:&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;// Custom command using cy.mapChain()&lt;/span&gt;
&lt;span class="nx"&gt;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;concatIntlMessages_mapChain&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;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// args should be arrays like ["hello", {lng:...}]&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mapChain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntlMessage&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Command invocation&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concatIntlMessages_mapChain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en-US&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; 🙂 &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;es-ES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tell.me.your.age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;el-GR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toúla&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cy&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;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;// Output in Cypress log: "Hello. 🙂 Mi edad… ¡100 años! Πόσων χρονών είσαι, Toúla;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;In the test code, we call &lt;code&gt;cy.concatIntlMessages_mapChain()&lt;/code&gt; with several arguments, each representing the input needed to retrieve an internationalized message (for example, a message key, its language and options). These arguments define what messages we want and in which order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inside the custom command, &lt;code&gt;cy.mapChain()&lt;/code&gt; is the key player. It takes that wrapped list of arguments and processes them one by one, running a Cypress command &lt;code&gt;cy.getIntlMessage()&lt;/code&gt; for each element instead of a plain JavaScript function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, once &lt;code&gt;mapChain()&lt;/code&gt; has processed all elements, the resulting array of messages is joined into a single string with &lt;code&gt;.then((text) =&amp;gt; text.join(' '))&lt;/code&gt;, and yielded as the result of the custom command.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;cy.mapChain()&lt;/code&gt; waits until all those Cypress commands have finished and then yields an array with all the resolved values, in the same order, to be combined in this case in single message string.&lt;/p&gt;

&lt;p&gt;And again... "there you go!"&lt;/p&gt;

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

&lt;p&gt;⚠️ DISCLAIMER: Yeah, sorry! Using the same image twice in the same article might be a bit much for some, but I couldn’t help myself. I really love the movie, and especially Gus Portokalos. Still, probably not quite as powerful as what we’ll get in &lt;em&gt;John Wick: Chapter 5&lt;/em&gt;. 😄&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Ok, fine. You win."&lt;/em&gt; 😜&lt;/p&gt;

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




&lt;h1&gt;
  
  
  ACT3: RESOLUTION
&lt;/h1&gt;

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

&lt;p&gt;Ok, we have finally reached the end of our Greek movie detour, where we learned new expressions in all three languages and, along the way, a couple of really &lt;em&gt;útiles&lt;/em&gt; Cypress &lt;em&gt;εργαλεία&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Cypress.Promise.all()&lt;/code&gt; is ideal when we want to run multiple Cypress commands and wait for all of them to finish, with all the yielded values returned as an array-like chainable.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cy.mapChain()&lt;/code&gt; excels when we want to apply a custom command to all the elements of an array-like Cypress chainable and yield the results, also as an array-like chainable.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Cypress.Promise.all()&lt;/code&gt; and &lt;code&gt;cy.mapChain()&lt;/code&gt;, although quite different in how they work, both helped solve the same problem, just with a little imagination.&lt;/p&gt;

&lt;p&gt;One approaches it from the Promise side, coordinating multiple async operations and resolving them together, while the other stays firmly in Cypress territory, transforming values as they move through the command chain. Different mental models, same outcome: &lt;strong&gt;cleaner&lt;/strong&gt;, &lt;strong&gt;more expressive tests&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can find the examples of this article in GitHub &lt;a href="https://github.com/sclavijosuero/cypressPromise-mapChain" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now it is up to you to decide how you want to use them to make your automation experience a bit more &lt;em&gt;pleasant&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Or maybe not... Your call, no judgment. 😊&lt;/p&gt;

&lt;p&gt;And... "&lt;strong&gt;THERE YOU GO&lt;/strong&gt;!!!" (one last time)&lt;/p&gt;

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

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




&lt;p&gt;&lt;em&gt;&lt;strong&gt;I'd love to hear from you! Please don't forget to follow me, leave a comment, or a reaction if you found this article useful or insightful.&lt;/strong&gt;&lt;/em&gt; ❤️ 🦄 🤯 🙌 🔥&lt;/p&gt;

&lt;p&gt;You can also connect with me on my new &lt;strong&gt;YouTube channel&lt;/strong&gt;: &lt;a href="https://www.youtube.com/@SebastianClavijoSuero" rel="noopener noreferrer"&gt;https://www.youtube.com/@SebastianClavijoSuero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are feeling especially generous and enjoy my articles, you can buy me a coffee or contribute to a training session. In both cases, my brain will definitely thank you for it! ☕😄 &lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/sclavijosuero" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cypress</category>
      <category>qa</category>
      <category>automation</category>
    </item>
    <item>
      <title>CYPRESS-FLAKY-TEST-AUDIT: thriving in the Cypress 'Dual-Verse' for once!</title>
      <dc:creator>Sebastian Clavijo Suero</dc:creator>
      <pubDate>Fri, 02 Jan 2026 01:37:00 +0000</pubDate>
      <link>https://forem.com/sebastianclavijo/cypress-flaky-testaudit-thriving-in-the-cypress-dual-verse-for-once-l4o</link>
      <guid>https://forem.com/sebastianclavijo/cypress-flaky-testaudit-thriving-in-the-cypress-dual-verse-for-once-l4o</guid>
      <description>&lt;p&gt;When flaky tests stop being &lt;em&gt;random&lt;/em&gt; and start being a &lt;em&gt;pattern&lt;/em&gt;, it is time to stop guessing and start auditing. This is the story of how to make peace with &lt;em&gt;Cypress’ Dual-Verse&lt;/em&gt;... and actually win.&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT 1: EXPOSITION
&lt;/h1&gt;

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

&lt;p&gt;At some point in every Cypress project, a test fails.&lt;/p&gt;

&lt;p&gt;You re-run it.&lt;br&gt;
It passes.&lt;/p&gt;

&lt;p&gt;You shrug, blame CI, and move on with your life.&lt;/p&gt;

&lt;p&gt;Then it happens again. And again. And somehow only on Tuesdays, only in CI, or only when Mercury is in retrograde.&lt;/p&gt;

&lt;p&gt;Welcome back to the Cypress Dual-Verse: the place where synchronous JavaScript and Cypress’ asynchronous command queue coexist peacefully… until they absolutely don’t.&lt;/p&gt;

&lt;p&gt;If this sounds familiar, you are not alone. In one of my previous articles, I went deep (very deep) into why Cypress behaves this way and how mixing sync JS with async commands can quietly sabotage your tests:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://dev.to/sebastianclavijo/the-async-nature-of-cypress-dont-mess-with-the-timelines-in-your-cypress-tests-dual-verse-3ehh"&gt;The Async Nature of Cypress: Don’t Mess With the Timelines&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I definitely wanted to write an article about how this asynchronous command queue works, but I wanted to approach it in a way that is easier to understand and more intuitive, without sacrificing depth or the practical knowledge you actually need to tackle it effectively.&lt;/p&gt;

&lt;p&gt;That is when I came up with the idea of presenting it as a quirky 'dual-universe' concept, using a very visual diagram so you can clearly see what is going on behind the scenes.&lt;/p&gt;

&lt;p&gt;For a long time after publishing the article, a (somewhat crazy) idea kept spinning in my head… What if I could see a visual representation while implementing and running those rebellious tests that keep failing or behaving flakily, tests where I can’t quite put my finger on why, but I strongly suspect (and I’m usually right) that race conditions are to blame?&lt;/p&gt;

&lt;p&gt;But man! This is not an easy task. It would mean diving deep into low-level test details and command interactions, then somehow turning all that chaos into something clear, understandable, and fully portable to a CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;And why stop there? Why not visualize it all with intuitive graphs (like in the 'dual-verse' article) that the human brain can actually enjoy digesting?&lt;/p&gt;

&lt;p&gt;After all… you know the saying: a 🖼️ is worth more than a thousand long 📜📜📜. 😅&lt;/p&gt;

&lt;p&gt;That is where CYPRESS-FLAKY-TEST-AUDIT plugin comes in.&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT 2: CONFRONTATION
&lt;/h1&gt;

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

&lt;p&gt;Flaky tests &lt;strong&gt;rarely announce themselves politely&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They &lt;em&gt;don’t fail everywhere&lt;/em&gt;.&lt;br&gt;
They &lt;em&gt;don’t fail consistently&lt;/em&gt;.&lt;br&gt;
And they definitely &lt;em&gt;don’t fail when you are watching&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Most of the time, the root cause is subtle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cypress commands mixed with sync control flow&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;State&lt;/em&gt; captured too early&lt;/li&gt;
&lt;li&gt;Variables that &lt;em&gt;look&lt;/em&gt; populated… but aren’t (yet)&lt;/li&gt;
&lt;li&gt;Assertions running outside the Cypress command chain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words: &lt;strong&gt;Dual-Verse violations&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The problem? These patterns are easy to write and painfully hard to spot, especially once your test suite grows beyond a handful of specs, or when you are staring at code written by someone else.&lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  Presenting &lt;code&gt;cypress-flaky-test-audit&lt;/code&gt;!
&lt;/h2&gt;

&lt;p&gt;This plugin does one simple but powerful thing:&lt;/p&gt;

&lt;p&gt;It scans your Cypress tests as they run and shows you (clearly and intuitively, no kidding!) what is running at every moment and exactly where things go wrong, &lt;strong&gt;making flakiness-prone patterns much easier to spot&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Not &lt;em&gt;hypotheticals&lt;/em&gt;.&lt;br&gt;
Not &lt;em&gt;style opinions&lt;/em&gt;.&lt;br&gt;
Real "this will bite you later" code smells.&lt;/p&gt;

&lt;p&gt;And as someone wise once said…&lt;br&gt;
"&lt;em&gt;No gimmicks. No AI. Just the information you need to understand why a test is flaky&lt;/em&gt;". 🙂&lt;/p&gt;

&lt;p&gt;It &lt;em&gt;doesn’t rewrite your tests&lt;/em&gt;.&lt;br&gt;
It &lt;em&gt;doesn’t block your CI&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It simply shines a flashlight into the dark corners of your test suite.&lt;/p&gt;

&lt;p&gt;You can run it locally, in CI, or as part of a quality gate, and decide how strict you want to be.&lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  What the plugin &lt;em&gt;actually&lt;/em&gt; does (and how that helps you)
&lt;/h2&gt;

&lt;p&gt;Unlike a static pattern detector or flake predictor, &lt;strong&gt;cypress-flaky-test-audit&lt;/strong&gt; gives you &lt;strong&gt;runtime insights into what your tests *actually did&lt;/strong&gt;*, command by command.&lt;/p&gt;

&lt;p&gt;In other words, it does not guess where flakiness &lt;em&gt;might&lt;/em&gt; be; it shows you exactly what happened when your tests ran.&lt;/p&gt;

&lt;p&gt;At a practical level, it provides:&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Per-command execution details
&lt;/h3&gt;

&lt;p&gt;For every Cypress command in a run, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the exact &lt;strong&gt;execution order&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;timing information&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;how many &lt;strong&gt;retries&lt;/strong&gt; happened&lt;/li&gt;
&lt;li&gt;whether each command &lt;strong&gt;passed or failed&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can immediately see which commands took longer than expected, retried often, or didn’t execute at all. This is much more actionable than a static linter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it helps:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
When a test flops in CI but works locally, you can compare these metrics and see &lt;em&gt;what changed&lt;/em&gt;, instead of guessing.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⏱ Timing and latency observations
&lt;/h3&gt;

&lt;p&gt;The plugin highlights command durations — which can be telling when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;network responses are slow&lt;/li&gt;
&lt;li&gt;UI takes time to load&lt;/li&gt;
&lt;li&gt;intermittent server delays impact your tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it helps:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Instead of "this fails sometimes", you see "this command took 4× longer in CI than locally."&lt;/p&gt;

&lt;h3&gt;
  
  
  🔁 Retry tracking
&lt;/h3&gt;

&lt;p&gt;Cypress retries certain commands automatically, and you can now see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which commands retried&lt;/li&gt;
&lt;li&gt;how many times&lt;/li&gt;
&lt;li&gt;whether retries &lt;em&gt;eventually succeeded or still failed&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially useful for flaky assertions or slow elements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it helps:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You will stop writing defensive &lt;code&gt;cy.wait()&lt;/code&gt; hacks and start understanding &lt;em&gt;when and why&lt;/em&gt; Cypress is retrying under the hood.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚦 Clear pass/fail status per command
&lt;/h3&gt;

&lt;p&gt;Every command is annotated with its success/failure state in the audit output, giving you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;confidence this test truly passed&lt;/li&gt;
&lt;li&gt;insight into hidden failures that were masked by retries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it helps:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No more "it failed once but passed twice so I think it’s fine". You &lt;em&gt;actually see the results&lt;/em&gt;, not just the final green light.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔍 Comparing retries within the same test run
&lt;/h3&gt;

&lt;p&gt;And this is my personal favorite!&lt;/p&gt;

&lt;p&gt;One of the most underrated features is the ability to compare what happened between retries of the same test, especially when the first attempt fails and the retry somehow passes (ah yes… CI/CD, we meet again).  😅&lt;/p&gt;

&lt;p&gt;Instead of seeing a single green checkmark and moving on, the audit lets you inspect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what commands ran in the failed attempt&lt;/li&gt;
&lt;li&gt;what commands ran in the successful retry&lt;/li&gt;
&lt;li&gt;timing differences between both executions&lt;/li&gt;
&lt;li&gt;commands that retried more aggressively in one attempt than the other&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it helps:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Because "it passed on retry" is not a root cause.&lt;/p&gt;

&lt;p&gt;By comparing both attempts side by side, you can spot subtle timing shifts, delayed elements, or network-dependent behavior that only shows up under certain conditions, all without re-running the test or adding guesswork.&lt;/p&gt;

&lt;p&gt;This is where flaky tests stop being mysterious and start being &lt;em&gt;debuggable&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In short, &lt;strong&gt;cypress-flaky-test-audit&lt;/strong&gt; &lt;strong&gt;turns your test run into an observable timeline rather than a black box&lt;/strong&gt;. Instead of &lt;em&gt;hoping something is flaky&lt;/em&gt;, you get &lt;strong&gt;real data&lt;/strong&gt; on what Cypress did and where your timing assumptions broke down.&lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  How this actually helps you (and where you see the results)
&lt;/h2&gt;

&lt;p&gt;All this data would be pointless if it stayed hidden.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CYPRESS-FLAKY-TEST-AUDIT&lt;/strong&gt; is intentionally noisy in the &lt;em&gt;right&lt;/em&gt; places and visual where it matters, so you can consume the results in whatever way fits your workflow.&lt;/p&gt;

&lt;p&gt;Here is how.&lt;br&gt;
 &lt;/p&gt;

&lt;h3&gt;
  
  
  🖥 Browser console output
&lt;/h3&gt;

&lt;p&gt;During test execution, the plugin logs detailed audit information directly in the &lt;strong&gt;browser console&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;each Cypress command as it executes (in the precise order)&lt;/li&gt;
&lt;li&gt;timing and retry information&lt;/li&gt;
&lt;li&gt;which test retry you are currently looking at&lt;/li&gt;
&lt;li&gt;subtle differences between attempts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why this helps:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
When debugging locally, you don’t need to leave the test runner. You can watch the Dual-Verse unfold in real time.&lt;/p&gt;

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

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

&lt;h3&gt;
  
  
  🧾 Terminal output (CI-friendly)
&lt;/h3&gt;

&lt;p&gt;The same audit information is also available in the &lt;strong&gt;terminal output&lt;/strong&gt;, making it CI-friendly by default.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;no screenshots required&lt;/li&gt;
&lt;li&gt;no guesswork when a test flakes in CI&lt;/li&gt;
&lt;li&gt;actionable logs even when the test eventually passes on retry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why this helps:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Because flaky tests don’t stop being flaky just because the pipeline turned green.&lt;/p&gt;

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

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

&lt;h3&gt;
  
  
  📊 HTML test audit report (where everything clicks)
&lt;/h3&gt;

&lt;p&gt;This is where things really come together.&lt;/p&gt;

&lt;p&gt;The plugin generates an &lt;strong&gt;HTML audit report&lt;/strong&gt; that visualizes for a test suite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every test&lt;/li&gt;
&lt;li&gt;every retry&lt;/li&gt;
&lt;li&gt;every command&lt;/li&gt;
&lt;li&gt;execution timelines per attempt&lt;/li&gt;
&lt;li&gt;pass/fail status and duration&lt;/li&gt;
&lt;li&gt;what gets executed, and what is never reached&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each retry is displayed individually, and when a test fails first and passes on retry, you can &lt;strong&gt;compare both executions side by side&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;And yes, if this looks familiar, that is intentional.&lt;/p&gt;

&lt;p&gt;The graphs purposely resemble the ones used in &lt;a href="https://dev.to/sebastianclavijo/the-async-nature-of-cypress-dont-mess-with-the-timelines-in-your-cypress-tests-dual-verse-3ehh"&gt;The Async Nature of Cypress: Don’t Mess with the Timelines in Your Cypress Tests 'Dual-Verse'&lt;/a&gt;, because they are designed to expose exactly the same thing: &lt;strong&gt;&lt;em&gt;what Cypress did VS when you thought it did it&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this helps:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Seeing both timelines next to each other turns flakiness from a feeling into evidence. Timing gaps, delayed commands, and retry-heavy steps stop hiding behind a green checkmark.&lt;/p&gt;

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

&lt;p&gt;In short, the plugin does not just tell you &lt;em&gt;that&lt;/em&gt; a test retried. It shows you &lt;strong&gt;how each retry actually behaved&lt;/strong&gt;, across the browser, the terminal, and a visual report that makes the Dual-Verse impossible to ignore.&lt;/p&gt;

&lt;p&gt;You can interact with a fully functional HTML report here: &lt;a href="https://sclavijosuero.github.io/samples/flaky-demo.html" rel="noopener noreferrer"&gt;https://sclavijosuero.github.io/samples/flaky-demo.html&lt;/a&gt;&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Who this is for (and who it is not)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This is for you if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You maintain a medium-to-large Cypress test suite.&lt;/li&gt;
&lt;li&gt;You’ve seen tests fail in CI but magically pass locally.&lt;/li&gt;
&lt;li&gt;You’ve ever said "just re-run it" and felt a &lt;em&gt;tiny&lt;/em&gt; bit guilty.&lt;/li&gt;
&lt;li&gt;You want guardrails, not lectures, around Cypress async behavior.&lt;/li&gt;
&lt;li&gt;You’re a &lt;strong&gt;new QA engineer&lt;/strong&gt; getting started with Cypress &lt;em&gt;or&lt;/em&gt; a &lt;strong&gt;seasoned professional&lt;/strong&gt;, because we &lt;em&gt;all&lt;/em&gt; write flaky tests at some point, specially after we forget (again) about the Cypress &lt;strong&gt;'Dual-Verse' gotcha&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This is probably &lt;em&gt;not&lt;/em&gt; for you if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re just getting started with Cypress and only have a handful of tests.&lt;/li&gt;
&lt;li&gt;You’re intentionally ignoring Cypress’ command queue (no judgment… okay, maybe a little).&lt;/li&gt;
&lt;li&gt;Your tests are already rock-solid and boring in the best possible way.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  ACT3: RESOLUTION
&lt;/h1&gt;

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

&lt;p&gt;The goal here is not to shame your tests.&lt;br&gt;
We have all written code that works perfectly… &lt;strong&gt;until it doesn’t&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The real win is shifting from:&lt;br&gt;
"&lt;em&gt;Why is this flaky?&lt;/em&gt;"&lt;br&gt;
to&lt;br&gt;
"&lt;em&gt;We know exactly where flakiness comes from.&lt;/em&gt;"&lt;/p&gt;

&lt;p&gt;Once you internalize and fully understand the Cypress execution model and back it up with automated auditing, something nice happens:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tests become predictable&lt;br&gt;
CI becomes boring (the good kind)&lt;br&gt;
Failures start meaning something again&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is what I mean by thriving in the Cypress Dual-Verse.&lt;/p&gt;

&lt;p&gt;Not fighting Cypress.&lt;br&gt;
Not outsmarting it.&lt;br&gt;
Just respecting the timeline, and letting a tool point out when you don’t.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where to find it?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/cypress-flaky-test-audit" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/cypress-flaky-test-audit&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sclavijosuero/cypress-flaky-test-audit" rel="noopener noreferrer"&gt;https://github.com/sclavijosuero/cypress-flaky-test-audit&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;&lt;strong&gt;I'd love to hear from you! Please don't forget to follow me, leave a comment, or a reaction if you found this article useful or insightful.&lt;/strong&gt;&lt;/em&gt; ❤️ 🦄 🤯 🙌 🔥&lt;/p&gt;

&lt;p&gt;You can also connect with me on my new &lt;strong&gt;YouTube channel&lt;/strong&gt;: &lt;a href="https://www.youtube.com/@SebastianClavijoSuero" rel="noopener noreferrer"&gt;https://www.youtube.com/@SebastianClavijoSuero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to support my work, consider buying me a coffee or contributing to a training session, so I can keep learning and sharing cool stuff with all of you.&lt;br&gt;
Thank you for your support!&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/sclavijosuero" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cypress</category>
      <category>qa</category>
      <category>automation</category>
      <category>testing</category>
    </item>
    <item>
      <title>WICK-A11Y v2.3.0: A Dazzling New Report, WCAG 2.2 AAA, and More Goodies</title>
      <dc:creator>Sebastian Clavijo Suero</dc:creator>
      <pubDate>Mon, 08 Sep 2025 07:45:13 +0000</pubDate>
      <link>https://forem.com/sebastianclavijo/wick-a11y-v230-a-dazzling-new-report-wcag-22-aaa-and-more-goodies-hd0</link>
      <guid>https://forem.com/sebastianclavijo/wick-a11y-v230-a-dazzling-new-report-wcag-22-aaa-and-more-goodies-hd0</guid>
      <description>&lt;h1&gt;
  
  
  ACT 1: EXPOSITION
&lt;/h1&gt;

&lt;p&gt;Yes, it was time to make a major improvement to the cooler plugin at the Round Table: the wicked 'Mr. Wick-A11y'.&lt;/p&gt;

&lt;p&gt;So I’m not going to bore you with blah, blah, blah 📜📜📜,... let’s get straight to the point. 🎯&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT 2: CONFRONTATION
&lt;/h1&gt;

&lt;p&gt;There are a bunch of cool new features!&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Support of WCAG 2.2 AAA
&lt;/h2&gt;

&lt;p&gt;Yep, we've got the whole enchilada, enabling accessibility analyses all the way up to WCAG 2.2 AAA (whatever axe-core® supports).&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Image from &lt;a href="https://www.sweans.com" rel="noopener noreferrer"&gt;https://www.sweans.com&lt;/a&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Mega Revamp of the HTML Report
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Improved design and usability.&lt;/li&gt;
&lt;li&gt;Fully mobile-responsive.&lt;/li&gt;
&lt;li&gt;The report itself is WCAG 2.2 AAA compliant (or as close as possible).&lt;/li&gt;
&lt;li&gt;Full keyboard navigation (no mouse required). 🚫🐭&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Want proof?&lt;/p&gt;

&lt;p&gt;Okay, this is what the &lt;strong&gt;old report&lt;/strong&gt; looked like:&lt;/p&gt;

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

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

&lt;p&gt;And this is the design of the new, &lt;strong&gt;dazzling report&lt;/strong&gt;:&lt;/p&gt;

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

&lt;p&gt;Pretty sassy, eh? That’s a mood!&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Improved Reported Selectors
&lt;/h2&gt;

&lt;p&gt;Uses &lt;code&gt;data-cy&lt;/code&gt;, &lt;code&gt;data-testid&lt;/code&gt;, &lt;code&gt;data-test&lt;/code&gt;, &lt;code&gt;data-qa&lt;/code&gt;, and &lt;code&gt;data-test-id&lt;/code&gt; selectors for accessibility violations when available.&lt;/p&gt;

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

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

&lt;h2&gt;
  
  
  Cypress v15+
&lt;/h2&gt;

&lt;p&gt;Supercharged Cypress support! WICK-A11Y v2.2.0+ now fully embraces Cypress v15.0.0+ powerful new features and its improved Cypress runner.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Image from &lt;a href="https://www.cypress.io/blog" rel="noopener noreferrer"&gt;https://www.cypress.io/blog&lt;/a&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT3: RESOLUTION (wick-a11y 2.3.0)
&lt;/h1&gt;

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

&lt;p&gt;Final word from our friend Wick: WCAG 2.2 AAA locked, HTML revamp in place, and a report that seals the deal. The web just got upgraded. The web just got upgraded.&lt;/p&gt;

&lt;p&gt;You find it here &lt;a href="https://www.npmjs.com/package/wick-a11y" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/wick-a11y&lt;/a&gt;, and here &lt;a href="https://github.com/sclavijosuero/wick-a11y" rel="noopener noreferrer"&gt;https://github.com/sclavijosuero/wick-a11y&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Cheers, my QA friends!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;strong&gt;Come on, friend! Follow me, leave a comment, or leave a reaction. Please don’t be shy!&lt;/strong&gt;&lt;/em&gt; ❤️ 🦄 🤯 🙌 🔥&lt;/p&gt;

&lt;p&gt;Also on my new &lt;strong&gt;YouTube channel&lt;/strong&gt;: &lt;a href="https://www.youtube.com/@SebastianClavijoSuero" rel="noopener noreferrer"&gt;https://www.youtube.com/@SebastianClavijoSuero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to support my work? Buy me a coffee or chip in for a training, so I can keep learning and sharing cool stuff. Thanks for the support!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/sclavijosuero" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cypress</category>
      <category>qa</category>
      <category>a11y</category>
      <category>automation</category>
    </item>
    <item>
      <title>WICK-A11Y v2.2.0 x CYPRESS v15: Parabellum for Axe-ssibility Barriers</title>
      <dc:creator>Sebastian Clavijo Suero</dc:creator>
      <pubDate>Mon, 01 Sep 2025 00:52:46 +0000</pubDate>
      <link>https://forem.com/sebastianclavijo/wick-a11y-v220-x-cypress-v15-parabellum-for-axe-essibility-barriers-2fam</link>
      <guid>https://forem.com/sebastianclavijo/wick-a11y-v220-x-cypress-v15-parabellum-for-axe-essibility-barriers-2fam</guid>
      <description>&lt;p&gt;&lt;em&gt;(Cover image from pexels.com by Bruna Sonore)&lt;/em&gt;&lt;br&gt;
 &lt;/p&gt;
&lt;h1&gt;
  
  
  ACT 1: EXPOSITION
&lt;/h1&gt;

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

&lt;p&gt;It has been just over a year since the &lt;strong&gt;WICK-A11Y&lt;/strong&gt; open‑source accessibility plugin for Cypress saw the light, and became one of the most powerful and beloved tools in the Cypress community for fighting accessibility barriers in web apps.&lt;/p&gt;

&lt;p&gt;Since then, there have been 14 releases, keeping the plugin at the top of accessibility tooling for Cypress test frameworks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9v2rm61ohcxez5t82vg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9v2rm61ohcxez5t82vg.png" alt=" " width="800" height="569"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;(Image from pexels.com by icon0 com)&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;And now, the brand‑new &lt;strong&gt;wick‑a11y v2.2.0&lt;/strong&gt; is fully supported in &lt;strong&gt;Cypress v15&lt;/strong&gt;, &lt;em&gt;just 10 days&lt;/em&gt; after the new Cypress version was officially released.&lt;/p&gt;

&lt;p&gt;That’s because I saw this coming even before Cypress 15 dropped. I already knew two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The latest cypress‑axe v1.6.0 supported up to Cypress 14, so a new release of the library was likely imminent (they typically keep pace with the latest Cypress within days).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Cypress Runner UX was going to change, and to keep wick‑a11y’s voice‑feedback feature working, I would need to dive back into the Runner UI.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;As of Aug 30, 2025, Wick‑A11y v2.2.0 is live for Cypress v15.&lt;/strong&gt;&lt;br&gt;
 &lt;/p&gt;


&lt;h1&gt;
  
  
  ACT 2: CONFRONTATION
&lt;/h1&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  ✨ What's new?
&lt;/h2&gt;

&lt;p&gt;Basically 3 things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;All the existing wick-a11y capabilities are fully supported&lt;/strong&gt; in Cypress v15:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comprehensive Accessibility Analysis&lt;/li&gt;
&lt;li&gt;Easy to Use and Highly Configurable&lt;/li&gt;
&lt;li&gt;Summary of Violations&lt;/li&gt;
&lt;li&gt;Detailed Violation Information&lt;/li&gt;
&lt;li&gt;Custom Styling&lt;/li&gt;
&lt;li&gt;Interactive Console&lt;/li&gt;
&lt;li&gt;HTML Reports&lt;/li&gt;
&lt;li&gt;Terminal Reports&lt;/li&gt;
&lt;li&gt;Voice Feedback&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Support for cypress-axe v1.7.0&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bug fixes&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fixed an issue where violation screenshots could not be copied to the HTML report folder when test folders were deeply nested.&lt;/li&gt;
&lt;li&gt;Other minor fixes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  👀 What's look like?
&lt;/h2&gt;

&lt;p&gt;Still looks very cool, probably even cooler! 😎&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cypress Log and Interactive console&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;HTML Report&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Voice Feedback&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  🤔 How to use it?
&lt;/h2&gt;

&lt;p&gt;Just install the new wick-a11y v2.2.0 as a Dev Dependency in your Cypress v15 project (a new project or an existing one):&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  ⚙️ Compatibility?
&lt;/h2&gt;

&lt;p&gt;✔️ &lt;strong&gt;Wick‑a11y v2.2.0&lt;/strong&gt; is for &lt;strong&gt;Cypress v15+&lt;/strong&gt; projects, due to voice‑feedback dependencies on the new Cypress Runner UI.&lt;/p&gt;

&lt;p&gt;✔️ Use &lt;strong&gt;wick‑a11y v2.1.0&lt;/strong&gt; for &lt;strong&gt;Cypress v14.x&lt;/strong&gt;, which includes the latest patches and features for that line.&lt;/p&gt;

&lt;p&gt;✔️ And &lt;strong&gt;wick‑a11y v1.4.0&lt;/strong&gt; or earlier for &lt;strong&gt;Cypress v12.x &amp;amp; v13.x&lt;/strong&gt;.&lt;/p&gt;

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




&lt;h1&gt;
  
  
  ACT3: RESOLUTION
&lt;/h1&gt;

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

&lt;p&gt;Now you have no excuse not to incorporate accessibility analysis into your Cypress v15 projects, because &lt;strong&gt;wick‑a11y v2.2.0&lt;/strong&gt; is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;More amazing than a flaky test turning green on the first try.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cooler than John Wick in a tailored Kevlar suit tipping the Continental concierge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;More powerful than a High Table decree.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And kinder to screen readers than a perfectly labeled form, because that’s exactly what you’ll ship (or quite close).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where to find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/package/wick-a11y" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/wick-a11y&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Github&lt;/strong&gt;: &lt;a href="https://github.com/sclavijosuero/wick-a11y" rel="noopener noreferrer"&gt;https://github.com/sclavijosuero/wick-a11y&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Video Tutorial&lt;/strong&gt;: &lt;a href="https://www.youtube.com/watch?v=WpdrXU-6xzc" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=WpdrXU-6xzc&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⭐⭐⭐ Also, I want to give a special thanks to &lt;strong&gt;&lt;a href="https://www.linkedin.com/in/petermsouzajr/" rel="noopener noreferrer"&gt;Peter M Souza Jr&lt;/a&gt;&lt;/strong&gt; for his continuous contributions to keeping wick‑a11y robust and reliable.&lt;br&gt;
You rock, man! 🤘&lt;/p&gt;

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




&lt;p&gt;&lt;em&gt;&lt;strong&gt;I'd love to hear from you! Please don't forget to follow me, leave a comment, or a reaction if you found this article useful or insightful.&lt;/strong&gt;&lt;/em&gt; ❤️ 🦄 🤯 🙌 🔥&lt;/p&gt;

&lt;p&gt;You can also connect with me on my new &lt;strong&gt;YouTube channel&lt;/strong&gt;: &lt;a href="https://www.youtube.com/@SebastianClavijoSuero" rel="noopener noreferrer"&gt;https://www.youtube.com/@SebastianClavijoSuero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to support my work, consider buying me a coffee or contributing to a training session, so I can keep learning and sharing cool stuff with all of you.&lt;br&gt;
Thank you for your support!&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/sclavijosuero" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cypress</category>
      <category>qa</category>
      <category>a11y</category>
      <category>automation</category>
    </item>
    <item>
      <title>The Almighty PLAYWRIGHT-SCHEMA-VALIDATOR: The Unstoppable Triumvirate Playwright + AJV + Zod</title>
      <dc:creator>Sebastian Clavijo Suero</dc:creator>
      <pubDate>Mon, 04 Aug 2025 01:13:35 +0000</pubDate>
      <link>https://forem.com/sebastianclavijo/the-almighty-playwright-schema-validator-the-unstoppable-triumvirate-playwright-ajv-zod-3jno</link>
      <guid>https://forem.com/sebastianclavijo/the-almighty-playwright-schema-validator-the-unstoppable-triumvirate-playwright-ajv-zod-3jno</guid>
      <description>&lt;h1&gt;
  
  
  ACT 1: EXPOSITION
&lt;/h1&gt;

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

&lt;p&gt;It's here! At last, the invincible &lt;a href="https://github.com/sclavijosuero/playwright-schema-validator" rel="noopener noreferrer"&gt;PLAYWRIGHT-SCHEMA-VALIDATOR&lt;/a&gt; has arrived to impose the "Schema Peace" on any rebellious API that dares exist on this side of the Rubicon.&lt;/p&gt;

&lt;p&gt;And it’s here wielding two powerful weapons: the classic AJV gladius and the newly forged, sharp ZOD trident.&lt;/p&gt;

&lt;p&gt;But don’t be deceived, citizen! The PLAYWRIGHT-SCHEMA-VALIDATOR remains fully backward compatible with its venerable predecessor, the &lt;a href="https://github.com/sclavijosuero/playwright-ajv-schema-validator" rel="noopener noreferrer"&gt;PLAYWRIGHT-AJV-SCHEMA-VALIDATOR&lt;/a&gt;, and allies seamlessly with the &lt;a href="https://github.com/sclavijosuero/pw-api-plugin" rel="noopener noreferrer"&gt;PW-API-PLUGIN&lt;/a&gt; to deliver an even more glorious display of schema violations, worthy of the Playwright Senate itself!&lt;/p&gt;

&lt;p&gt;Let us behold how this almighty triumvirate can make your API testing as effortless and triumphant as a Roman victory parade.&lt;/p&gt;

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




&lt;h1&gt;
  
  
  ACT 2: CONFRONTATION
&lt;/h1&gt;

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

&lt;p&gt;Sorry, my QA friend, I just couldn't resist! I am captivated by the Roman era, both the early Republic and the later Empire, so I had to theme this blog around that glorious epoch when so many boundaries were shattered.&lt;/p&gt;

&lt;p&gt;And that is precisely what the PLAYWRIGHT-SCHEMA-VALIDATOR plugin sets out to do: shatter any wall that dares stand between you and flawless API validation.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  New Features Available
&lt;/h2&gt;

&lt;p&gt;With this new plugin, you will benefit from new features such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Full support for the AJV validator&lt;/strong&gt; (for plain JSON schemas, Swagger documents, and OpenAPI schemas) &lt;strong&gt;as well as the ZOD validator&lt;/strong&gt; (for Zod schemas).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistent display of schema validation errors&lt;/strong&gt;, regardless of whether you use AJV or ZOD.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ability to configure custom styles&lt;/strong&gt; to present schema violations according to your team’s preferences (including icons and HEX colors).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complete backward compatibility&lt;/strong&gt; and support for all existing features found in its predecessor, playwright-ajv-schema-validator.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  Complete List of Features
&lt;/h2&gt;

&lt;p&gt;But if you want to know the full list of features included in the playwright-schema-validator plugin, here they are:&lt;/p&gt;

&lt;p&gt;✔️ Function &lt;strong&gt;&lt;code&gt;validateSchema()&lt;/code&gt;&lt;/strong&gt; (and alias &lt;strong&gt;&lt;code&gt;validateSchemaAjv()&lt;/code&gt;&lt;/strong&gt;) performs a JSON Schema Validation and reports errors in the responses of network requests.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;⭐ Schemas are provided as JSON objects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;⭐ Supports Plain JSON schemas, OpenAPI 3.x schema documents and Swagger 2.0 schema documents.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;⭐ Utilizes the &lt;a href="https://github.com/sclavijosuero/core-ajv-schema-validator" rel="noopener noreferrer"&gt;&lt;em&gt;core-ajv-schema-validator&lt;/em&gt;&lt;/a&gt;, leveraging the Ajv JSON Schema Validator.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✔️ Fuction &lt;strong&gt;&lt;code&gt;cy.validateSchemaZod()&lt;/code&gt;&lt;/strong&gt; identifies and reports Zod schema validation errors in the responses of network requests.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;⭐ Schemas are provided as Zod objects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;⭐ Uses the &lt;a href="https://github.com/sclavijosuero/core-zod-schema-validator" rel="noopener noreferrer"&gt;&lt;em&gt;core-zod-schema-validator&lt;/em&gt;&lt;/a&gt;, leveraging the Zod Schema Validator.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✔️ The functions process the responses provided by Playwright API requests.&lt;/p&gt;

&lt;p&gt;✔️ Provides a user-friendly view of schema errors and mismatches between the validated data and the JSON schema, clearly highlighting where each validation error occurred and the exact reason for the mismatch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;⭐ Total number of schema errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;⭐ Full list of schema errors as provided by &lt;strong&gt;Ajv&lt;/strong&gt; or &lt;strong&gt;Zod&lt;/strong&gt; depending on the selected function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;⭐ A nested tree view of the validated data, clearly indicating the errors and where they occurred in an easy-to-understand format.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✔️ Allow custom styles (icons and text colors) to match the user's preferences for distinguishing schema errors.&lt;/p&gt;

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

&lt;p&gt;✔️ Presents results to the user in a consistent format, regardless of whether the AJV Schema Validator or ZOD Validator is used.&lt;/p&gt;

&lt;p&gt;✔️ Environment variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;⭐ &lt;code&gt;DISABLE_SCHEMA_VALIDATION&lt;/code&gt; to disable schema validation in your tests even when function &lt;code&gt;validateSchema()&lt;/code&gt; is present.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;⭐ &lt;code&gt;LOG_API_UI&lt;/code&gt; to enable the display of API call details in &lt;strong&gt;&lt;em&gt;Playwright UI&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;Trace Viewer&lt;/em&gt;&lt;/strong&gt; .&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;⭐ &lt;code&gt;LOG_API_REPORT&lt;/code&gt; to enable the display of API call details in &lt;strong&gt;&lt;em&gt;HTML Report&lt;/em&gt;&lt;/strong&gt; .&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;⭐ Integrates seamlessly with the &lt;strong&gt;&lt;code&gt;pw-api-plugin&lt;/code&gt;&lt;/strong&gt; but also works independently with &lt;strong&gt;standard Playwright API requests&lt;/strong&gt;.&lt;/p&gt;

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

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

&lt;h2&gt;
  
  
  How to Migrate
&lt;/h2&gt;

&lt;p&gt;To upgrade your Playwright framework from &lt;strong&gt;PLAYWRIGHT-AJV-SCHEMA-VALIDATOR&lt;/strong&gt; to the new &lt;strong&gt;PLAYWRIGHT-SCHEMA-VALIDATOR&lt;/strong&gt;, you only need to:&lt;/p&gt;

&lt;p&gt;1) Uninstall the previous plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm uninstall playwright-ajv-schema-validator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Install the new plugin:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;3) Replace previous imports in your project:&lt;/p&gt;

&lt;p&gt;Replace:&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;validateSchema&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-ajv-schema-validator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With:&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;validateSchema&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-schema-validator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No major refactoring required, just update your imports!&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You can continue using the &lt;code&gt;validateSchema()&lt;/code&gt; function with AJV schemas as before.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For AJV validation, you now also have the option to use the new &lt;code&gt;validateSchemaAjv()&lt;/code&gt; alias.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For ZOD schema validation, the plugin provides the new &lt;code&gt;validateSchemaZod()&lt;/code&gt; function.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

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

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

&lt;h4&gt;
  
  
  Example 1: &lt;code&gt;validateSchemaAjv()&lt;/code&gt; using Playwright standard API requests and overriding &lt;code&gt;issuesStyles&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Should validate schema AJV of POST "/store/order" for Playwright standard API and custom Styles override&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;issuesStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;iconPropertyError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🟦&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;colorPropertyError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#5178eb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;iconPropertyMissing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🟪&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;colorPropertyMissing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#800080&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;requestBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;petId&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quantity&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shipDate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2024-01-01T00:57:29.231Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;placed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responsePost&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;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://petstore.swagger.io/v2/store/order`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json; charset=UTF-8&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responsePost&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="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseBodyPost&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;responsePost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;validateSchemaAjv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nx"&gt;responseBodyPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;petStoreSwaggerErrors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/store/order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nx"&gt;issuesStyles&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h4&gt;
  
  
  Example 2: &lt;code&gt;validateSchemaAjv()&lt;/code&gt; using &lt;code&gt;pw-api-plugin&lt;/code&gt; with &lt;code&gt;pwApi&lt;/code&gt; class
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Should validate schema of POST "/store/order" for pw-api-plugin and pwApi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;petId&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quantity&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shipDate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2024-01-01T00:57:29.231Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;placed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responsePost&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;pwApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&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="s2"&gt;`https://petstore.swagger.io/v2/store/order`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json; charset=UTF-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responsePost&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="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseBodyPost&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;responsePost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;validateSchemaAjv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nx"&gt;responseBodyPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;petStoreSwaggerErrors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/store/order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h4&gt;
  
  
  Example 3: &lt;code&gt;validateSchemaZod()&lt;/code&gt; using &lt;code&gt;pw-api-plugin&lt;/code&gt; with &lt;code&gt;axiosApi&lt;/code&gt; class
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Should validate schema ZOD of GET "/pet/findByStatusr" for pw-api-plugin and axiosApi and overriding `issuesStyles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseGet&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;axiosApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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="s2"&gt;`https://petstore.swagger.io/v2/pet/findByStatus?status=pending`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;findByStatusReq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;findByStatusReq&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="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseFindByStatus&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;findByStatusReq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;validateSchemaZod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nx"&gt;responseBodyGet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;petStoreSwaggerErrors&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  PLAYWRIGHT-SCHEMA-VALIDATOR: Just One Marvel in the Joyful Empire of the Wicked Schema Validators
&lt;/h3&gt;

&lt;p&gt;If you want to perform schema validations not only in Playwright projects but also in any other Node projects, or even in Cypress, I have you covered!&lt;/p&gt;

&lt;p&gt;Over time, I have created the Wicked Schema Validators ecosystem.&lt;/p&gt;

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

&lt;p&gt;With this, you have all the weapons you need to conquer any API testing battlefield!&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT3: RESOLUTION
&lt;/h1&gt;

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

&lt;p&gt;Migrating to PLAYWRIGHT-SCHEMA-VALIDATOR is so effortless that, if you’re doing API testing in your Playwright framework, there’s really no excuse not to take advantage of this powerful plugin. 😅&lt;/p&gt;

&lt;p&gt;Simply cross the Rubicon, and the empire of seamless API testing will be yours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sclavijosuero/playwright-schema-validator" rel="noopener noreferrer"&gt;https://github.com/sclavijosuero/playwright-schema-validator&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/playwright-schema-validator" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/playwright-schema-validator&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

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




&lt;p&gt;&lt;em&gt;&lt;strong&gt;I'd love to hear from you! Please don't forget to follow me, leave a comment, or a reaction if you found this article useful or insightful.&lt;/strong&gt;&lt;/em&gt; ❤️ 🦄 🤯 🙌 🔥&lt;/p&gt;

&lt;p&gt;You can also connect with me on my new &lt;strong&gt;YouTube channel&lt;/strong&gt;: &lt;a href="https://www.youtube.com/@SebastianClavijoSuero" rel="noopener noreferrer"&gt;https://www.youtube.com/@SebastianClavijoSuero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to support my work, consider buying me a coffee or contributing to a training session, so I can keep learning and sharing cool stuff with all of you.&lt;br&gt;
Thank you for your support!&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/sclavijosuero" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>qa</category>
      <category>apitesting</category>
      <category>schemavalidator</category>
    </item>
    <item>
      <title>The 'second' most abused (and misused) Cypress command ever: cy.contains()</title>
      <dc:creator>Sebastian Clavijo Suero</dc:creator>
      <pubDate>Sat, 21 Jun 2025 06:02:14 +0000</pubDate>
      <link>https://forem.com/sebastianclavijo/the-second-most-abused-and-misused-cypress-command-ever-cycontains-126j</link>
      <guid>https://forem.com/sebastianclavijo/the-second-most-abused-and-misused-cypress-command-ever-cycontains-126j</guid>
      <description>&lt;p&gt;&lt;em&gt;(Cover image from pexels.com by Levi Damasceno)&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ACT 1: EXPOSITION&lt;/li&gt;
&lt;li&gt;
ACT 2: CONFRONTATION

&lt;ul&gt;
&lt;li&gt;
What is &lt;code&gt;.contains()&lt;/code&gt;?

&lt;ul&gt;
&lt;li&gt;It has a Dual Nature&lt;/li&gt;
&lt;li&gt;It is a Cypress query&lt;/li&gt;
&lt;li&gt;Some Overlooked Options&lt;/li&gt;
&lt;li&gt;Selector Support&lt;/li&gt;
&lt;li&gt;Contains with &lt;code&gt;input[type="submit"]&lt;/code&gt; Element&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

12+1 Misuses of &lt;code&gt;.contains()&lt;/code&gt; – some might even call it abuse

&lt;ul&gt;
&lt;li&gt;1. Unnecessary Use of &lt;code&gt;.should('exist')&lt;/code&gt; Assertion&lt;/li&gt;
&lt;li&gt;2. Misinterpreting Partial Matching&lt;/li&gt;
&lt;li&gt;3. Ignoring Whitespace Issues&lt;/li&gt;
&lt;li&gt;4. Ignoring Case-sensitivity Issues&lt;/li&gt;
&lt;li&gt;5. Assuming &lt;code&gt;.contains()&lt;/code&gt; Returns All Matching Elements&lt;/li&gt;
&lt;li&gt;6. Using it for Too Broad Element Searches (VERY IMPORTANT!)&lt;/li&gt;
&lt;li&gt;7. Relying on &lt;code&gt;.contains()&lt;/code&gt; for Only Visible Elements&lt;/li&gt;
&lt;li&gt;8. Relying on &lt;code&gt;cy.contains&lt;/code&gt; Instead of Declarative Selectors&lt;/li&gt;
&lt;li&gt;9. Using &lt;code&gt;.contains()&lt;/code&gt; Against Non-Readable or Elements&lt;/li&gt;
&lt;li&gt;10. Misuse of &lt;code&gt;.contains()&lt;/code&gt; in Assertions for Non-Text Elements&lt;/li&gt;
&lt;li&gt;11. Misunderstanding &lt;code&gt;.contains()&lt;/code&gt; with Dynamic Content&lt;/li&gt;
&lt;li&gt;12. Misuse of &lt;code&gt;.contains()&lt;/code&gt; Accessing a Shadow DOM Element&lt;/li&gt;
&lt;li&gt;12+1. Chaining Incorrectly Multiple &lt;code&gt;.contains()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Diferences: &lt;code&gt;.contains()&lt;/code&gt; vs &lt;code&gt;.should('contains.text')&lt;/code&gt; vs &lt;code&gt;.should('have.text')&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;Preference Order When Looking for Elements&lt;/li&gt;

&lt;li&gt;New: &lt;code&gt;.findOne()&lt;/code&gt; in Gleb Bahmutov's cypress-map Plugin&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;ACT3: RESOLUTION&lt;/li&gt;

&lt;/ul&gt;




&lt;h1&gt;
  
  
  ACT 1: EXPOSITION
&lt;/h1&gt;

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

&lt;p&gt;About a year ago, I wrote an article about how &lt;a href="https://dev.to/sebastianclavijo/the-most-abused-cypress-command-ever-cywaittime-15e0"&gt;&lt;code&gt;cy.wait(TIME)&lt;/code&gt; is the most abused Cypress command ever&lt;/a&gt;, and it quickly sparked an interesting conversation among my fellow QAs.&lt;/p&gt;

&lt;p&gt;When I asked which other Cypress command was excessively used but equally misunderstood or misused, the response was quite unanimous: &lt;code&gt;cy.contains()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While I still firmly believe &lt;code&gt;cy.wait(TIME)&lt;/code&gt; is the reigning champion of infamous commands in Cypress, I must admit that &lt;code&gt;cy.contains()&lt;/code&gt; earns a solid silver medal.&lt;/p&gt;

&lt;p&gt;Do not get me wrong, &lt;code&gt;cy.contains()&lt;/code&gt; is one of my favorite and most powerful commands in the Cypress arsenal. But much like &lt;em&gt;John Wick&lt;/em&gt;, it’s a double edged weapon, capable of incredible precision, yet risky if not handled correctly. This misuse frequently results in failing or flaky tests, making its misuse both widespread and often tricky to catch.&lt;/p&gt;

&lt;p&gt;So yes, &lt;code&gt;cy.contains()&lt;/code&gt; (amazing but often poorly executed), deserves, in my opinion, its spot as runner-up in the "most abused/misused Cypress commands" debate!&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT 2: CONFRONTATION
&lt;/h1&gt;

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

&lt;h2&gt;
  
  
  What is &lt;code&gt;.contains()&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;According to the &lt;a href="https://docs.cypress.io/api/commands/contains" rel="noopener noreferrer"&gt;official Cypress documentation&lt;/a&gt;, it is used to &lt;em&gt;get the DOM element containing the text. DOM elements can contain more than the desired text and still match&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In other words, it yields &lt;strong&gt;one DOM element&lt;/strong&gt; that contains the passed &lt;strong&gt;substring&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  It has a Dual Nature
&lt;/h3&gt;

&lt;p&gt;It has a dual nature because it can start a new Cypress chain (searching the entire document for the content):&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;// Start a chain (search entire document)&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;`
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or use an existing chain (querying inside the DOM tree of the specified yielded element):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Use an existing chain (search restricted within element with id 'list')&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my text&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;
  
  
  It is a Cypress query
&lt;/h3&gt;

&lt;p&gt;You must know that &lt;code&gt;.contains()&lt;/code&gt; is a &lt;strong&gt;Cypress query&lt;/strong&gt; that retries until all the chained assertions pass and the element exists in the DOM or until the timeout expires according to the &lt;code&gt;defaultCommandTimeout&lt;/code&gt; in &lt;code&gt;cypress.config.js&lt;/code&gt;. But this can be overridden with the &lt;code&gt;timeout&lt;/code&gt; option.&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;// Timeout override in millisecs&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;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="s2"&gt;`
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;.contains()&lt;/code&gt; is a Cypress query, it not only retrieves the element matching the text but also performs an implicit assertion that the element must exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Some Overlooked Options
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;.contains()&lt;/code&gt; supports the &lt;code&gt;log&lt;/code&gt; and &lt;code&gt;timeout&lt;/code&gt; options, like many other Cypress commands and queries, but there are a couple of very important options that are often overlooked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;matchCase&lt;/code&gt;&lt;/strong&gt;: To check case sensitivity. By default, it is &lt;code&gt;true&lt;/code&gt;, meaning the match is case-sensitive.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// It will also find "My TEXT"&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;matchCase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="s2"&gt;` 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;includeShadowDom&lt;/code&gt;&lt;/strong&gt;: To include in the match elements within the shadow DOM. With this option you can directly target elements within the Shadow DOM without using the &lt;code&gt;.shadow()&lt;/code&gt; command explicitly. By default, it uses the &lt;code&gt;includeShadowDom&lt;/code&gt; option in the &lt;code&gt;cypress.config.js&lt;/code&gt;, or &lt;code&gt;false&lt;/code&gt; if not specified.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Includes elements inside Shadow DOM.&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;includeShadowDom&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="s2"&gt;` 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: A shadow DOM element is a hidden subtree attached to a regular DOM element, isolating its HTML and CSS from the main document to prevent conflicts and enable reusable, self-contained web components.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I would like you to try to remember these two options &lt;code&gt;matchCase&lt;/code&gt; and &lt;code&gt;includeShadowDom&lt;/code&gt;, as they’re going to come up again later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Selector Support
&lt;/h3&gt;

&lt;p&gt;An important thing to know about &lt;code&gt;.contains()&lt;/code&gt; is that you can provide a &lt;strong&gt;selector&lt;/strong&gt; to narrow and accelerate the search. When a selector is provided, it will return the DOM element that matches the selector, and either itself or one of its children matches the specified text.&lt;/p&gt;

&lt;p&gt;For example, for this HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Enter Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter your password"&lt;/span&gt;
         &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we execute a &lt;code&gt;.contains()&lt;/code&gt; on the document using the text "Password":&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;// Obtains the &amp;lt;label&amp;gt; element that contains "Password" as text&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, if we execute a &lt;code&gt;.contains()&lt;/code&gt; using the HTML tag &lt;code&gt;form&lt;/code&gt; as the selector and the text "Password":&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;// Obtains the &amp;lt;form&amp;gt; element that contains "Password" in its DOM tree&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&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;blockquote&gt;
&lt;p&gt;⚠️ Notice that, in the last case, although the element with the text "Password" is the &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;, &lt;code&gt;.contains()&lt;/code&gt; will return the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element because it is the provided selector, and it contains a child with the text "Password" in its DOM tree.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Contains with &lt;code&gt;input[type="submit"]&lt;/code&gt; Element
&lt;/h3&gt;

&lt;p&gt;There is an interesting case with &lt;code&gt;.contains()&lt;/code&gt; where it can find an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element with the attribute &lt;code&gt;type="submit"&lt;/code&gt; and the attribute &lt;code&gt;value&lt;/code&gt; contains the provided text.&lt;/p&gt;

&lt;p&gt;For example, with the following HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"main"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
    ...
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Send"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will execute successfully:&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;// It will find the &amp;lt;input&amp;gt; element of type submit with text "Send"&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Send&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;blockquote&gt;
&lt;p&gt;This is because &lt;code&gt;input[type='submit']&lt;/code&gt; is an element that behaves like a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, with its text in the &lt;code&gt;value&lt;/code&gt; attribute. Therefore, &lt;code&gt;.contains(&lt;/code&gt;) also includes this specific type of element in the search.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Ok, now that we understand the theory, using &lt;code&gt;.contains()&lt;/code&gt; in our test shouldn’t be too hard, right? 😎&lt;/p&gt;

&lt;p&gt;Well, most likely not.&lt;/p&gt;

&lt;p&gt;But what if I told you that this mighty command is, unfortunately, &lt;strong&gt;often misused&lt;/strong&gt; in Cypress tests, making them &lt;strong&gt;flakily unpredictable&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Don’t believe me? 🩳🔥&lt;br&gt;
Alright, let me explain!&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  12+1 Misuses of &lt;code&gt;.contains()&lt;/code&gt; – some might even call it abuse
&lt;/h2&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  1. Unnecessary Use of &lt;code&gt;.should('exist')&lt;/code&gt; Assertion
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Misuse&lt;/strong&gt;&lt;br&gt;
Unnecessarily chaining the assertion &lt;code&gt;.should('exist')&lt;/code&gt; after &lt;code&gt;.contains()&lt;/code&gt; to ensure the element is present in the DOM.&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;// Chaining `.should('exist')` after `.contains()` is unnecessary&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Better Practice&lt;/strong&gt;&lt;br&gt;
Do not include &lt;code&gt;.should('exist')&lt;/code&gt;, as &lt;code&gt;.contains()&lt;/code&gt; is a Cypress query (like &lt;code&gt;.get()&lt;/code&gt;) and automatically waits for the element to exist and be in an actionable state.&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;// Directly use `.contains()`&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  2. Misinterpreting Partial Matching
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Misuse&lt;/strong&gt;&lt;br&gt;
Users may not realize &lt;code&gt;cy.contains()&lt;/code&gt; performs partial text matching by default, leading to unintended behavior.&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;// Matches both "Add to Cart" and "Address".&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Add&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Better Practice&lt;/strong&gt;&lt;br&gt;
If you want an exact match, you can use a regular expression with special characters: start-of-string &lt;code&gt;^&lt;/code&gt; and end-of-string &lt;code&gt;$&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Ensures it matches the whole word 'Add'&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^Add$/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to find an element that contains an exact text match, and part of the text comes from a dynamic variable, you can use &lt;code&gt;new RegExp()&lt;/code&gt; to handle the dynamic value:&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;// Exact match for "John Wick"&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;who&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Wick&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`^John &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;who&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  3. Ignoring Whitespace Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Misuse&lt;/strong&gt;&lt;br&gt;
Passing text that doesn't account for formatting, such as extra spaces and newlines.&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;// Fails due to extra spaces after the word "Cart"&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Add to Cart   &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Better Practice&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trim or format the text properly.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Avoid trailing/leading spaces&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; Add to Cart  &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Or better yet, be precise in matching.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Precise matching&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Add to Cart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  4. Ignoring Case-sensitivity Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Misuse&lt;/strong&gt;&lt;br&gt;
Passing text that doesn't account for casing issues. By default &lt;code&gt;.contains()&lt;/code&gt; is &lt;strong&gt;&lt;em&gt;case-sensitive&lt;/em&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Only finds an element with the exact text "add to cart" in all lowercase&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add to cart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Better Practice&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to find an element with the text "add to cart" regardless of its letter casing, you can use &lt;code&gt;matchCase&lt;/code&gt; option.&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;// Enables case-insensitive matching&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add to cart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;matchCase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could also write a regular expression to achieve the same goal of case-insensitive matching:&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;// Also enables case-insensitive matching&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/add to cart/i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  5. Assuming &lt;code&gt;.contains()&lt;/code&gt; Returns All Matching Elements
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Misuse&lt;/strong&gt;&lt;br&gt;
Beginners often assume that &lt;code&gt;.contains()&lt;/code&gt; retrieves all the elements that match the specified text, whereas it actually only selects the first matching element in the DOM.&lt;/p&gt;

&lt;p&gt;For example, if your DOM contains multiple matching elements with the word "Enter":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Enter Username&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Enter Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then this command will find only the first &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element matching the text:&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;// Will find and return only the first &amp;lt;div&amp;gt; that contains "Enter",&lt;/span&gt;
&lt;span class="c1"&gt;// and that would be &amp;lt;label&amp;gt;Enter Username&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Better Practice&lt;/strong&gt;&lt;br&gt;
To get all elements that contains the substring you can use &lt;code&gt;cy.get()&lt;/code&gt; with &lt;code&gt;:contains()&lt;/code&gt; jquery selector extension.&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;// Retrieves all elements in the DOM that contain the text "Enter"&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:contains("Enter")&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  6. Using it for Too Broad Element Searches (VERY IMPORTANT!)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Misuse&lt;/strong&gt;&lt;br&gt;
Relying on &lt;code&gt;cy.contains()&lt;/code&gt; without narrowing the context can cause the command to match unintended elements.&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;// Consider any element including text "Password" on the DOM&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there are multiple elements with the text "Password" on the page, this &lt;strong&gt;&lt;em&gt;can lead&lt;/em&gt;&lt;/strong&gt; to selecting the wrong element.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better Practice&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's consider the following HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Enter Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter your password"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Approach A&lt;/em&gt;&lt;/strong&gt;: Use a parent element or container to scope the search.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Scoped to &amp;lt;form&amp;gt; or children elements within the &amp;lt;form&amp;gt; that match the text&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contains&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="c1"&gt;// Yields the &amp;lt;label&amp;gt; element&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;cy.get('form')&lt;/code&gt; selects all &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; elements in the DOM.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Within these &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; elements, &lt;code&gt;cy.contains('Password')&lt;/code&gt; finds a child element matching the text "Password" (in this case, the &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;This approach is useful for constraining the scope of searchable elements before filtering by text.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Approach B&lt;/em&gt;&lt;/strong&gt;: Leverage &lt;code&gt;.contains()&lt;/code&gt; with a selector.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Scope only to &amp;lt;form&amp;gt; elements that match the provided text itself or&lt;/span&gt;
&lt;span class="c1"&gt;// within one of their children&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form&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;Password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Yields the &amp;lt;form&amp;gt; element&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this other case:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Finds the first &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element anywhere in the whole DOM that contains the text "Password" within its DOM tree.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;This is very useful for a quick search if you are looking for any &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; in the DOM that contains an element matching the text.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;h3&gt;
  
  
  7. Relying on &lt;code&gt;.contains()&lt;/code&gt; for Only Visible Elements
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Misuse&lt;/strong&gt;&lt;br&gt;
Assuming that &lt;code&gt;.contains()&lt;/code&gt; will only locate visible elements while ignoring hidden ones.&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;// The element containing 'Hidden Text' will be found even if the text is hidden&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hidden Text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beginners may mistakenly believe that &lt;code&gt;.contains()&lt;/code&gt; will locate only elements that are visible. However, it will return the first element in the DOM that contains the specified text, regardless of its visibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better Practice&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To ensure you're working only with visible elements, you can use the &lt;strong&gt;&lt;code&gt;:visible&lt;/code&gt; &lt;em&gt;pseudo-selector&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Directly as part of a CSS selector:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example 1.&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;// It will return a visible &amp;lt;div&amp;gt; element that contains the text "Username"&lt;/span&gt;
&lt;span class="c1"&gt;// or a child element within a visible &amp;lt;div&amp;gt; that contains the text "Username"&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div:visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contains&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example 2.&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;// It will return a &amp;lt;div&amp;gt; element that contains the text "Username"&lt;/span&gt;
&lt;span class="c1"&gt;// or a &amp;lt;div&amp;gt; element whose child contains the text "Username"&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div:visible&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;Username&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;blockquote&gt;
&lt;p&gt;⚠️ Important: Note the nuance! Both might look similar, but they are slightly different and could yield completely different elements or even pass or fail the implicit assertion depending on the DOM tree under the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Used in a &lt;code&gt;.filter()&lt;/code&gt; command:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// It will return a visible &amp;lt;div&amp;gt; element that contains the text "Username"&lt;/span&gt;
&lt;span class="c1"&gt;// or a child element within a visible &amp;lt;div&amp;gt; that contains the text "Username"&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contains&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  8. Relying on &lt;code&gt;cy.contains&lt;/code&gt; Instead of Declarative Selectors
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Misuse&lt;/strong&gt;&lt;br&gt;
Overusing &lt;code&gt;.contains()&lt;/code&gt; instead of targeting elements by data attributes or selectors leads to brittle tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// It breaks if the text changes to something like "Remove"&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Delete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Better Practice&lt;/strong&gt;&lt;br&gt;
Utilize data attributes or stable semantic selectors.&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;// Robust and still readable (clearly we are targeting the 'delete' button)&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-cy="delete-btn"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  9. Using &lt;code&gt;.contains()&lt;/code&gt; Against Non-Readable or Elements
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Misuse&lt;/strong&gt;&lt;br&gt;
Attempting to use &lt;code&gt;.contains()&lt;/code&gt; with non-readable elements, such as &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If your DOM includes an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&lt;/span&gt; 
       &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter your username"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the user enters the text "John Wick" into the input field:&lt;/p&gt;

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

&lt;p&gt;A &lt;code&gt;.contains()&lt;/code&gt; command searching for "John Wick" in the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element will not find 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="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&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;John Wick&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// This will fail&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Better Practice&lt;/strong&gt;&lt;br&gt;
Use &lt;code&gt;cy.get()&lt;/code&gt; for non-readable elements like &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;have.value&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;John Wick&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// This will pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  10. Misuse of &lt;code&gt;.contains()&lt;/code&gt; in Assertions for Non-Text Elements
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Misuse&lt;/strong&gt;&lt;br&gt;
Using &lt;code&gt;.contains()&lt;/code&gt; to locate structural attributes (like &lt;code&gt;value&lt;/code&gt;, &lt;code&gt;alt&lt;/code&gt; or &lt;code&gt;placeholder&lt;/code&gt;) instead of visible text.&lt;/p&gt;

&lt;p&gt;If your DOM includes an &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"submit-btn"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Submit Button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following will not work:&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 will fail because "Submit Button" is an alt attribute, not a visible text&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Submit Button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Better Practice&lt;/strong&gt;&lt;br&gt;
Use &lt;code&gt;cy.get()&lt;/code&gt; for non-standard-text elements, like &lt;code&gt;&amp;lt;alt&amp;gt;&lt;/code&gt; attribute.&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;have.attr&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;alt&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;Submit Button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  11. Misunderstanding &lt;code&gt;.contains()&lt;/code&gt; with Dynamic Content
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Misuse&lt;/strong&gt;&lt;br&gt;
Failing to account for dynamically loaded content, causing intermittent errors.&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 might timeout if the assertion is made prematurely&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Loading...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not.exist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Better Practice&lt;/strong&gt;&lt;br&gt;
Chain &lt;code&gt;.contains()&lt;/code&gt; with &lt;code&gt;cy.wait()&lt;/code&gt; or &lt;code&gt;cy.intercept()&lt;/code&gt; to handle async loading.&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;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;intercept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&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;/api/items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getItems&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@getItems&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Loaded Content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  12. Misuse of &lt;code&gt;.contains()&lt;/code&gt; Accessing a Shadow DOM Element
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Misuse&lt;/strong&gt;&lt;br&gt;
If you attempt to access a Shadow DOM element while testing a standard DOM page without explicitly indicating to Cypress to account for shadow elements, it will not locate them.&lt;/p&gt;

&lt;p&gt;A Shadow DOM element is part of a hidden DOM tree attached to a standard DOM element (known as the shadow host). This feature allows the encapsulation of styles and scripts, preventing them from influencing or being influenced by the rest of the document.&lt;/p&gt;

&lt;p&gt;For example, consider this HTML with a Shadow DOM component that includes a button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;...
  &lt;span class="nt"&gt;&amp;lt;my-shadow-component&amp;gt;&lt;/span&gt;
    #shadow-root
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click Me&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/my-shadow-component&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we try to locate the  with text "Click Me" using .contains():&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 will not work because the button is inside a Shadow DOM&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Click Me&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Better Practice&lt;/strong&gt;&lt;br&gt;
Use the &lt;code&gt;includeShadowDom&lt;/code&gt; option set to &lt;code&gt;true&lt;/code&gt; within &lt;code&gt;.contains()&lt;/code&gt; to automatically traverse Shadow DOM boundaries.&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 will locate and interact with the &amp;lt;button&amp;gt; element inside the Shadow DOM&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Click Me&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;includeShadowDom&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="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to include Shadow DOM elements in all searches across your project, you can configure it globally in the &lt;code&gt;cypress.config.js&lt;/code&gt; file by setting the &lt;code&gt;includeShadowDom&lt;/code&gt; option to &lt;code&gt;true&lt;/code&gt;. This way, you don’t need to include it in every &lt;code&gt;.contains()&lt;/code&gt; call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cypress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// Other configuration options...&lt;/span&gt;
  &lt;span class="na"&gt;includeShadowDom&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="c1"&gt;// Additional options...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you can use the .shadow() command to traverse shadow DOM boundaries:&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 will locate and interact with the &amp;lt;button&amp;gt; element inside the Shadow DOM&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-shadow-component&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="nf"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Click Me&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="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  12+1. Chaining Incorrectly Multiple &lt;code&gt;.contains()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Misuse&lt;/strong&gt;&lt;br&gt;
Sometimes, users do not realize that when performing a &lt;code&gt;.contains()&lt;/code&gt; query, the yielded element may have changed. If you chain a second &lt;code&gt;.contains()&lt;/code&gt;, the subsequent query will be applied to the element returned by the first &lt;code&gt;.contains()&lt;/code&gt; and not to the original search context.&lt;/p&gt;

&lt;p&gt;If we have this html:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;...
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"#container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;User&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter your username"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter your password"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The chained contains() will fail the test:&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;// The second contains will fail&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contains&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why is that?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first &lt;code&gt;.contains('User')&lt;/code&gt; will locate the &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element with the text "User".&lt;/li&gt;
&lt;li&gt;The second &lt;code&gt;.contains('Password')&lt;/code&gt; will be applied to the element returned by the first &lt;code&gt;.contains()&lt;/code&gt;, which is a &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element, and not the original element yielded by &lt;code&gt;cy.get('#container')&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;As a result, the second &lt;code&gt;.contains()&lt;/code&gt; query will fail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Better Practice&lt;/strong&gt;&lt;br&gt;
Avoid chaining &lt;code&gt;.contains()&lt;/code&gt; unless you are absolutely sure that the subsequent &lt;code&gt;.contains()&lt;/code&gt; query will operate within the DOM tree of the element yielded by the first &lt;code&gt;.contains(&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This test will pass&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contains&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  Diferences: &lt;code&gt;.contains()&lt;/code&gt; vs &lt;code&gt;.should('contains.text')&lt;/code&gt; vs &lt;code&gt;.should('have.text')&lt;/code&gt;
&lt;/h2&gt;

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

&lt;p&gt;Something I have noticed during my time working with Cypress is that quite a number of people, especially newer users, confuse the differences between &lt;code&gt;.contains()&lt;/code&gt;, &lt;code&gt;.should('contains')&lt;/code&gt;, and &lt;code&gt;.should('have.text')&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;While they may seem quite similar at first glance, each has its own specific purpose and behavior that can significantly impact your test cases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.contains('a_text')&lt;/code&gt;&lt;/strong&gt;: Cypress query used to locate elements containing specific text within them. It searches the entire or part of the DOM (and optionally shadow DOM) to find elements with either partial or exact text. It’s primarily a selector.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Finds any element that contains the exact text "Submit"&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.should('contains.text', 'a_text')&lt;/code&gt;&lt;/strong&gt;: Assertion that checks if an element’s text includes a specific substring. It is not used to locate elements, but rather to validate that a particular element has text that contains the expected value. Other alias: &lt;code&gt;contain.text&lt;/code&gt;, &lt;code&gt;includes.text&lt;/code&gt;, and &lt;code&gt;include.text&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Asserts the element with class .greeting contains "Welcome" within its text&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.greeting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contains.text&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;Welcome&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;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.should('have.text', 'a_text')&lt;/code&gt;&lt;/strong&gt;: Stricter assertion used to validate that an element’s text exactly matches the expected value.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Asserts the text of the element with class .greeting is exactly "Welcome User"&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.greeting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;have.text&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;Welcome User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

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

&lt;h2&gt;
  
  
  Preference Order When Looking for Elements
&lt;/h2&gt;

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

&lt;p&gt;Cypress gives top priority to a selector when it is explicitly provided in the &lt;code&gt;.contains()&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;If no selector is specified, Cypress defaults to selecting elements higher in the DOM tree when they are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;input[type='submit']&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means that if the element containing the text is a &lt;span&gt; inside a  element, &lt;code&gt;.contains()&lt;/code&gt; will yield the &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element instead of the &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;For example, in the following HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;...
&lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;User&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"user"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we search for the element that contains the text "User":&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;// Yields the &amp;lt;label&amp;gt;, not the &amp;lt;span&amp;gt;&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  New: &lt;code&gt;.findOne()&lt;/code&gt; in Gleb Bahmutov's cypress-map Plugin
&lt;/h2&gt;

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

&lt;p&gt;One week prior to when this article was written, &lt;a href="https://www.linkedin.com/in/bahmutov/" rel="noopener noreferrer"&gt;Gleb Bahmutov&lt;/a&gt; introduced a new Cypress query &lt;code&gt;.findOne()&lt;/code&gt; to his &lt;a href="https://github.com/bahmutov/cypress-map" rel="noopener noreferrer"&gt;cypress-map&lt;/a&gt; plugin.&lt;/p&gt;

&lt;p&gt;This new query finds one DOM element that &lt;strong&gt;exactly matches&lt;/strong&gt; the provided text.&lt;/p&gt;

&lt;p&gt;For the following HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;...
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tools"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Mallet&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Power Hammer&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Hammer&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Sledgehammer&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.findOne()&lt;/code&gt; command for the text "Hammer":&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;// It will yield &amp;lt;label&amp;gt;Hammer&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#tools&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hammer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would be equivalent to using a regular expression for an exact match in a &lt;code&gt;.contains()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// It will yield &amp;lt;label&amp;gt;Hammer&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#tools&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^Hammer$/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The advantage of this command &lt;code&gt;.findOne()&lt;/code&gt; is that it makes it much easier to find an element when the text is dynamic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Power&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// It will yield &amp;lt;label&amp;gt;Power Hammer&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#tools&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;findOne&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;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Hammer`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And no need to use the cumbersome RegExp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Power&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="c1"&gt;// It will yield &amp;lt;label&amp;gt;Power Hammer&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#tools&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RegExp&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;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Hammer$`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find a video about how to use &lt;code&gt;.findOne()&lt;/code&gt; at this &lt;a href="https://www.youtube.com/watch?v=uc0W4UtZd2A" rel="noopener noreferrer"&gt;link&lt;/a&gt;.&lt;/p&gt;

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




&lt;h1&gt;
  
  
  ACT3: RESOLUTION
&lt;/h1&gt;

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

&lt;p&gt;Well... Do you believe me now when I said at the beginning of this article that people &lt;em&gt;abuse&lt;/em&gt; (or &lt;em&gt;misuse&lt;/em&gt;) &lt;code&gt;.contains()&lt;/code&gt; in many different ways?&lt;/p&gt;

&lt;p&gt;Let me tell you, there are plenty of other ways to mess with this powerful Cypress query, but I stopped at 12+1 since this article is already quite lengthy. Besides, I really like the &lt;strong&gt;mystical number 13&lt;/strong&gt;, and that's because I'm not superstitious (that might bring bad luck)! 😄&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;strong&gt;I'd love to hear from you! Please don't forget to follow me, leave a comment, or a reaction if you found this article useful or insightful.&lt;/strong&gt;&lt;/em&gt; ❤️ 🦄 🤯 🙌 🔥&lt;/p&gt;

&lt;p&gt;You can also connect with me on my new &lt;strong&gt;YouTube channel&lt;/strong&gt;: &lt;a href="https://www.youtube.com/@SebastianClavijoSuero" rel="noopener noreferrer"&gt;https://www.youtube.com/@SebastianClavijoSuero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to support my work, consider buying me a coffee or contributing to a training session, so I can keep learning and sharing cool stuff with all of you.&lt;br&gt;
Thank you for your support!&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/sclavijosuero" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cypress</category>
      <category>qa</category>
      <category>automation</category>
      <category>testing</category>
    </item>
    <item>
      <title>Meet the New CYPRESS-SCHEMA-VALIDATOR: When ZOD Joined Forces with AJV for the Ultimate Validation Duo!</title>
      <dc:creator>Sebastian Clavijo Suero</dc:creator>
      <pubDate>Mon, 09 Jun 2025 05:07:51 +0000</pubDate>
      <link>https://forem.com/sebastianclavijo/meet-the-new-cypress-schema-validator-when-zod-joined-forces-with-ajv-for-the-ultimate-validation-2o73</link>
      <guid>https://forem.com/sebastianclavijo/meet-the-new-cypress-schema-validator-when-zod-joined-forces-with-ajv-for-the-ultimate-validation-2o73</guid>
      <description>&lt;h1&gt;
  
  
  ACT 1: EXPOSITION
&lt;/h1&gt;

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

&lt;p&gt;I am thrilled to introduce the new &lt;strong&gt;&lt;a href="https://github.com/sclavijosuero/cypress-schema-validator" rel="noopener noreferrer"&gt;CYPRESS-SCHEMA-VALIDATOR&lt;/a&gt;&lt;/strong&gt; plugin, the ultimate successor to &lt;strong&gt;&lt;a href="https://github.com/sclavijosuero/cypress-ajv-schema-validator" rel="noopener noreferrer"&gt;CYPRESS-AJV-SCHEMA-VALIDATOR&lt;/a&gt;&lt;/strong&gt;! Combining all the power of AJV schema validation from its predecessor and now also featuring ZOD Schema Validation, and new features. 🔥 🔥 🔥 &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;CYPRESS-SCHEMA-VALIDATOR is fully backward-compatible with the previous version&lt;/strong&gt;, and continues to integrate smoothly with leading Cypress API plugins like &lt;code&gt;@bahmutov/cy-api&lt;/code&gt; and &lt;code&gt;cypress-plugin-api&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This new version represents a significant leap forward in flexibility, usability, and debugging efficiency.&lt;/p&gt;

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




&lt;h1&gt;
  
  
  ACT 2: CONFRONTATION
&lt;/h1&gt;

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

&lt;h2&gt;
  
  
  🤔 What’s New?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Support for Both AJV and ZOD Validators&lt;/strong&gt;: Choose between AJV and ZOD for schema validation, offering versatility for diverse workflows.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terminal Display of Schema Errors&lt;/strong&gt;: Schema errors are now shown directly in the terminal during &lt;code&gt;run&lt;/code&gt; mode, simplifying debugging.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Customizable Styles&lt;/strong&gt;: Add a personalized touch by using custom icons and HEX colors to better distinguish validation issues.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistent Error Format&lt;/strong&gt;: Enjoy unified error formatting, regardless of the validator selected (AJV or ZOD), for seamless and clear reporting during testing.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  ⏮️ Fully Backward Compatible?
&lt;/h2&gt;

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

&lt;p&gt;AFFIRMATIVE! 🤟🤟🤟&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  🛸 How Do I Migrate?
&lt;/h2&gt;

&lt;p&gt;Upgrading your Cypress framework from &lt;strong&gt;CYPRESS-AJV-SCHEMA-VALIDATOR&lt;/strong&gt; to the new &lt;strong&gt;CYPRESS-SCHEMA-VALIDATOR&lt;/strong&gt; is as simple as this:&lt;/p&gt;

&lt;p&gt;1) Uninstall the previous plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm uninstall cypress-ajv-schema-validator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Install the new plugin:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;3) Replace previous imports in your project:&lt;/p&gt;

&lt;p&gt;Replace:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cypress-ajv-schema-validator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cypress-schema-validator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  ✔️ What About the existing &lt;code&gt;.validateSchema()&lt;/code&gt; Command In My Code?
&lt;/h2&gt;

&lt;p&gt;No worries, &lt;strong&gt;you don’t need to refactor your test cases beyond the imports&lt;/strong&gt;!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;.validateSchema()&lt;/code&gt; command still works with AJV schemas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A new alias, &lt;code&gt;.validateSchemaAjv()&lt;/code&gt;, is also available for AJV validation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Additionally, the plugin introduces &lt;code&gt;.validateSchemaZod()&lt;/code&gt; for ZOD schema validation.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it, &lt;strong&gt;NO DRAMA-LAMA!&lt;/strong&gt; 🦙🦙🦙&lt;/p&gt;

&lt;p&gt;If you're looking for drama, check out my article series: &lt;a href="https://dev.to/sebastianclavijo/series/30152"&gt;The Test Drama: Cypress and Playwright, Who Steals the Show?&lt;/a&gt; 😁&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT3: RESOLUTION
&lt;/h1&gt;

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

&lt;p&gt;Don't wait! Upgrade your Cypress framework with the new CYPRESS-SCHEMA-VALIDATOR and enjoy unparalleled flexibility for schema validations using two of the most powerful validators available today: AJV and ZOD (plus many other perks).&lt;/p&gt;

&lt;p&gt;You can find it in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/cypress-schema-validator" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/cypress-schema-validator&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/sclavijosuero/cypress-schema-validator" rel="noopener noreferrer"&gt;https://github.com/sclavijosuero/cypress-schema-validator&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;&lt;strong&gt;I'd love to hear from you! Please don't forget to follow me, leave a comment, or a reaction if you found this article useful or insightful.&lt;/strong&gt;&lt;/em&gt; ❤️ 🦄 🤯 🙌 🔥&lt;/p&gt;

&lt;p&gt;You can also connect with me on my new &lt;strong&gt;YouTube channel&lt;/strong&gt;: &lt;a href="https://www.youtube.com/@SebastianClavijoSuero" rel="noopener noreferrer"&gt;https://www.youtube.com/@SebastianClavijoSuero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to support my work, consider buying me a coffee or contributing to a training session, so I can keep learning and sharing cool stuff with all of you.&lt;br&gt;
Thank you for your support!&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/sclavijosuero" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cypress</category>
      <category>qa</category>
      <category>apitesting</category>
      <category>schemavalidator</category>
    </item>
    <item>
      <title>Stand Out in the QA Crowd: Just Do You, but 'wick' Style!</title>
      <dc:creator>Sebastian Clavijo Suero</dc:creator>
      <pubDate>Mon, 12 May 2025 04:15:30 +0000</pubDate>
      <link>https://forem.com/sebastianclavijo/stand-out-in-the-qa-crowd-just-do-you-but-wick-style-368i</link>
      <guid>https://forem.com/sebastianclavijo/stand-out-in-the-qa-crowd-just-do-you-but-wick-style-368i</guid>
      <description>&lt;p&gt;&lt;em&gt;(Cover image from pexels.com by Photos by ERRN)&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT 1: Why am I writing this article?
&lt;/h1&gt;

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

&lt;p&gt;Over the past year, I have been approached numerous times by people from various continents who aspire to become QA professionals. Many of them wanted to know what they could do to become great QA Engineers.&lt;/p&gt;

&lt;p&gt;In essence, they were asking how they could &lt;strong&gt;Stand Out in the QA Crowd&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But how could I tell these individuals what they should do? Frankly, I wasn’t entirely sure, as I’m still carving out my own path in this competitive, ever-evolving, yet fascinating QA world. These are people who are placing their trust in you and your judgment to guide their professional careers. That’s a huge responsibility.&lt;/p&gt;

&lt;p&gt;So, I shared with them the same principles I have strived to uphold throughout my personal and professional life. I believe these values are universally applicable and can genuinely help you shine in today’s highly competitive market, regardless of the profession or role you pursue. And that’s what this article is all about.&lt;/p&gt;

&lt;p&gt;However, to effectively apply these principles, long time ago I realized I needed to embrace something that could be captured in one simple yet powerful phrase:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Just Do You!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This seemingly simple yet often challenging approach is what will give you the courage to uphold these principles and truly make you stand out in the crowd.&lt;/p&gt;

&lt;p&gt;To do justice to this mindset, I will share with you, my friend, the raw, unfiltered, straightforward—and sometimes even blunt—version of my thoughts, to get it real!&lt;/p&gt;

&lt;p&gt;So, if you’re not particularly open to hearing thoughts that might push you out of your comfort zone, this might be the perfect moment to pause and redirect your focus to something else. 🙂&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT 2:  Let’s Get Real—No Sugar Coating
&lt;/h1&gt;

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

&lt;h2&gt;
  
  
  Just Do You
&lt;/h2&gt;

&lt;p&gt;OK, let me be completely honest to be fair...&lt;/p&gt;

&lt;p&gt;Most of the time, this approach has served me tremendously well, helping me achieve great success, overcome significant challenges, and earn the respect of many peers.&lt;/p&gt;

&lt;p&gt;However, there have been instances where it didn’t work as effectively as I had hoped. Sometimes it was because I didn’t implement it in the most effective way, and in many other cases, I genuinely believe no approach would have worked—whether due to external factors beyond my control or certain personalities where nothing could have made a difference.&lt;/p&gt;

&lt;p&gt;So, &lt;strong&gt;why should I change to please others?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Exactly, just do you! But that doesn’t mean you have a free pass to do whatever you want or to shirk your responsibilities.&lt;/p&gt;

&lt;p&gt;What it truly means is to &lt;strong&gt;gift the world the best version of you&lt;/strong&gt;. Your core values must be strong and unwavering if you aim to succeed in anything.&lt;/p&gt;

&lt;p&gt;We all know the expression "&lt;em&gt;Fake it Until You Make It&lt;/em&gt;". It has unfortunately become very popular, especially among people navigating today’s market but lacking solid technical or interpersonal skills. Too often, it is used as an excuse to avoid putting in the extra effort required beforehand to truly fulfill the demands of any position.&lt;/p&gt;

&lt;p&gt;But let me tell you what I think about that phrase, which is becoming a way of life for many... &lt;strong&gt;It's BS&lt;/strong&gt;! Pure, unfiltered, and odorous BS! 😏&lt;/p&gt;

&lt;p&gt;Sorry if I am too graphic, but yes, it is BS, no matter how many flies are trying to sell it to you, or even eat it themselves... Ewww! As we say in my home country, it is just pure '&lt;strong&gt;caca de vaca&lt;/strong&gt;' 🐄💩.&lt;/p&gt;

&lt;p&gt;So instead of faking it, don’t wait for "some day" to make it, &lt;strong&gt;just do it&lt;/strong&gt;! No tomorrow, not in one week... but NOW!&lt;/p&gt;

&lt;p&gt;Yes, it might sound cliché —like a well-known sports brand slogan— but it is a mindset you must embrace and commit to fully. Believe me, pretending won’t take you far. People are sharp and perceptive, and most can spot a fake from miles away.&lt;/p&gt;

&lt;p&gt;It might not be easy, I know. But I truly believe most people are capable of doing what they put their mind to. They just need to realize their own potential and &lt;em&gt;learn how to overcome their fear&lt;/em&gt; to give it their all. &lt;strong&gt;If you want to succeed, you cannot hold back&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But still... YOU NEED TO BE YOU. If you’re quirky, don’t be afraid to embrace it. If your mind works differently or you approach problems in unconventional ways, that’s not a flaw, &lt;strong&gt;it is a strength&lt;/strong&gt;. It makes you unique. Unique people achieve unique things, often in ways that others might label as unorthodox or even weird, but that’s perfectly OK. &lt;strong&gt;Just do &lt;em&gt;(the better side of)&lt;/em&gt; you&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;You don’t have to be quirky if that’s not who you are. But if you are, embrace it. What truly matters to most companies—especially those worth working for—isn’t necessarily how you do your work, but that you deliver results and ensure high quality. These companies do want to trust you, but you must prove yourself worthy of that trust. Often, they’re not overly concerned with originality or unconventional methods. However, those traits can work in your favor, helping you stand out and be remembered, turning your quirkiness into a valuable asset rather than a liability.&lt;/p&gt;

&lt;p&gt;You’ll encounter companies or individuals who may not embrace your uniqueness, and that’s perfectly fine. Chances are, you don’t align with their rigid perspective either, and that’s OK too. Move on! As my mother often says: "You didn’t grow up there, and they didn’t shape who you are". So again, why change for them?&lt;/p&gt;

&lt;p&gt;Actually, in some cases, your individuality might give you a head start in standing out from the crowd, but let’s be real—it won’t be enough on its own. You still need to deliver on your responsibilities.&lt;/p&gt;

&lt;p&gt;And for that, there is one thing you absolutely have to embrace with every fiber of your being: &lt;strong&gt;You need to Truly Care!&lt;/strong&gt;&lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  Truly Care
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;best professionals genuinely care about the work they do&lt;/strong&gt; or the projects they contribute to, &lt;strong&gt;and they show it&lt;/strong&gt;. They don’t fake it! They take full ownership, treating their work as more than just a way to fulfill a job description or earn a paycheck.  They possess an innate drive to feel proud at the end of the day about what they have accomplished.&lt;/p&gt;

&lt;p&gt;You don’t? Then maybe it’s time to reconsider your career path and seek a role where your lack of care will not impact the lives of so many around you.&lt;/p&gt;

&lt;p&gt;QA is a role of immense responsibility. We are the guardians of user experience, ensuring our product's integrity and preserving our company’s reputation!&lt;/p&gt;

&lt;p&gt;Let me tell you a story about something that happened to me many years ago.&lt;/p&gt;

&lt;p&gt;It was early March in Sevilla, my beautiful hometown. Some of you might find it familiar… Perhaps because of &lt;em&gt;Game of Thrones&lt;/em&gt;? 😉&lt;/p&gt;

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

&lt;p&gt;I had just joined a new startup company in Spain less than a week earlier. Their offices were located in the garage of the founder’s house in Madrid.&lt;/p&gt;

&lt;p&gt;At the same time, I was preparing for a big life change—I was about to get married that summer, though my fiancée was still in the US. Believe me, switching from a well established company to a brand-new startup operating out of a garage, especially when you’re about to get married, might seem like a bold move. But I knew it was the right choice for the future of my new family.&lt;/p&gt;

&lt;p&gt;On my fifth day, while working from home until they set up an office in Sevilla, I got a call from the Founder-CEO-Director-PR-HR of my new company, let's call her M. Well, when a company operates out of a garage, one person often has to wear many hats! 😄&lt;/p&gt;

&lt;p&gt;I still remember it like it was just yesterday. She said:&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;"Sebastian—&lt;em&gt;told me, M&lt;/em&gt;—, I need you to come to Madrid for three months to work at the offices of a new client. Well… they’re not exactly our client, but I want them to be (&lt;em&gt;it was one of the largest telecom companies in the country back then&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;But… how can I put this... —&lt;em&gt;surprised me, M&lt;/em&gt;— Everything’s a mess! We’re not working directly for the client but for a consulting company they hired. This company already has three so-called expert consultants on-site, but they have no idea how to solve the client’s problem. So, they’re subcontracting us to get the job done. And by 'us,' I mean you.&lt;/p&gt;

&lt;p&gt;But I should also tell you that on-site—&lt;em&gt;continued, M&lt;/em&gt;—, there are a couple of official consultants for the product the client is using. However, they’ll only be there for one more week, so you’ll have, at most, three days to overlap with them.&lt;/p&gt;

&lt;p&gt;They told me they’re leaving because everything is in shambles—&lt;em&gt;warned me, M&lt;/em&gt;—. The code has been meddled with by too many hands, none skilled enough to preserve its integrity. On top of that, they believe there are too many external companies involved, each with conflicting interests."&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Honestly, they weren’t wrong. Counting my company, there were now four external companies, plus the client’s own resources, working on this project. 🤦‍♂️&lt;/p&gt;

&lt;p&gt;Can you guess what words crossed my mind?&lt;/p&gt;

&lt;p&gt;Exactly! "&lt;em&gt;This is pure 'caca de vaca'&lt;/em&gt;" 🐄💩&lt;/p&gt;

&lt;p&gt;I could smell it, even from 533 kilometers away, all the way from Sevilla to Madrid! Sorry for being too graphic again! 😁&lt;/p&gt;

&lt;p&gt;I’m not going to lie to you... she had me scared. All of a sudden, I wasn’t so sure anymore if joining this startup was the best decision for me and the new family I was about to build. Man, I was really in a pickle!&lt;/p&gt;

&lt;p&gt;Three days later, there I was (&lt;em&gt;Sebastian&lt;/em&gt;) on the AVE (&lt;em&gt;the high-speed train originally built for the Universal Expo '92&lt;/em&gt;) racing toward the Wild Wild Madrid to face... I really had no idea what I was about to face. The only thing I knew for certain was that I had been hired to do a job in a multimillion-dollar project, and both my company and the client were counting on me. Uff, no joke!&lt;/p&gt;

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

&lt;p&gt;After being met with a certain dose of cynicism by the consultants from the company we were contracted by (who, by the way, had already been there for over six months), I was finally introduced to the client. I could feel their apprehension and the frustration of a project that had been stagnating for nine months. Even more unsettling was their certainty that nobody who had shown up so far was capable of getting things done. They genuinely didn’t know who they could trust anymore.&lt;/p&gt;

&lt;p&gt;What could I possibly say to this person that he hadn’t already heard from the countless others who had come across the project?&lt;/p&gt;

&lt;p&gt;I sympathized with them. How could I show them that I genuinely wanted to help, that I truly &lt;strong&gt;did care&lt;/strong&gt; about them?&lt;/p&gt;

&lt;p&gt;So, I simply told them what I could honestly do given the circumstances: "OK, let me take a look at what’s there, and we’ll talk again in a week."&lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  The Method to Support the Principles
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;I give you a hint: Follow the the &lt;strong&gt;Bold&lt;/strong&gt; Brick Road!&lt;/em&gt; 😄&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In situations like that, there are basically two things you can do:&lt;br&gt;
You can try to keep a low profile, letting the days and weeks slip by while giving the impression that you’re busy and productive;&lt;br&gt;
Or, you can slap your palm on the table (when no one’s looking) and declare loud and clear: '&lt;strong&gt;&lt;em&gt;Here I am, baby, ready to shake things up!&lt;/em&gt;&lt;/strong&gt;'&lt;/p&gt;

&lt;p&gt;That’s the attitude that will make you stand out in the crowd. Sure, taking risks can be scary, and it’s always tempting to stay in your comfort zone—and that’s perfectly fine if that’s what you choose. But hear me out: staying comfortable is not what will make you stand out. No pain, no glory. &lt;/p&gt;

&lt;p&gt;To grow professionally, you need to step up and do grown-up things. Tasks that kids can’t handle or are not willing to take on.&lt;/p&gt;

&lt;p&gt;I strongly recommend &lt;strong&gt;setting a deadline to come up with a plan&lt;/strong&gt;, a realistic deadline, but not too far in the future. In my case, I committed to returning in a week with a clear course of action.&lt;/p&gt;

&lt;p&gt;Also, &lt;strong&gt;assess your environment&lt;/strong&gt;. What kind of ground are you stepping on? Is it solid, or is it unstable and squishy? Can you breathe the air safely, or do you need to control your breath because it’s too unpleasant?  Do you remember the &lt;em&gt;vaquita&lt;/em&gt;? 😅&lt;/p&gt;

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

&lt;p&gt;After the initial warnings I received and just one day in the field, it became glaringly clear: the terrain was sticky, and the air felt suffocating to breathe. The consulting company that had hired us to assist with the system design and implementation (though, truthfully, to do their job) was clearly at odds with a competitor who was also on-site, managing the data collection and entry for the client. They suspected that the ultimate, unspoken goal of the competitor was to outmaneuver them—the very company that brought us in—and claim the entire enchilada for themselves.&lt;/p&gt;

&lt;p&gt;The third external company, the official consultants for the product, was simply there to bill hours, and counting down the hours until they could get out of there!&lt;/p&gt;

&lt;p&gt;Meanwhile, the client had their own internal team, a mix of developers and data quality control staff who were brought on just nine months ago, supposedly brimming with expertise in the field.&lt;/p&gt;

&lt;p&gt;And then there was my company. In other words, me.&lt;/p&gt;

&lt;p&gt;After that, I did the only thing I could do if I truly wanted to solve the conundrum I was caught in:&lt;/p&gt;

&lt;p&gt;'&lt;em&gt;Echarle dos cojones&lt;/em&gt;' (as figuratively we say in my home country), and...&lt;/p&gt;

&lt;p&gt;Dig, and ask. Dig again, and ask again. &lt;strong&gt;Keep digging, and keep asking&lt;/strong&gt; relentlessly, even if you’re asking uncomfortable questions!&lt;/p&gt;

&lt;p&gt;It's fascinating what you can uncover once you start 'excavating', especially in a place where countless people before you have been digging and then carefully covering their tracks. It is indeed like working on the infamous &lt;em&gt;Money Pit&lt;/em&gt; on Oak Island! Back then, &lt;a href="https://www.history.com/shows/the-curse-of-oak-island" rel="noopener noreferrer"&gt;The Course of Oak Island TV show&lt;/a&gt; didn’t even exist yet, but man, it’s the first image that popped into my mind as I was writing this—so, of course, I had to include it. I just couldn’t resist (right, Todd?)! 😆&lt;/p&gt;

&lt;p&gt;The reality was that, even though I wasn’t getting straight answers to any of my questions, the information I gathered from the different stakeholders across these various companies gave me plenty of leads. They were all disconnected, but each one seemed worth exploring.&lt;/p&gt;

&lt;p&gt;I haven’t mentioned it yet, nor do I intend to go into detail about the nature of the work or the type of system I had to get running. After all, it’s just like the &lt;em&gt;MacGuffin&lt;/em&gt; in a Hitchcock movie—the backdrop to the story I’m sharing with you. What I will share is that it involved a distributed system operating across the county, where all database replicas needed to stay synchronized, ensuring no precious data was lost. And I think that’s plenty of detail for you to know. 😄&lt;/p&gt;

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

&lt;p&gt;The interesting thing was that, after a week, the apprehension I might have felt initially was gradually fading away, inversely proportional to my growing knowledge of the system.&lt;/p&gt;

&lt;p&gt;Well… it makes sense, doesn’t it? Duh!&lt;/p&gt;

&lt;p&gt;But only once you have truly immersed yourself in the mess, instead of taking the easy road and keeping a low profile and letting the days and weeks quietly slip by.&lt;/p&gt;

&lt;p&gt;After a week in their offices, I approached the client as promised (the head of the department, who was a not insignificant big shot in that telecom) and presented my &lt;strong&gt;blueprint&lt;/strong&gt; outlining how I thought we could fix the broken pipes with minimal monetary expense. We just needed to tighten a few screws and replace a couple of leaking tubes. However, it was essential to bring together what remained of the 'team' (remember,  the official product consultants were already gone).&lt;/p&gt;

&lt;p&gt;So, the next step in the plan was to build &lt;strong&gt;consensus toward a common objective&lt;/strong&gt;. And for that, nothing works better than recognizing what each person was doing well and then, with carefully nuanced guidance (so they still believed it was their own idea), asking them how they could improve their part in the process.&lt;/p&gt;

&lt;p&gt;I know, this might seem like a little bit of manipulation, but as they say, &lt;em&gt;all’s fair in love and war&lt;/em&gt;. Just kidding! 😉&lt;/p&gt;

&lt;p&gt;Truth be told, when you candidly talk to people and &lt;strong&gt;genuinely listen to what they have to say&lt;/strong&gt;, you realize that they aren’t the mischievous personalities you initially thought—or were led to believe—they were.&lt;/p&gt;

&lt;p&gt;They need to see that you &lt;strong&gt;genuinely care&lt;/strong&gt;. Once they do, they might surprise you by showing they care as well. It is the same apprehension you felt when stepping into unfamiliar territory that made them defensive. It’s not about assigning blame for someone else’s stagnation, but rather fostering understanding and cultivating a shared sense of growth, all directed toward a common goal.&lt;/p&gt;

&lt;p&gt;And now comes the hard part, &lt;strong&gt;getting things done&lt;/strong&gt;! You can't expect or ask others to do something you are not willing to do yourself. You've likely heard the well-worn phrase: "Lead by example!" Beautiful sentiment, right? Very motivational! We drop it into conversations every chance we get...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cut the BS&lt;/strong&gt; (AKA 'caca de vaca 🐄💩)! It's not enough to simply say "lead by example" or to just make it seem like you're doing it. You have to genuinely mean it, and do it for real! That’s how you learn, how you inspire others to follow you, and how you truly stand out from the crowd, whether it’s a QA crowd, a Dev crowd, or any other kind of crowd.&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT 3:  Sebastian, You Are Amazing! Truly Inspirational! 🤣
&lt;/h1&gt;

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

&lt;p&gt;Thank you, I really appreciate your sentiment! 😍&lt;/p&gt;

&lt;p&gt;But believe me, I wouldn’t say so lightly. Like any declaration of intentions and goodwill, we are still human, and sometimes our actions simply can’t keep up with our intentions.&lt;/p&gt;

&lt;p&gt;But that’s OK, nobody is really expecting you to operate at 110% all the time. That level of effort is unsustainable. The true key lies in giving it your all when it truly matters. And that’s something not everyone is willing to do. That is what will set you apart and put that extra mile between you and the rest of the crowd.&lt;/p&gt;

&lt;p&gt;To wrap up the story, the funny part of all this—which still makes me smile to this day—is that, after barely three weeks of working there, a stakeholder approached looking for some crucial information. To my surprise, the client's senior programmer-analyst genuinely told them: "You need to talk to Sebastian. He’s the expert and knows our system better than anyone". 😆&lt;/p&gt;

&lt;p&gt;By the way, within the next six months, the client replaced all the external companies and &lt;strong&gt;entrusted&lt;/strong&gt; my company to take charge of the entire operation—and cook the whole enchilada. 🌮 (Yes, this is a taco, but sadly, there’s no emoji for an enchilada!)&lt;/p&gt;

&lt;p&gt;I know... this ended up being a long story, maybe a little too quirky, maybe even a bit too graphic for your taste. But that’s just me, the real me: I like to share, some times too much, because I &lt;strong&gt;truly care&lt;/strong&gt;; I might be a little unorthodox, because I &lt;strong&gt;just do me&lt;/strong&gt;; And yes, I might be a little too graphic because, as we say in my home country, '&lt;em&gt;Me gustan las cosas claras, y el chocolate espeso!&lt;/em&gt;' (I like things to be clear, and my chocolate thick!)&lt;/p&gt;

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




&lt;p&gt;&lt;em&gt;&lt;strong&gt;I'd love to hear from you! Please don't forget to follow me, leave a comment, or a reaction if you found this article useful or insightful.&lt;/strong&gt;&lt;/em&gt; ❤️ 🦄 🤯 🙌 🔥&lt;/p&gt;

&lt;p&gt;You can also connect with me on my new &lt;strong&gt;YouTube channel&lt;/strong&gt;: &lt;a href="https://www.youtube.com/@SebastianClavijoSuero" rel="noopener noreferrer"&gt;https://www.youtube.com/@SebastianClavijoSuero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to support my work, consider buying me a coffee or contributing to a training session, so I can keep learning and sharing cool stuff with all of you.&lt;br&gt;
Thank you for your support!&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/sclavijosuero" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>qa</category>
      <category>careerdevelopment</category>
    </item>
    <item>
      <title>The Async Nature of Cypress: Don't Mess with the Timelines in Your Cypress Tests 'Dual-Verse'</title>
      <dc:creator>Sebastian Clavijo Suero</dc:creator>
      <pubDate>Wed, 30 Apr 2025 05:46:32 +0000</pubDate>
      <link>https://forem.com/sebastianclavijo/the-async-nature-of-cypress-dont-mess-with-the-timelines-in-your-cypress-tests-dual-verse-3ehh</link>
      <guid>https://forem.com/sebastianclavijo/the-async-nature-of-cypress-dont-mess-with-the-timelines-in-your-cypress-tests-dual-verse-3ehh</guid>
      <description>&lt;ul&gt;
&lt;li&gt;ACT 1: EXPOSITION&lt;/li&gt;
&lt;li&gt;
ACT 2: CONFRONTATION

&lt;ul&gt;
&lt;li&gt;The Asynchronous Nature of Cypress&lt;/li&gt;
&lt;li&gt;
The Two Timelines in Cypress Tests (AKA 'Dual-Verse')

&lt;ul&gt;
&lt;li&gt;Example Test 1&lt;/li&gt;
&lt;li&gt;Example Test 2&lt;/li&gt;
&lt;li&gt;Example Test 3&lt;/li&gt;
&lt;li&gt;Example Test 4&lt;/li&gt;
&lt;li&gt;Example Test 5&lt;/li&gt;
&lt;li&gt;Example Test 6&lt;/li&gt;
&lt;li&gt;And What About Cypress Hooks?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;ACT3: RESOLUTION&lt;/li&gt;

&lt;/ul&gt;




&lt;h1&gt;
  
  
  ACT 1: EXPOSITION
&lt;/h1&gt;

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

&lt;p&gt;A lot has been said about the asynchronous nature of the Cypress test framework. This asynchrony is often blamed as the primary cause of flakiness in Cypress tests. And, I would say… yeah, I agree. &lt;strong&gt;Asynchrony is the main reason of flakiness, but that’s because it’s not used correctly, which stems from it not being properly understood&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Why is that? I can’t pinpoint the exact reason, but this is my suspicious, and I’m really not trying to be judgmental:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The proper documentation on Cypress’s async behavior is often skipped or not fully read before people jump into creating more complex tests.&lt;br&gt;
 &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It’s very common for people to mix synchronous JavaScript code with asynchronous Cypress commands and queries in their tests, and treat them the same way. &lt;br&gt;
 &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And, well… asynchronous behavior doesn’t come naturally to our brains. We think synchronously by default, and grasping asynchronous concepts requires extensive hands-on usage, often coupled with making a lot (and I mean a lot) of mistakes.&lt;br&gt;
 &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal of this blog is not to lecture you on general theories of asynchrony and race conditions, but to offer a clearer yet detailed explanation of how asynchrony works specifically in Cypress when running tests in a browser.&lt;/p&gt;

&lt;p&gt;I decided to tackle the subject with a different approach than how it is usually discussed and explained. This approach will be more visually oriented, as the human brain processes images and diagrams more effectively than large amounts of text, especially when dealing with complex concepts.&lt;/p&gt;

&lt;p&gt;To lay it all out, I’ve coined a term: “&lt;strong&gt;&lt;em&gt;The Two Timelines in Your Cypress Tests&lt;/em&gt;&lt;/strong&gt;” or, to add a little fun, “&lt;strong&gt;&lt;em&gt;The Cypress Dual-Verse&lt;/em&gt;&lt;/strong&gt;”! 😄&lt;/p&gt;




&lt;h1&gt;
  
  
  ACT 2: CONFRONTATION
&lt;/h1&gt;

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

&lt;h2&gt;
  
  
  The Asynchronous Nature of Cypress
&lt;/h2&gt;

&lt;p&gt;But before we explore the details (and the fun graphical part) of how the two timelines behave and coexist when running your Cypress tests, I want to take a moment to touch on and discuss some core aspects of how Cypress works in this context. If you’re looking for more specifics, I highly recommend reading the official Cypress.io documentation on &lt;a href="https://learn.cypress.io/cypress-fundamentals/understanding-the-asynchronous-nature-of-cypress" rel="noopener noreferrer"&gt;Understanding the Asynchronous nature of Cypress&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are a few essential points to note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cypress commands operate asynchronously and are &lt;strong&gt;executed later in a queued sequence&lt;/strong&gt; (grasping this is essential!).&lt;br&gt;
 &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cypress commands do not return their subjects directly; instead, they &lt;strong&gt;yield&lt;/strong&gt; their subjects. As a result, you cannot assign their output to a variable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cypress commands do not return JavaScript Promises; rather, they return what are commonly referred to as &lt;strong&gt;&lt;em&gt;Chainables&lt;/em&gt;&lt;/strong&gt;. Although the behavior of Chainables is somewhat similar to Promises, you cannot use &lt;code&gt;async/await&lt;/code&gt; in your Cypress tests.&lt;br&gt;
 &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Cypress commands are inherently asynchronous and yield chainable objects, which enables the next command in the chain to execute after the previous one completes.&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Example of chained commands in Cypress:&lt;br&gt;
 &lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Visit the '/home' web page&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Wait until the &amp;lt;button&amp;gt; element is present in the DOM,&lt;/span&gt;
&lt;span class="c1"&gt;// verify it contains the text "Click me!", and then click it&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contain&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;Click me!&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="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


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

&lt;p&gt;In this example, the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element retrieved by the &lt;code&gt;.get()&lt;/code&gt; command is yielded to the &lt;code&gt;.should()&lt;/code&gt; command, which makes an assertion over it, and in turn is yielded to the &lt;code&gt;.click()&lt;/code&gt; command.&lt;br&gt;
 &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are two Cypress commands that help streamline the handling of asynchronous behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;wrap()&lt;/code&gt;&lt;/strong&gt;: Used to wrap a JavaScript object or value so that it can be used within Cypress's chainable command structure. This is particularly useful when you want to work with non-Cypress objects, such as data variables or elements returned by a JavaScript function, within a Cypress test.&lt;br&gt;
 &lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test Object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Wrap the object to use it in Cypress's command chain&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;its&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;equal&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;Test Object&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;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;then()&lt;/code&gt;&lt;/strong&gt;: Used to execute a callback function once the previous Cypress command is resolved. It allows you to work directly with the resolved value of an asynchronous Cypress chainable command. This is especially useful for performing custom actions or additional assertions that cannot be directly achieved through Cypress commands.&lt;br&gt;
 &lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;$button&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// $button is the resolved jQuery element from cy.get('button')&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;$button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// Log the button's text&lt;/span&gt;
  &lt;span class="nx"&gt;$button&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="c1"&gt;// Perform a click directly with raw jQuery commands&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


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

&lt;p&gt;If you're accustomed to working with native Promises, the Cypress .then() behaves in a similar way. However, they are not promises; instead,  they are sequential commands added to a central Cypress queue, where they are executed asynchronously at a later time.&lt;/p&gt;

&lt;p&gt;You can nest additional Cypress commands within the &lt;code&gt;.then()&lt;/code&gt; block. Each nested command can utilize the results or actions performed by the preceding commands. Commands outside of the &lt;code&gt;.then()&lt;/code&gt; block will not execute until all the nested commands inside have completed. This is crucial for gaining a proper understanding of how the Cypress queue works.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📑 Note: In Cypress, you cannot implement a &lt;code&gt;catch(error)&lt;/code&gt; handler for a failed command since Cypress does not provide built-in error recovery for failed commands. If a command fails, it halts the execution of all subsequent commands, leading to the failure of the entire test.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;h2&gt;
  
  
  The Two Timelines in Cypress Tests (AKA 'Dual-Verse')
&lt;/h2&gt;

&lt;p&gt;We have finally reached the fun part! 😄&lt;br&gt;
What do I mean by "The Two Timelines"?&lt;/p&gt;

&lt;p&gt;Here’s the answer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The first is the &lt;strong&gt;JavaScript timeline&lt;/strong&gt;, executed by the browser, which runs the JavaScript code synchronously, one statement after another.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The second time line is the &lt;strong&gt;Cypress queue&lt;/strong&gt;, which follows its own pace, operating "in parallel" with the JavaScript timeline.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;🚨🚨 Spoiler alert: the JavaScript timeline does not wait for the Cypress queue until both timelines reach the end of the block in which they are defined (this could be an &lt;code&gt;it()&lt;/code&gt; test, a &lt;code&gt;.then()&lt;/code&gt; block, a &lt;code&gt;before()&lt;/code&gt;/&lt;code&gt;beforeEach()&lt;/code&gt;/&lt;code&gt;after()&lt;/code&gt;/&lt;code&gt;afterEach()&lt;/code&gt; hook, or any other block definition supported in Cypress). &lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;The official recommendation from Cypress is not to mix async code with sync code (see &lt;a href="https://docs.cypress.io/app/core-concepts/introduction-to-cypress#Mixing-Async-and-Sync-code" rel="noopener noreferrer"&gt;here&lt;/a&gt;). However, in reality, there will be cases and situations where you encounter this practice—whether you’re debugging flaky tests implemented by someone else or addressing logical problems that cannot be resolved with only Cypress commands.&lt;/p&gt;

&lt;p&gt;This is why it is crucial for you to understand how Cypress's async behavior works and how these two timelines operate and interact with each other.&lt;/p&gt;

&lt;p&gt;And we are going to learn this through a series of six examples and some "&lt;em&gt;pretty&lt;/em&gt;" timeline graphs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ DISCLAIMER: The examples we will explore are not meant to showcase the recommended best practices for these cases but are instead tailored to help me clearly illustrate how these timelines work.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Example Test 1
&lt;/h3&gt;

&lt;p&gt;Here is the test:&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;// Cypress Commands and JS Sync Code&lt;/span&gt;
&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test 1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&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://jsonplaceholder.typicode.com/posts/1&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="nf"&gt;then&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;  &lt;span class="c1"&gt;// userId value is 1&lt;/span&gt;
            &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;equal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Pass! (because id == 1)&lt;/span&gt;
            &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;// id set to 2&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;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Pass! (because id == 2)&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the first example, we will perform a network request and then process the response body by extracting the &lt;code&gt;userId&lt;/code&gt; field (which contains the value &lt;code&gt;1&lt;/code&gt;). Next, we will assert the obtained value, modify it, and then assert again.&lt;/p&gt;

&lt;p&gt;However, note that we have &lt;strong&gt;mixed Cypress &lt;code&gt;should&lt;/code&gt; assertions with Chai &lt;code&gt;expect&lt;/code&gt; assertions&lt;/strong&gt; while also storing the extracted values in a &lt;strong&gt;local variable&lt;/strong&gt; within the scope of the block.&lt;/p&gt;

&lt;p&gt;But in what order do the browser execute these JavaScript statements (which are sync) along with the Cypress commands (that are async)?&lt;/p&gt;

&lt;p&gt;When running a test, Cypress adds all the commands it finds to its &lt;strong&gt;queue&lt;/strong&gt; in the same order they are encountered. However, the QA Engineer does not truly know or cannot assume when a command will be taken by the Cypress runner from the queue and executed.&lt;/p&gt;

&lt;p&gt;And that is because JavaScript statements are executed lightning-fast by the browser and don’t wait around for the Cypress commands. It is like they are each living in their own parallel universe with separate timelines. 😄&lt;/p&gt;

&lt;p&gt;The only certainty is that nested &lt;strong&gt;JavaScript statements and Cypress commands&lt;/strong&gt; within another Cypress command block, are executed before any remaining &lt;strong&gt;Cypress command&lt;/strong&gt; outside that block.&lt;/p&gt;

&lt;p&gt;The two timelines come together either at the conclusion of a test or when a Cypress block finishes (like a &lt;code&gt;.then()&lt;/code&gt; block).&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;If we carefully analyze the order in which each statement and command is executed for the Test 1, it will look like a temporal flow diagram like this:&lt;/p&gt;

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

&lt;p&gt;Interesting right? 🤔&lt;/p&gt;

&lt;p&gt;Notice in the temporal diagram, that the JavaScript statements are executed as soon as the &lt;code&gt;.then()&lt;/code&gt; block begins and do not wait for the Cypress commands, even if those commands are defined earlier in the test code. The &lt;strong&gt;&lt;code&gt;time??&lt;/code&gt;&lt;/strong&gt; (&lt;em&gt;time with question marks&lt;/em&gt; )in the graph represents the uncertainty around how much time will elapse between when a command is queued by Cypress and when it is actually executed. However, it is almost guaranteed that the JavaScript statements will run before the Cypress commands are executed within the block.&lt;/p&gt;

&lt;p&gt;Also, keep in mind that the value of the JavaScript variable &lt;code&gt;id&lt;/code&gt;, which is used in &lt;code&gt;.wrap()&lt;/code&gt;, is taken from its actual value at the moment &lt;code&gt;.wrap()&lt;/code&gt; is encountered during the test execution. The wrap command is then queued with that specific value (in this case, &lt;code&gt;1&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;And what happens in the Cypress Log in the test runner when we execute this test?&lt;/p&gt;

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

&lt;p&gt;The Cypress Log clearly shows that the &lt;code&gt;expect()&lt;/code&gt; Chai assertion (which is JavaScript code) runs before the &lt;code&gt;.wrap()&lt;/code&gt; and &lt;code&gt;.should()&lt;/code&gt; Cypress commands. And the test pass!&lt;/p&gt;

&lt;p&gt;Yeah, I wasn’t lying before—pure JavaScript code runs much faster than Cypress commands. It’s like they exist in a &lt;strong&gt;&lt;em&gt;Dual-Verse&lt;/em&gt;&lt;/strong&gt;, with time running much faster in one than the other, and they only converge in the future when the Cypress test or block ends!&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Example Test 2
&lt;/h3&gt;

&lt;p&gt;What if we create a second test similar to the first one, performing the same assertions, but using only Cypress commands?&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;// Only Cypress Commands&lt;/span&gt;
&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test 2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&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://jsonplaceholder.typicode.com/posts/1&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="nf"&gt;its&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.userId&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="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eq&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Pass! (because userId == 1)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Increment id and yield value to next command&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eq&lt;/span&gt;&lt;span class="dl"&gt;'&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;// Pass! (because id == 2)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is what the temporal execution diagram will look like in this case:&lt;/p&gt;

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

&lt;p&gt;Hmmm, it's quite different from Test 1, even though it performs the same type of assertions. However, this time, it only uses Cypress commands instead of mixing with Chai assertions.&lt;/p&gt;

&lt;p&gt;This difference is also noticeable when we look at the test results in the Cypress Log:&lt;/p&gt;

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

&lt;p&gt;This time, the assertions were executed in order (first asserting &lt;code&gt;1&lt;/code&gt;, and then asserting &lt;code&gt;2&lt;/code&gt;), because Cypress executes commands in the same order they are added to the command queue. And the test also pass!&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Example Test 3
&lt;/h3&gt;

&lt;p&gt;Is the third test... a charm?&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;// Cypress Commands (with 2 .then()) and JS Sync Code&lt;/span&gt;
&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test 3 &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// id set to null&lt;/span&gt;

    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&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://jsonplaceholder.typicode.com/posts/1&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="nf"&gt;then&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;  &lt;span class="c1"&gt;// userId value is 1&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;equal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Pass! (because id == 1)&lt;/span&gt;
        &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;// id set to 2&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;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Pass! (because id == 2)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This third test is a bit more complex. We are defining a local variable &lt;code&gt;id&lt;/code&gt; at the beginning of the test, which is accessible anywhere within the test, and also mixing JavaScript statements with Cypress commands.&lt;/p&gt;

&lt;p&gt;The temporal diagram will look like:&lt;/p&gt;

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

&lt;p&gt;Notice that the variable &lt;code&gt;id&lt;/code&gt; is initialized at the very beginning of the test to &lt;code&gt;null&lt;/code&gt; and then modified twice within both &lt;code&gt;.then()&lt;/code&gt; blocks. Similar to Test 1, the &lt;code&gt;expect&lt;/code&gt; Chai assertion (that is Javascript sync code) is executed before the &lt;code&gt;.should()&lt;/code&gt; Cypress assertion (that is async). However, the test still pass.&lt;/p&gt;

&lt;p&gt;This is confirmed by the Cypress log when we run it in the Cypress runner:&lt;/p&gt;

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

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

&lt;h3&gt;
  
  
  Example Test 4
&lt;/h3&gt;

&lt;p&gt;Let’s spice things up a bit! What would happen if we threw an &lt;code&gt;expect&lt;/code&gt; Chai assertion at the very end of the test, outside both &lt;code&gt;.then()&lt;/code&gt; blocks?&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;// Cypress Commands (with 2 .then()), JS Sync Code and expect() outside the last .then()&lt;/span&gt;
&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test 4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// id set to null&lt;/span&gt;

    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&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://jsonplaceholder.typicode.com/posts/1&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="nf"&gt;then&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;  &lt;span class="c1"&gt;// userId value is 1&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;equal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Pass! (because id == 1)&lt;/span&gt;
        &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;// id set to 2&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;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Pass! (because id == 2)&lt;/span&gt;
    &lt;span class="p"&gt;})&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;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Fail!!! (because id == null)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And who wins the &lt;em&gt;Amazing Race&lt;/em&gt;?&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The test fail!!!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Indeed, even though the &lt;code&gt;expect&lt;/code&gt; is placed at the end of the code, it is executed immediately after we initialize the variable &lt;code&gt;id&lt;/code&gt; to &lt;code&gt;null&lt;/code&gt;, long before it is updated within the Cypress commands, so &lt;strong&gt;the Cypress commands are not even executed at all&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;This is confirmed by the Cypress log when the test is executed:&lt;/p&gt;

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

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

&lt;h3&gt;
  
  
  Example Test 5
&lt;/h3&gt;

&lt;p&gt;Let me think...&lt;/p&gt;

&lt;p&gt;What if, instead of putting an &lt;code&gt;expect&lt;/code&gt; at the end of the test, I use Cypress commands to perform the assertion (using &lt;code&gt;.wrap()&lt;/code&gt; and &lt;code&gt;.should()&lt;/code&gt;)?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Cypress Commands (with 2 .then()), JS Sync Code and should() outside the last .then()&lt;/span&gt;
&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test 5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// id set to null&lt;/span&gt;

    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&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://jsonplaceholder.typicode.com/posts/1&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="nf"&gt;then&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;  &lt;span class="c1"&gt;// userId value is 1&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;equal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Pass! (because id == 1)&lt;/span&gt;
        &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;// id set to 2&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;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Pass! (because id == 2)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;equal&lt;/span&gt;&lt;span class="dl"&gt;'&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;// Will Fail!!! (because id == null)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well... since Cypress commands are added to the queue in the order they are defined in the test, that final assertion should be executed at the end of the test, right?&lt;/p&gt;

&lt;p&gt;Let's see the diagram:&lt;/p&gt;

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

&lt;p&gt;And that is exactly what happens! The Cypress assertion at the very end of the test is executed last, &lt;strong&gt;BUT&lt;/strong&gt; when the command &lt;code&gt;.wrap()&lt;/code&gt; is added to the Cypress queue, the variable &lt;code&gt;id&lt;/code&gt; still holds the value &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Remember? JavaScript statements are executed before Cypress commands, so the JavaScript timeline reaches the end of the test (with the &lt;code&gt;id&lt;/code&gt; variable still set to &lt;code&gt;null&lt;/code&gt;) before any of the earlier Cypress commands are executed. So the test fails!&lt;/p&gt;

&lt;p&gt;And here’s the proof (the Cypress Log never lies!):"&lt;/p&gt;

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

&lt;p&gt;Pesky variable! 😜&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Example Test 6
&lt;/h3&gt;

&lt;p&gt;And finally, the last test example.&lt;/p&gt;

&lt;p&gt;Why don’t we enclose the final Cypress assertion within a &lt;code&gt;cy.then()&lt;/code&gt; command?&lt;/p&gt;

&lt;p&gt;In this case, the &lt;code&gt;.then()&lt;/code&gt; command is not chained to the yielded value of another Cypress command, but is applied directly to the global object &lt;code&gt;cy&lt;/code&gt; (from the Cypress test framework).&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test 6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// id set to null&lt;/span&gt;

    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&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://jsonplaceholder.typicode.com/posts/1&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="nf"&gt;then&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;  &lt;span class="c1"&gt;// userId value is 1&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;equal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Pass! (because id == 1)&lt;/span&gt;
        &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;// id set to 2&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;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Pass! (because id == 2)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Pass! (because id == 2)&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;equal&lt;/span&gt;&lt;span class="dl"&gt;'&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;//Pass! (because id == 2)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach will encapsulate those assertions within a Cypress block, allowing time for the &lt;code&gt;id&lt;/code&gt; variable to be set by the previous commands. Such a technique is a common and recommended practice in Cypress when you want to 'sync' commands with one another.&lt;/p&gt;

&lt;p&gt;We also added, as a bonus, an expect in that final &lt;code&gt;cy.then()&lt;/code&gt; block, and as expected, it is executed before the final Cypress assertion.&lt;/p&gt;

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

&lt;p&gt;Again Javascript synchronous timeline is faster!&lt;/p&gt;

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

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

&lt;h3&gt;
  
  
  And What About Cypress Hooks?
&lt;/h3&gt;

&lt;p&gt;Actually, Cypress hooks are also blocks that wrap JavaScript and Cypress commands, so the same rules apply to them.&lt;/p&gt;

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




&lt;h1&gt;
  
  
  ACT3: RESOLUTION
&lt;/h1&gt;

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

&lt;p&gt;So we’ve reached the end of the exercise!&lt;/p&gt;

&lt;p&gt;Now you understand how Cypress's asynchronous nature really works—don’t be afraid of it. When used wisely, it can be a truly powerful tool. And also, the chained commands are quite elegant.&lt;/p&gt;

&lt;p&gt;Once again, the official Cypress recommendation is to avoid mixing JavaScript synchronous code with Cypress asynchronous commands. However, you now have the knowledge and tools to handle the &lt;strong&gt;&lt;em&gt;The Two Timelines&lt;/em&gt;&lt;/strong&gt; in your Cypress tests—for instance, when debugging a faulty or flaky test written by someone you’ve never met who knows how long ago! 😄&lt;/p&gt;

&lt;p&gt;One tool that might help you debug flaky tests caused by incorrect usage of Cypress's asynchronous nature is the &lt;a href="https://github.com/bahmutov/cypress-command-chain" rel="noopener noreferrer"&gt;cypress-command-chain&lt;/a&gt; plugin, developed by &lt;a href="https://www.linkedin.com/in/bahmutov" rel="noopener noreferrer"&gt;Gleb Bahmutov&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is very easy to install and use. The plugin displays the Cypress queued commands in the Cypress log in the exact order they are inserted, along with the values of the arguments at that moment.&lt;/p&gt;

&lt;p&gt;If we run &lt;strong&gt;Example Test 5&lt;/strong&gt; (from our earlier discussion) with the &lt;code&gt;cypress-command-chain&lt;/code&gt; plugin enabled, this is what you would see in your Cypress log:&lt;/p&gt;

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

&lt;p&gt;At the top of the log, the Cypress Command Queue is displayed, showing each command inserted in order along with its arguments. Below that is the regular Cypress log, which includes the results of the assertions as they are executed.&lt;/p&gt;

&lt;p&gt;You can find all the examples from this article in the GitHub repo: &lt;a href="https://github.com/sclavijosuero/cypress-async-behavior-examples" rel="noopener noreferrer"&gt;sclavijosuero/cypress-async-behavior-examples&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;strong&gt;I'd love to hear from you! Please don't forget to follow me, leave a comment, or a reaction if you found this article useful or insightful.&lt;/strong&gt;&lt;/em&gt; ❤️ 🦄 🤯 🙌 🔥&lt;/p&gt;

&lt;p&gt;You can also connect with me on my new &lt;strong&gt;YouTube channel&lt;/strong&gt;: &lt;a href="https://www.youtube.com/@SebastianClavijoSuero" rel="noopener noreferrer"&gt;https://www.youtube.com/@SebastianClavijoSuero&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to support my work, consider buying me a coffee or contributing to a training session, so I can keep learning and sharing cool stuff with all of you.&lt;br&gt;
Thank you for your support!&lt;br&gt;
&lt;a href="https://www.buymeacoffee.com/sclavijosuero" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cypress</category>
      <category>qa</category>
      <category>automation</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
