Cypress vs Playwright Advent Calendar Day 9
Waiting for page elements
Our web application starts by loading the data from the backend. API calls take time, so our application “signals” the loaded state by setting a class name on the <BODY> element: something easily observable from an end-to-end test
// app.js internal code
SET_LOADING(state, flag) {
state.loading = flag
if (flag === false) {
// an easy way for the application to signal
// that it is done loading
document.body.classList.add(’loaded’)
}
},You saw the assertions in the previous issue of the calendar
// Playwright
await page.goto(’/’)
// confirm the app has loaded when the app sets
// the “loaded” class on the body element
await page.locator(’body.loaded’).waitFor()
// Cypress
cy.visit(’/’)
// confirm the app has loaded when the app sets
// the “loaded” class on the body element
cy.get(’body.loaded’).should(’be.visible’)This is how Playwright looks when the command waitFor passes
Here is Cypress when the .should(’be.visible’) completes
By default PW waitFor command checks if the element is visible, while cy.get command checks if an element is attached to the DOM of the page. Since cy.get has a built-in existence assertion, it could be used without an assertion to confirm a matching element exists.
// PW and CY methods to assert element is visible 👀
await <locator>.waitFor()
// same as
cy.get(<locator>).should(’be.visible’)
// PW and CY methods to check if element exists 🔍
await <locator>.waitFor({ state: ‘attached’ })
// same as
cy.get(<locator>)This advent calendar is based on my online course “Cypress vs Playwright“ and open-source workshop bahmutov/cypress-workshop-cy-vs-pw. You can find links to the previous advent calendar days in my blog post “Cypress vs Playwright Advent Calendar 2025“.



