61

Is it possible to wait until an element is visible?

cy.get('[data-test=submitIsVisible]').should('be.visible');

This should error if the submit button is not visible. I want to wait until the submit button is visible.

The primary use case is visual testing, i.e. taking a screenshot of the page.

SSpall
  • 139
  • 1
  • 8
KayakinKoder
  • 3,243
  • 3
  • 23
  • 37
  • 4
    Are you saying this does not work? It looks like it should. Can you give more context, like what the page HTML is and what causes the button to go from invisible to visible. –  Feb 04 '20 at 21:10
  • 2
    @MarionMorrison I just misread the docs, if the accepted answer below is correct (I haven't fully tested) then that's great – KayakinKoder Feb 05 '20 at 02:25
  • I still find it astounding that years after this question was posted, which has clearly helped a lot of folks, someone decided this question should be closed. This is yet another example of why many consider SO to be "toxic" – KayakinKoder May 30 '23 at 14:54

4 Answers4

117

You can wait for the element to be visible like so:

// Give this element 10 seconds to appear
cy.get('[data-test=submitIsVisible]', { timeout: 10000 }).should('be.visible');

According to Cypress's Documentation:

DOM based commands will automatically retry and wait for their corresponding elements to exist before failing.

Cypress offers you many robust ways to query the DOM, all wrapped with retry-and-timeout logic.

Another way to wait for an element’s presence in the DOM is through timeouts. Cypress commands have a default timeout of 4 seconds, however, most Cypress commands have customizable timeout options. Timeouts can be configured globally or on a per-command basis. Check the customizable timeout options list here.

In some cases, your DOM element will not be actionable. Cypress gives you a powerful {force:true} option you can pass to most action commands.

Caveat:

As Anthony Cregan pointed out, the .should('be.visible') assertion checks whether an element is visible on the page, not necessarily in the viewport. This means that this assertion will return true even if the element is not within the visible area of the screen when the test is run.

Further recommended readings:

Manuel Abascal
  • 5,616
  • 5
  • 35
  • 68
  • 6
    I've tested a negative and .should('be.visible') seems to mean it is visible on the page (not necessarily the viewport). I thought it worth pointing that out as I am testing a scrollIntoView implementation and this returned true even when the element was not in the viewport. – Anthony Cregan Apr 07 '21 at 12:20
  • I also used {timeout: xxxxx} as second parameter. – Wirat Leenavonganan Jan 18 '22 at 01:39
43

Updated for Cypress v12

If you want to see exactly how Cypress waits for something to become visible, follow this example.

Using this code, you can check out how the delay and the timeout can affect the passing or failing of the .should('be.visible') assertion.

Steps

  1. Add a simple page to a VSCode project containing Cypress v12.1.0 Call it index.html

    <html>
      <body>
        <h2>API fetched data</h2>
        <span>will become visible here</span>
      </body>
      <script>
        fetch('https://jsonplaceholder.typicode.com/posts/1')
          .then(response => response.json())
          .then(data => document.querySelector('span').innerText = data.title )
      </script>
    </html>
    
  2. Right-click index.html and choose "Open with Live Server" to activate the page.

  3. Add this test to see how Cypress waits for the API data

    describe('test the waiting of API data', () => {
    
      const timings = [
        { delay: 0, timeout: 4000 },     // default, passes
        { delay: 2000, timeout: 4000 },  // passes 
        { delay: 4000, timeout: 4000 },  // flaky
        { delay: 5000, timeout: 4000 },  // fails
        { delay: 5000, timeout: 10000 },  // passes
      ]
    
      timings.forEach(timing => {
    
        const {delay, timeout} = timing;
    
        it(`delayed API by ${delay} ms, command timout is ${timeout} ms`, () => {
    
          cy.intercept('https://jsonplaceholder.typicode.com/posts/1', (req) => {
            req.continue((res) => res.setDelay(delay))
          })
    
          cy.visit('http://127.0.0.1:5500/index.html')
    
          cy.contains('sunt aut facere', {timeout})
            .should('be.visible')
        })
      })
    })
    

Result

enter image description here

This shows that the longer the delay in receiving the data, the bigger the timeout needed on the visibility assertion.

Lola Ichingbola
  • 3,035
  • 3
  • 16
  • But why? What's the point of that? (It stands to reason that if your timeout is lower than your delay it will fail - it is not visible if it is not there.) – Apollo Jul 04 '23 at 10:02
  • 4
    I like it, it's an excellent reproducible test of how Cypress waits for an element to be visible. – Lyonne Jul 05 '23 at 06:26
  • @lola lchingbola, can we override default command timeout using cypress-wait-until plugin. – Deepak Aug 23 '23 at 14:30
  • No, they are implemented in different places. Think of the timeout as a loop which stops when condition is met - Cypress implements the command timeout, and plugin has it's own loop to handle timeout. – Lola Ichingbola Aug 24 '23 at 10:18
2

you can also do it by passing below script into your cypress.config.js files

e2e: {
    defaultCommandTimeout: 25000,
}

pass defaultCommandTimeout as per your requirement.

Sudheer Singh
  • 555
  • 5
  • 10
-2

Try element.should('have.length.greaterThan', 0).and('be.visible')

GjoDim
  • 7
  • 2