Command Timed Out After 3 Seconds on Try 1 of 4 Trying Again With a 6 Second Timeout

A core feature of Cypress that assists with testing dynamic web applications is retry-ability. Similar a practiced manual in a motorcar, information technology usually works without y'all noticing it. But understanding how it works will aid you write faster tests with fewer run-time surprises.

Commands vs assertions

There are 2 types of methods y'all can call in your Cypress tests: commands and assertions. For case, there are 6 commands and 2 assertions in the test below.

                          it              (              'creates ii items'              ,              (              )              =>              {              cy.              visit              (              '/'              )              // command              cy.              focused              (              )              // command              .              should              (              'accept.form'              ,              'new-todo'              )              // assertion              cy.              get              (              '.new-todo'              )              // command              .              type              (              'todo A{enter}'              )              // control              .              blazon              (              'todo B{enter}'              )              // command              cy.              go              (              '.todo-listing li'              )              // command              .              should              (              'have.length'              ,              2              )              // assertion              }              )                      

The Command Log shows both commands and assertions with passing assertions showing in dark-green.

ommands and assertions

Let's expect at the terminal command and assertion pair:

            cy.              get              (              '.todo-list li'              )              // control              .              should              (              'have.length'              ,              2              )              // exclamation                      

Because nothing is synchronous in modern web applications, Cypress tin't query all the DOM elements with the course todo-list and check if at that place are only two of them. There are many examples of why this would non work well.

  • What if the application has not updated the DOM by the fourth dimension these commands run?
  • What if the application is waiting for its back end to respond before populating the DOM element?
  • What if the application does some intensive computation before showing the results in the DOM?

Thus the Cypress cy.get command has to exist smarter and expect the application to potentially update. The cy.get() queries the application'southward DOM, finds the elements that match the selector, and so tries the assertion that follows it (in our instance should('have.length', 2)) against the list of found elements.

  • ✅ If the assertion that follows the cy.become() command passes, then the command finishes successfully.
  • 🚨 If the assertion that follows the cy.get() command fails, then the cy.go() command volition requery the awarding'southward DOM again. Then Cypress will try the exclamation against the elements yielded from cy.go(). If the assertion withal fails, cy.become() volition effort requerying the DOM again, and and so on until the cy.get() command timeout is reached.

The retry-ability allows the tests to complete each command as shortly equally the exclamation passes, without difficult-coding waits. If your application takes a few milliseconds or fifty-fifty seconds to render each DOM element - no big deal, the test does not have to change at all. For example, let's introduce an artificial delay of 3 seconds when refreshing the application'southward UI below in an example TodoMVC model lawmaking:

            app.              TodoModel              .              prototype              .              addTodo              =              role              (              title              )              {              this              .              todos              =              this              .              todos              .              concat              (              {              id              :              Utils              .              uuid              (              )              ,              title              :              title,              completed              :              faux              ,              }              )              // let'due south trigger the UI to return afterward three seconds              setTimeout              (              (              )              =>              {              this              .              inform              (              )              }              ,              3000              )              }                      

My test still passes! The last cy.become('.todo-list') and the assertion should('have.length', two) are clearly showing the spinning indicators, meaning Cypress is requerying for them.

Retrying finding 2 items

Within a few milliseconds subsequently the DOM updates, cy.get() finds two elements and the should('take.length', 2) assertion passes

Multiple assertions

A single command followed past multiple assertions retries each one of them -- in order. Thus when the first assertion passes, the command will be retried with first and second exclamation. When the outset and second assertion pass, then the command will be retried with the beginning, 2d, and third assertion, and so on.

For case, the following test has .should() and .and() assertions. .and() is an alias of the .should() control, and then the second assertion is actually a custom callback assertion in the class of the .should(cb) function with 2 wait assertions within of it.

            cy.              get              (              '.todo-list li'              )              // control              .              should              (              'have.length'              ,              two              )              // exclamation              .              and              (              (              $li              )              =>              {              // two more assertions              expect              ($li.              get              (              0              )              .              textContent              ,              'first detail'              )              .              to              .              equal              (              'todo a'              )              expect              ($li.              get              (              1              )              .              textContent              ,              'second particular'              )              .              to              .              equal              (              'todo B'              )              }              )                      

Because the second assertion expect($li.get(0).textContent, 'first item').to.equal('todo a') fails, the third assertion is never reached. The command fails after timing out, and the Command Log correctly shows that the first encountered assertion should('have.length', 2) passed, but the second assertion and the command itself failed.

Retrying multiple assertions

Not every command is retried

Cypress simply retries commands that query the DOM: cy.become(), .find(), .contains(), etc. Y'all can cheque if a item command is retried by looking at the "Assertions" section in its API documentation. For example, "Assertions section" of .first() tells united states of america that the command is retried until all assertions that follow it are passing.

  • .first() will automatically retry until the element(due south) exist in the DOM
  • .first() will automatically retry until all chained assertions accept passed

Why are some commands Non retried?

Commands are not retried when they could potentially change the state of the application under test. For example, Cypress volition non retry the .click() command, because it could change something in the application.

Built-in assertions

Ofttimes a Cypress command has built-in assertions that will cause the command to be retried. For example, the .eq() command will be retried even without whatever fastened assertions until it finds an element with the given alphabetize in the previously yielded list of elements.

            cy.              go              (              '.todo-list li'              )              // command              .              should              (              'have.length'              ,              ii              )              // assertion              .              eq              (              iii              )              // control                      

Retrying built-in assertion

Some commands that cannot be retried nonetheless have built-in waiting. For case, as described in the "Assertions" section of .click(), the click() control waits to click until the element becomes actionable.

Cypress tries to act like a human user would using the browser.

  • Can a user click on the chemical element?
  • Is the chemical element invisible?
  • Is the chemical element backside another chemical element?
  • Does the element have the disabled attribute?

The .click() command volition automatically wait until multiple congenital-in assertion checks like these pass, and then it will attempt to click only once.

Timeouts

By default each command that retries, does so for up to four seconds - the defaultCommandTimeout setting.

Increase time to retry

You tin change this timeout for all commands. Encounter Configuration: Overriding Options for examples of overriding this option.

For instance, to set the default command timeout to 10 seconds via the control line:

            cypress run --config              defaultCommandTimeout              =              10000                      

Nosotros practice non recommend changing the command timeout globally. Instead, pass the individual command'south { timeout: ms } option to retry for a different period of time. For example:

                          // nosotros've modified the timeout which affects default + added assertions              cy.              become              (              '.mobile-nav'              ,              {              timeout              :              10000              }              )              .              should              (              'be.visible'              )              .              and              (              'contain'              ,              'Dwelling'              )                      

Cypress volition retry for up to 10 seconds to notice a visible element of class mobile-nav with text containing "Home". For more than examples, read the Timeouts section in the "Introduction to Cypress" guide.

Disable retry

Overriding the timeout to 0 will essentially disable retrying the command, since it will spend 0 milliseconds retrying.

                          // cheque synchronously that the chemical element does not be (no retry)              // for example just after a server-side return              cy.              get              (              '#ssr-error'              ,              {              timeout              :              0              }              )              .              should              (              'non.be'              )                      

Simply the last command is retried

Here is a short test that demonstrates some scrap.

                          it              (              'adds two items'              ,              (              )              =>              {              cy.              visit              (              '/'              )              cy.              become              (              '.new-todo'              )              .              type              (              'todo A{enter}'              )              cy.              get              (              '.todo-listing li'              )              .              observe              (              'label'              )              .              should              (              'contain'              ,              'todo A'              )              cy.              go              (              '.new-todo'              )              .              type              (              'todo B{enter}'              )              cy.              get              (              '.todo-list li'              )              .              find              (              'label'              )              .              should              (              'contain'              ,              'todo B'              )              }              )                      

The test passes in Cypress without a hitch.

Test passes

But sometimes the test fails - not ordinarily locally, no - it almost ever fails on our continuous integration server. When the test fails, the recorded video and screenshots are NOT showing any obvious issues! Here is the declining examination video:

Test fails

The trouble looks weird - I can clearly come across the label "todo B" nowadays in the list, and so why isn't Cypress finding information technology? What is going on?

Call up the delay we introduced into our application code that causes the examination to time out? We added a 100ms delay earlier the UI rerenders itself.

            app.              TodoModel              .              paradigm              .              addTodo              =              function              (              championship              )              {              this              .              todos              =              this              .              todos              .              concat              (              {              id              :              Utils              .              uuid              (              )              ,              title              :              title,              completed              :              false              ,              }              )              setTimeout              (              (              )              =>              {              this              .              inform              (              )              }              ,              100              )              }                      

This filibuster could exist the source of our flaky tests when the awarding is running on our CI server. Hither is how to run across the source of the problem. In the Command Log, hover over each command to meet which elements Cypress institute at each step.

In the failing test, the first label was indeed establish correctly:

First item label

Hover over the second "Notice characterization" command - something is wrong here. It found the get-go characterization, then kept requerying to detect the text "todo B", merely the first detail always remains "todo A".

Second item label

Hmm, weird, why is Cypress only looking at the beginning particular? Permit's hover over the "GET .todo-list li" control to audit what that command found. Ohh, interesting - there was only one item at that moment.

Second get li

During the test, the cy.become('.todo-listing li') command quickly found the rendered <li> item - and that detail was the first and only "todo A" item. Our application was waiting 100ms before appending the 2nd detail "todo B" to the list. By the time the second item was added, Cypress had already "moved on", working simply with the beginning <li> element. Information technology only searched for <label> inside the first <li> element, completely ignoring the newly created second item.

To confirm this, let's remove the artificial delay to see what'due south happening in the passing test.

Two items

When the web application runs without the delay, information technology gets its items into the DOM before the Cypress command cy.get('.todo-list li') runs. After the cy.become() returns 2 items, the .find() command just has to observe the correct label. Groovy.

Now that we sympathize the existent reason behind the flaky exam, we need to call back about why the default retry-ability has non helped us in this situation. Why hasn't Cypress institute the 2 <li> elements after the 2d 1 was added?

For a variety of implementation reasons, Cypress commands merely retry the last control before the exclamation. In our test:

            cy.              become              (              '.new-todo'              )              .              type              (              'todo B{enter}'              )              cy.              go              (              '.todo-list li'              )              // queries immediately, finds one <li>              .              find              (              'label'              )              // retried, retried, retried with 1 <li>              .              should              (              'contain'              ,              'todo B'              )              // never succeeds with only 1st <li>                      

Utilise retry-ability correctly

Luckily, one time we understand how retry-power works and how just the concluding control is used for assertion retries, we can fix this test for practiced.

Merging queries

The offset solution nosotros recommend is to avoid unnecessarily splitting commands that query elements. Instead of cy.get('.todo-list li').find('label') nosotros can combine two divide queries into 1 - forcing the combined query to be retried.

                          it              (              'adds two items'              ,              (              )              =>              {              cy.              visit              (              '/'              )              cy.              get              (              '.new-todo'              )              .              type              (              'todo A{enter}'              )              cy.              get              (              '.todo-list li label'              )              // 1 query command              .              should              (              'contain'              ,              'todo A'              )              // assertion              cy.              get              (              '.new-todo'              )              .              type              (              'todo B{enter}'              )              cy.              get              (              '.todo-list li label'              )              // 1 query command              .              should              (              'contain'              ,              'todo B'              )              // assertion              }              )                      

To show the retries, I increased the awarding's bogus delay to 500ms. The test at present always passes because the entire selector is retried. Information technology finds ii listing elements when the 2nd "todo B" is added to the DOM.

Combined selector

Similarly, when working with deeply nested JavaScript properties using the .its() command, try not to split it across multiple calls. Instead, combine belongings names into a single telephone call using the . separator:

                          // 🛑 not recommended              // only the terminal "its" will be retried              cy.              window              (              )              .              its              (              'app'              )              // runs once              .              its              (              'model'              )              // runs once              .              its              (              'todos'              )              // retried              .              should              (              'have.length'              ,              2              )              // ✅ recommended              cy.              window              (              )              .              its              (              'app.model.todos'              )              // retried              .              should              (              'accept.length'              ,              2              )                      

See the Fix flag to start tests blog for the total instance.

Alternate commands and assertions

In that location is some other style to set our flaky examination. Whenever you write a longer test, we recommend alternating commands with assertions. In this example, I will add an assertion after the cy.get() control, but earlier the .find() control.

                          it              (              'adds ii items'              ,              (              )              =>              {              cy.              visit              (              '/'              )              cy.              get              (              '.new-todo'              )              .              type              (              'todo A{enter}'              )              cy.              get              (              '.todo-listing li'              )              // control              .              should              (              'accept.length'              ,              1              )              // assertion              .              detect              (              'characterization'              )              // command              .              should              (              'contain'              ,              'todo A'              )              // assertion              cy.              get              (              '.new-todo'              )              .              type              (              'todo B{enter}'              )              cy.              get              (              '.todo-list li'              )              // command              .              should              (              'have.length'              ,              ii              )              // exclamation              .              discover              (              'label'              )              // command              .              should              (              'comprise'              ,              'todo B'              )              // assertion              }              )                      

Passing test

The examination passes, because the 2d cy.get('.todo-list li') is retried with its own assertion at present .should('take.length', ii). But later successfully finding two <li> elements, the command .detect('label') and its assertion starts, and past now, the item with the right "todo B" label has been correctly queried.

Utilise .should() with a callback

If you accept to utilize commands that cannot exist retried, but need to retry the entire chain, consider rewriting the commands into a single .should(callbackFn) chained off the very outset retry-able command.

Below is an example where the number value is set up afterwards a delay:

                                                            <div                grade                                  =                  "random-number-example"                                >                            Random number:                                                <bridge                id                                  =                  "random-number"                                >              🎁                                  </span                >                                                              </div                >                                                              <script                >                                                              const                  el                  =                  document                  .                  getElementById                  (                  'random-number'                  )                  setTimeout                  (                  (                  )                  =>                  {                  el.                  innerText                  =                  Math                  .                  flooring                  (                  Math                  .                  random                  (                  )                  *                  x                  +                  one                  )                  }                  ,                  1500                  )                                                                              </script                >                                    

Random number

Incorrectly waiting for values

You may want to write a test like below, to exam that the number is betwixt 1 and ten, although this volition not piece of work as intended. The examination yields the following values, noted in the comments, before failing.

                          // WRONG: this test will not work as intended              cy.              go              (              '#random-number'              )              // <div>🎁</div>              .              invoke              (              'text'              )              // "🎁"              .              and then              (parseFloat)              // NaN              .              should              (              'be.gte'              ,              one              )              // fails              .              and              (              'be.lte'              ,              10              )              // never evaluates                      

Unfortunately, the .then() command is not retried. Thus the test simply runs the unabridged chain once before failing.

First attempt at writing the test

Correctly waiting for values

Nosotros need to retry getting the element, invoking the text() method, calling the parseFloat role and running the gte and lte assertions. We can achieve this using the .should(callbackFn).

            cy.              get              (              '#random-number'              )              .              should              (              (              $div              )              =>              {              // all the lawmaking within here will retry              // until it passes or times out              const              due north              =              parseFloat              ($div.              text              (              )              )              look              (n)              .              to              .              be              .              gte              (              1              )              .              and              .              be              .              lte              (              10              )              }              )                      

The higher up examination retries getting the element and invoking the text of the chemical element to get the number. When the number is finally set in the application, then the gte and lte assertions laissez passer and the test passes.

Random number using callback

Use aliases

When using cy.stub() or cy.spy() to test awarding'due south code, a good practice is to give information technology an alias and use the cy.get('@allonym').should('...') assertion to retry.

For instance, when confirming that the button component invokes the click prop testing with the @cypress/react plugin, the post-obit test might or might not work:

Incorrectly checking if the stub was chosen

                          const                              Clicker                            =              (                              {                click                }                            )              =>              (              <div>              <button onClick=              {click}              >              Click              me<              /button>              <              /div>              )              information technology              (              'calls the click prop twice'              ,              (              )              =>              {              const              onClick              =              cy.              stub              (              )              // "mount" function comes from              // https://github.com/cypress-io/cypress/tree/master/npm/react              mount              (              <              Clicker              click=              {onClick}              /              >              )              cy.              get              (              'push'              )              .              click              (              )              .              click              (              )              .              then              (              (              )              =>              {              // works in this example, but non recommended              // because .and so() does non retry              expect              (onClick)              .              to              .              be              .              calledTwice              }              )              }              )                      

The in a higher place example will fail if the component calls the click prop after a delay.

                          const                              Clicker                            =              (                              {                click                }                            )              =>              (              <div>              <button onClick=              {              (              )              =>              setTimeout              (click,              500              )              }              >              Click              me<              /button>              <              /div>              )                      

Expect fails the test without waiting for the delayed stub

The test finishes before the component calls the click prop twice, and without retrying the assertion expect(onClick).to.be.calledTwice.

Correctly waiting for the stub to exist called

Nosotros recommend aliasing the stub using the .as command and using cy.get('@alias').should(...) assertions.

                          it              (              'calls the click prop'              ,              (              )              =>              {              const              onClick              =              cy.              stub              (              )              .              as              (              'clicker'              )              // "mountain" function comes from              // https://github.com/cypress-io/cypress/tree/master/npm/react              mount              (              <              Clicker              click=              {onClick}              /              >              )              cy.              get              (              'button'              )              .              click              (              )              .              click              (              )              // good practice 💡              // auto-retry the stub until it was called twice              cy.              get              (              '@clicker'              )              .              should              (              'have.been.calledTwice'              )              }              )                      

Retrying the assertions using a stub alias

Picket the brusk video below to see this instance in action

See as well

  • Read our blog posts about fighting the test flake.
  • You can add together retry-ability to your own custom commands; encounter this pull request to cypress-xpath for an example.
  • You tin retry any function with fastened assertions using the third party plugins cypress-pipe and cypress-wait-until.
  • third party plugin cypress-recurse can be used to implement the visual testing with retry-power for canvas elements
  • See retry-ability examples in the Cypress should callback blog postal service.
  • To learn how to enable Cypress' examination retries functionality, which retries tests that fail up to the configured number, check out our official guide on Test Retries.

longgreasse.blogspot.com

Source: https://docs.cypress.io/guides/core-concepts/retry-ability

0 Response to "Command Timed Out After 3 Seconds on Try 1 of 4 Trying Again With a 6 Second Timeout"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel