You can find the sample app in index.html and all tests in cypress/integration/spec.js. The tests spy / stub Notification function in various ways to check how the app handles:
- permission was granted before
- permission was denied before
- permission was neither granted nor denied before, so the app asks the user and acts depending on the answer
The first test just checks that the browser supports notifications
it('are supported by the test browser', () => {
cy.visit('index.html')
cy.window().should('have.property', 'Notification').should('be.a', 'function')
})
If you enable notifications from Cypress itself, you will see a popup if you click "Notify me!" button.
The rest of the tests stubs Notification constructor to avoid popups
Application code
// Let's check if the browser supports notifications
if (!("Notification" in window)) {
alert("This browser does not support desktop notification");
return
}
Test
it('shows alert if the browser does not support notifications', () => {
cy.visit('index.html', {
onBeforeLoad (win) {
delete win.Notification
},
})
cy.on('window:alert', cy.stub().as('alerted'))
cy.get('button').click()
cy.get('@alerted').should('have.been.calledOnce')
.and('have.been.calledWith', 'This browser does not support desktop notification')
})
cy.stub(win, 'Notification')
and not cy.spy(win, 'Notification')
because the Sinon.js spy Cypress creates does not call the original function with new
keyword.
Cypress issue #6129
To solve the above problem, we can wrap the native Notification
constructor with our own function that always calls the problematic constructor using new
keyword.
it('spying on Notification via workaround', () => {
cy.visit('index.html', {
onBeforeLoad (win) {
// let's wrap Notification constructor
// to make sure it is always called with "new" keyword
const _Notification = win.Notification
win.Notification = function MockNotification (text) {
// use "new" when calling true Notification
return new _Notification(text)
}
// and copy the rest of the important properties
win.Notification.requestPermission = _Notification.requestPermission
win.Notification.permission = 'granted'
// now spy on the wrapped Notification method
cy.spy(win, 'Notification').as('Notification')
},
})
cy.get('button').click()
cy.get('@Notification')
.should('have.been.calledWithNew')
.and('have.been.calledWithExactly', 'Permission was granted before')
.and('have.been.calledWithNew')
})
See the actual notification popup during test visible in the screenshot.
- Cypress guide to Stubs, spies and clocks
- Read Mozilla Notification page
cy.stub