You're talking about the difference between active and passive element checking.
Generally speaking, active waiting for the notification is better
cy.contains('[data-e2e-notification-message-text]', 'ERROR: ', {timeout: 7000})
.should('not.exist')
than passive waiting
cy.on('notification', (message) => { // NOT REAL CODE - for illustration
if (message.includes('ERROR: ')) {
throw 'Notification occurred' // fail the test
}
})
because the chain of events involves asynchronous call from the backend, which can vary in timing. If the test ends before the notification arrives, you get a false positive test.
You can set up an intercept if the action is request/response style, e.g
cy.intercept(...).as('notification') // listen
cy.get(button).click() // action
cy.wait('@notification') // assert
Add a stub if the live server is slow
cy.intercept(url, { notification: 'Error: ' }) // immediately fake response
cy.get(button).click() // action
cy.contains('[data-e2e-notification-message-text]', 'ERROR: ') // assert
Or don't explicitly wait
cy.intercept(url, (req) => {
req.on('response', (res) => {
if (res.body.includes('Error:')) {
throw 'Notification of error' // fail the test
}
})
cy.get(button).click() // action
But also a chance of false positive result depending on timing.
If you did have a use case for periodic checking for an element, you could tap into the command:end event.
You can only do static (jQuery) DOM querying in an event handler (no cy.() commands).
// after every command look for notification
Cypress.on('command:end', () => {
const notification = Cypress.$('[data-e2e-notification-message-text]:contains(ERROR:)')
if (notification.length) {
throw 'Notification of error happened' // fail the test
}
})
Same caveat applies - this could be flaky if the test is faster than the notification.