Cypress: Support browser specific protocols (chrome extensions)

Created on 17 Jun 2018  路  32Comments  路  Source: cypress-io/cypress

Support cy.visit() with protocols other than http/https such as:

  • chrome://
  • chrome-extension://
  • resource://

Most notably, chrome-extension:// will allow users to test the UI of a chrome extension.

5锔忊儯 proposal 馃挕 feature

Most helpful comment

I belive there should be way to test extension in real Chrome. Not under web server, not stubbing anything. With doing all this extra work, there is huge space to introduce new bugs and worse - to miss some real bugs.

I didn't check the source code of Cypress or Chrome so I'm really not sure how it is working under the hood. But isn't it possible to navigate to chrome:// and chrome-extension:// in exact same way as to http://? Isn't it only alias to http and working over http protocol?

Btw. I started to play with Cypress only because I need to test extension, I wouldn't give it a try if it wouldn't look like it can do this from docs. As other tools are lacking in this area too, it my be great to provide better support for extension authors.

All 32 comments

There's also Add support for testing chrome extensions but it's closed.
I've added a comment because I believe the issue is still happening.

@JulianG that issue was for testing content scripts, which is currently possible. But yeah this is the other half of Chrome extensions

Would love to see support for this added!

I鈥檇 be very keen to see this - you can already make a fair bit of headway with extensions, but this would be the icing on the cake.

I would love to see this ability. It would be so great.

Curious, which pages are you most wanting to test? background.html? popup.html?
And you need access to the chrome.* apis correct?
I'm trying to figure out the scope of work here, because embedding the page in an iframe while maintaining access to chrome.* privileged apis might be tricky

@Bkucera For my own use case, I would be able to run Cypress on my extension's popup in order to see if:

  • it actually renders
  • some dynamic elements are present
  • some links are present too
  • internal tabs navigation (vue-router) works

I tried cy.visit('chrome-extension://.../popup.html') but it doesn't work due to invalid protocol.

Also, background.html is literally never used because it's a auto-generated page when you use a single background.js (probably ~99% of the time), but it would be nice for running Cypress on options.html page. :+1:

Having access to chrome* API would be awesome (e.g.: to check if a notification has been triggered, sync, ...), or maybe we can manually stub chrome* methods at the moment?

Thanks you for taking time on this. :slightly_smiling_face:

@Bkucera Exactly. My chrome extension uses chrome.windows.* and chrome.tabs.* and in order to display in a list and manipulate well... windows and tabs.
I'm currently in the process of writing a fake (as in not a mock nor stub) chrome api so I can test in Cypress, but I would very much prefer to test the real thing.

(In my case it's neither background.html nor popup.html. My background.js uses chrome api to open a window containing index.html.)

In my case I want to test via cy.visit('chrome-extension://.../popup.html') or if possible by using the keyboard hotkeys to trigger the extension. In my case the hotkey causes the background.js to run a script that attaches the HTML for the extension to the DOM. My extension is used to do tab switching so the user could switch to any tab they have open. I also use the history and sessions APIs.

One thing I had hoped to do was call cy.visit a couple times to get cypress to load some browsing history and then run my extension to test the calls to the APIs but I would be willing to settle for stubbing their responses in cypress.

Thanks everyone for the input. I think the use case of @Kocal can currently be done by stubbing chrome.* apis and running a simple http server in the folder with manifest.json. Then you can just cy.visit http://localhost:8080/popup.html and this should be very similar to visiting chrome-extension://...
I have done this before and you can change the viewport to be something more similar to popup size

My popup is now successfuly tested, thanks you all! :sunglasses:

capture d ecran de 2018-08-24 17-26-59

Some tips for people who wants to spec their extension's views and mocking chrome* API:

  • Build your extension (e.g.: output in dist folder)
  • Run a local server pointing on your dist folder (serve is pretty nice)
  • Update your Cypress configuration:
{
  "baseUrl": "http://localhost:5000"
}
  • Into your spec:
describe('App', () => {
  before(() => {

    // Load your popup
    cy.visit('/popup/popup.html', {

      // If you need to stub `chrome*` API, you should do it there:
      onBeforeLoad(win) {
        win.chrome = win.chrome || {};
        win.chrome.runtime = {
          sendMessage(message, cb) {
            // ...
            cb(some_data);
          },
        };
      },
    });
  });
});

Have you stubbed chrome.* in the end?
I started making a fake implementation of chrome.windows.* and chrome tabs.* which is what I use. Do you know if such thing already exists?

Well, I only use chrome.runtime.sendMessage inside my popup view, I didn't take time for other methods :confused:

EDIT: some times ago I used jest-webextension-mock for mocking chrome.* in Jest context. Maybe it can helps you

I have used sinon-chrome before, it works well for unit/IT testing. Not sure how it compares to jest-webextension-mock.

For me, the use case would not be testing an extension but testing our app with an extension I want to be able to click some buttons on the extension etc while navigating through our app

I can see both use cases being strong.

Say you're developing an extension that has some UI flows in the popup, it gets pretty annoying to manually test those each time by clicking the extension icon! Regressions are so hard to notice because of the nature of the popup. With Cypress interface, I'd spot the regressions by just running the test command, and notice exactly where it happens.

On the other side, you're obviously developing an extension to enhance a browser -- and websites as a result -- so running an "example site" inside Cypress with the extension loaded could be useful to notice things like what the extension might inject, etc. like what @Moejoe90 is suggesting.

@Bkucera is this being actively worked on? Is there anything the community can do to support?

@callmenick I'm working on testing a chrome extension that injects html into a third party site, but no Test Runner features are being developed yet.

Most likely I'll end up using something from here to allow the extension to detect content in the AUT (application under test), which cypress puts in an iframe

Cool, thanks for the update. I'm personally interested in the first point, supporting extension protocols so I can visit chrome-extension://id/index.html and run tests from there. Is there any roadmap for that?

@callmenick you can check https://github.com/cypress-io/cypress/issues/1965#issuecomment-415796370 I guess

Yeah that's what we're doing now as well @Kocal. Thanks for that!

Hey everyone, I was able to set up a good workflow (see however below*) for testing chrome extensions that inject HTML onto third party sites.

For example, to test an extension that injects HTML inside of Gmail, there were a few things I had to do:

  1. Export the gmail webpages that need testing using this chrome extension: Save Page WE
    (I saved them to cypress/plugins/gmail/, one of my pages is called gmail_home.html)
  2. Host the exported webpages with a express server in plugins/index.js, and it needs to have a self-signed SSL cert: this requires launching cypress with sudo due to needing port 443 (google's port)


    plugins/index.js code:
const path = require('path');

module.exports = (on) => {
  on('task', {
    setUrlPath (url) {
      console.log(url)
      filepath = url
      return null
    }
  })
}

let filepath = 'gmail/gmail_home.html'
const express = require('express')

const PORT = 443

const app = express()
const selfSignedHttps = require('self-signed-https')

app.get('/*', (req, res) => {
  return res.sendFile(filepath, { root: __dirname })  
})

selfSignedHttps(app).listen(PORT, () => console.log(`Server running on port ${PORT}`))

...

  1. add to cypress.json:
"hosts": {
    "mail.google.com": "127.0.0.1",
}
  1. add "all_frames":true to extension's manifest.json
 "content_scripts": [
    {
      "matches": [
        "http://mail.google.com/*",
        "https://mail.google.com/*"
      ],
      "js": [
        ...
      ],
      "css": [
        ...
      ],
      "all_frames": true
    }
  1. Finally, load the extension into Cypress by modifying the plugins/index.js file as shown here: https://docs.cypress.io/api/plugins/browser-launch-api.html#Usage

:+1:

However:

Cypress cy.route stubbing will not affect traffic originating in a chrome extension, so there is more work to do before that can be used.

This will be fixed by Full Network stubbing, and is on our roadmap: #687

Similar to @callmenick above, I'm curious about whether or not Cypress can support simply visit a HTML page from a Chrome extension. If an extension has an HTML page listed in its web_accessible_resources it can be loaded in an iframe without too much trouble. That manifest entry, coupled with the --load-extension=$folder flag could allow a very basic UI test of a Chrome extension. Is this something Cypress could support?

@Bkucera any idea on when this might happen? Asking since it'll influence our testing roadmap.

Thanks for all your hard work :)

@bdresser the main hurdle to this is #687 , and I have no timeline for that; however I'm not exactly sure what you are testing. could you describe your use case like others have above?

Thanks for the information thread and guidance, @Bkucera , along with the example!

To fully test my Chrome extension, I want to be mocking out a variety of API responses, including 2xx, 4xx, and 5xx responses from my API. If I understand correctly, I cannot currently do this with Cypress by itself. There is also no way you can easily think of to use some other library to mock it out that will work with running tests in Cypress, correct?

I think I can accomplish this by using Puppeteer directly, as described in this SO answer and using nock to mock the API responses.

Is chrome-extension protocol working?
It fails with error > Error: Invalid protocol: chrome-extension:

I also have a use case where I would like to use Cypress, but looking at this issue, wont work. I would like to test a chrome extensions UI that communicates to a webpage using chrome.runtime.connect(extID, portName). The extension has a state stored in redux using webext-redux to share the store, which I think needs to run from within an extension and wont work by hosting the files locally. Being able to test pages starting with chrome-extension:// would be amazing.

I belive there should be way to test extension in real Chrome. Not under web server, not stubbing anything. With doing all this extra work, there is huge space to introduce new bugs and worse - to miss some real bugs.

I didn't check the source code of Cypress or Chrome so I'm really not sure how it is working under the hood. But isn't it possible to navigate to chrome:// and chrome-extension:// in exact same way as to http://? Isn't it only alias to http and working over http protocol?

Btw. I started to play with Cypress only because I need to test extension, I wouldn't give it a try if it wouldn't look like it can do this from docs. As other tools are lacking in this area too, it my be great to provide better support for extension authors.

@Bkucera

the main hurdle to this is #687 , and I have no timeline for that; however I'm not exactly sure what you are testing. could you describe your use case like others have above?

Since #687 landed in the meantime, would you say there are other major blockers? I'd be interested in working on making this happen and some rough pointers would be appreciated.

https://github.com/cypress-io/cypress/issues/687 is actually not a prerequisite to delivering this feature. There was a misunderstanding previously on the scope of that issue.

@jennifer-shehane Thanks for the info. Do you by chance know if there's something important missing currently which would make implementing this feature especially hard? Asking because it's tagged with "difficulty 5".

As of now is there a robust and reliable method for testing a chrome extension - i.e. is it possible for Cypress to open the extension window/popup and interact with the UI?
I would require a test to initially start on a website and in the course of testing, it will send requests and receive responses from the extension based on user input on both the site and extension.

_Example scenario:_
Site: user clicks on a button
Extn: receives request from site
User: Navigates to extension to confirm/deny request
Extn: returns response based on user input
Site: handles response

Was this page helpful?
0 / 5 - 0 ratings