Controlling Browser Permissions in Cypress End-to-End Tests

I am excited to release a new open source package cypress-browser-permissions. 🎉 You can view it on GitHub at kamranayub/cypress-browser-permissions.

This package solves a real need when testing more sophisticated applications when using Cypress, the end-to-end testing framework. It helps control the permission level of various browser features such as:

  • Desktop Notifications
  • Geolocation
  • Images
  • Camera
  • Microphone
  • etc.

How to Use It

To get started, you'll need to install the package and you'll need Cypress installed already.

npm i cypress cypress-browser-permissions --save-dev

If this is your first time installing Cypress, you'll need to run it once to generate a project structure:

npx cypress open

Then, you need to initialize the plugin to hook it into Cypress' plugin pipeline. In cypress/plugins/index.js, modify it as follows:

+ const { cypressBrowserPermissionsPlugin } = require('cypress-browser-permissions')

/**
 * @type {Cypress.PluginConfig}
 */
module.exports = (on, config) => {
  // `on` is used to hook into various events Cypress emits
  // `config` is the resolved Cypress config
+ config = cypressBrowserPermissionsPlugin(on, config);
+ return config;
};

Now you will have the ability to control various permissions for Chrome, Edge, and Firefox using Cypress environment variables.

For example, if you want to just set permissions for your project you can do so in cypress.json:

{
  "env": {
    "browserPermissions": {
      "notifications": "allow",
      "geolocation": "allow"
    }
  }
}

The plugin will read the permission settings and apply them when launching the browser. It will also reset between launches since modifying the browser profile is persisted across sessions.

You can read more about supported permissions and values in the README.

Writing an End-to-End Notification Test

So let's try it out! Once I finish my Testing Progressive Web Apps Pluralsight course, it will come with an open source sample app. In the meantime, we can write a basic test to see if permissions are working. This same test is included in the repo.

First, we have an HTML file that uses window.Notification to display a desktop notification:

cypress/html/notification.html

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cypress Notification Test</title>
</head>
<body>
    <script type="text/javascript">
        const n = new window.Notification('test', { body: 'This is a test!' })
        n.addEventListener('show', (e) => {
            window.__CypressNotificationShown = e;
        })
    </script>
</body>
</html>

You can learn more about how the Notification API works but what we are doing is immediately triggering a notification. Once the browser shows the toast, it triggers the show event on the Notification instance. Since Cypress is awesome and we can hook directly into the window object, we set a callback value globally that we can then inspect/wait for in our test.

If you have a blank Cypress project you do not even need a server as Cypress will automatically host the root of the project when there is no other configuration.

Save the notification.html file under cypress/html and then we can visit that page in the test.

We can create a test suite in cypress/integration:

cypress/integration/notification.test.js

import { isPermissionAllowed } from 'cypress-browser-permissions';

describe("notifications", () => {
    it("should be enabled", () => {
        expect(isPermissionAllowed("notifications")).to.be.true;
    })

    // Only test notification showing in "headed" browsers, which also
    // works in CI :tada:
    Cypress.browser.isHeaded && it("should display desktop notification", () => {
    
        // Visit the page we created previously
        cy.visit('/cypress/html/notification.html')
        
        // Wait for the window callback to populate with the event data
        cy.window().its('__CypressNotificationShown').should('exist');
    })
})

Now we can run our tests:

npx cypress open

That's all! If browserPermissions.notifications is set to allow then our test should pass:

And a notification will be shown!

How It Works

In Cypress, you have control over the launch preferences for browsers, so the magic lies in what preferences to pass to each browser.

This topic is not heavily documented as evidenced by this open issue in the Cypress repo I came across while researching this. It has been open since 2018 with no one mentioning the ability to control launch preferences.

Thanks to BrowserStack for documenting some of these permissions as well as these StackOverflow posts:

I was able to piece together the information needed to tackle this with a Cypress plugin. Since each browser family uses different preferences, I thought it would be best to abstract it.

What's Next?

My hope is that this package is actually short-lived and the Cypress team can incorporate these permission settings into the core of the product, since it's such an important feature especially when testing new, modern APIs.

There will be a full sample of using Cypress with this plugin (as well as other black magicks such as bypassing service workers and more!) in my Testing Progressive Web Apps course soon on Pluralsight. It should be released in August, you can follow me there to get notified when it releases. The sample app will be open source on GitHub so you'll be able to reference it 👍