cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
46.41k stars 3.14k forks source link

Expose and document Chrome DevTools Protocol #7942

Open jennifer-shehane opened 3 years ago

jennifer-shehane commented 3 years ago

Current behavior:

Currently there is no documented way to access the Chrome DevTools Protocol.

This actually is possible today, although it's undocumented using the method below.

Cypress.automation("remote:debugger:protocol", {

})

Desired behavior:

People have requested that they'd like direct access to the Chrome DevTools Protocol. Would want this documented so people could use it. Some examples of usage are below:

This feature needs to support:

Additionally, there's no cross-browser support to this, so that's a challenge.

Versions

4.10.0

avallete commented 3 years ago

I did used this into my cypress-nhover package. I could try to add some documentation about it.

PS: since Firefox seem to implement CDP as well (cf: https://github.com/firefox-devtools/rust-cdp, https://firefox-source-docs.mozilla.org/remote/) it may be worthy to create a new issue to be able to use it into cypress too.

froatsnook commented 3 years ago

Being able to run one test in American Samoa, the next in UK, and the next in Fiji would be useful for my use case (I had a regression where recurring events showed on the wrong dates in some time zones). Using the undocumented remote:debugger:protocol works great (although it seems you can only do Emulation.setTimezoneOverride once before you get a Unhandled rejection Error: Timezone override is already in effect error in $Cypress.automation).

Sathish787 commented 3 years ago

Could suppress exception/unhandled rejection by placing code in support/command.js

Cypress.on('uncaught:exception', (err, runnable) => {
    return false
})

Mocking only timezone in cypress and not to mix with locale (mocking language). It is better to create separate custom command for locale to avoid error. Paste below code in support/command.js

Cypress.Commands.add('stubBrowserTimezone', timeZone) {
         return new Promise((resolve) => {
        return resolve(
            Cypress.automation("remote:debugger:protocol", {
                command: "Emulation.setTimezoneOverride",
                params: {
                    timezoneId: timeZone
                }                
            })
        )
    })
}

Then, in test file, use it like:

cy.stubBrowserTimezone("Europe/London").then(() => {
    cy.log(new Date().toString())
        test steps.... should be inside loop...
})
flotwig commented 3 years ago

PS: since Firefox seem to implement CDP as well (cf: firefox-devtools/rust-cdp, firefox-source-docs.mozilla.org/remote) it may be worthy to create a new issue to be able to use it into cypress too.

@avallete now that CDP is available in FF stable as of FF86, look out for this coming soon

flotwig commented 3 years ago

Here is an API that I think could work nicely for this:

/**
 * Send a CDP command with optional parameters and receive the result.
 */
Cypress.CDP.send = (method: string, params?: any) => Promise<CDPResult>
/**
 * Subscribe to a CDP event. Some events must be enabled before being listened to.
 */
Cypress.CDP.on = (method: string, cb: (params: any) => void)) => void

I don't see a need to integrate it with Cypress-isms like the chainable, since it is somewhat of a low-level API that may not make sense to write in that way. Any thoughts?

EDIT: Also, we probably need a way to select targets, or default to the test frame target.

nileshgulia1 commented 2 years ago

Its now possible to simulate events like "copy to clipboard" using CDP : https://github.com/dmtrKovalenko/cypress-real-events

shwarcu commented 2 years ago

@jennifer-shehane @flotwig is this topic parked/abandoned?

flotwig commented 2 years ago

@shwarcu This is still something we'd like to add to Cypress, but it is not currently being worked on.

If you're interested in starting a PR to add this, this comment describes a partial API, but we'd also need some way to select Targets.

BloodyRain2k commented 8 months ago

Mocking only timezone in cypress and not to mix with locale (mocking language). It is better to create separate custom command for locale to avoid error. Paste below code in support/command.js

Cypress.Commands.add('stubBrowserTimezone', timeZone) {
         return new Promise((resolve) => {
        return resolve(
            Cypress.automation("remote:debugger:protocol", {
                command: "Emulation.setTimezoneOverride",
                params: {
                    timezoneId: timeZone
                }                
            })
        )
    })
}

This command has been a huge help with the project I'm working on. Thanks a lot for sharing it. I was especially excited when I found out that it's not required to run the test commands inside the .then(), as that would have meant a major overhaul of my already existing tests. Just calling the command in beforeEach() works just as well. And considering that the setting of the timezone is, for whatever reason, only allowed once anyways, it makes more sense this way too.

I did however seem to have found an edge-case where this might cause issues:

I had a literal unit-test set for my project, which are the only tests that can actually be measured in "ms" (avg. <100ms), unlike all others which could as well be just measured in 1/10ths of minutes. After permutating the original ~300 tests to ~1100 by just running the original set in 4 different timezones, I started seeing random errors that said nothing more than "beforeEach() failed".

I tried finding an actual cause for this, but after an hour I just gave up because there was simply no pattern to be found, nor any details about what actually broke. Sometimes it barely got past 100 before breaking, other times it got past 500 before throwing the first non-descriptive error.

I then went with a "gut feeling" and restructured the way the tests were grouped, so that beforeEach() would be called less than 10-20x a second, and after having now ~110 tests that take around 150-200ms each, the error has not yet appeared again.

In my case it would be ideal if the timezone would be just another "option" for describe/it, like "env" is. So it would be possible to set up a set like describe("Timezone tests", { tz: "America/NewYork" }, () => { ... }) and just have the entire set run with that timezone.

But that's just QoL for "eventually". For now I just wanted to leave this here in case anyone else manages to run into this issue, so they at least have one approach of trying to prevent it.

skitterm commented 7 months ago

@jennifer-shehane while we await Cypress CDP wrappers, could you share an example of listening for a CDP event?

Use case I'd like to intercept network requests using CDP (I know there's cy.intercept(), but I'd like to reuse this code across testing frameworks so CDP would be preferable). I can currently send the fetch.enable method

Cypress.automation("remote:debugger:protocol", {
    command: "Fetch.enable",
    params: {}
})

but I don't see a way to listen to events like Fetch.requestPaused or Network.responseReceived with the current API (to get the response status code and body, for example). Is there a way to receive CDP events currently, or is the current Cypress.automation('remote:debugger:protocol') approach only for calling CDP methods?

fabianeichfeldt commented 2 months ago

Are there any news about listening to CDP events?

I would like to use CDP for memory analysis and taking HeapSnapshots, therefore I need to listen to events

RomanDavlyatshin commented 2 months ago

Are there any news about listening to CDP events?

I would like to use CDP for memory analysis and taking HeapSnapshots, therefore I need to listen to events

Hey Fabian, its been a long time, so my memory on the issue is somewhat vague, but I have managed to use CDP without issue following the code sample provided by OP (although I may did some digging in Cypress sources)

Here is the example on some CDP calls I used:

// in supportfile.js  (utlized by Cypress to setup all kinds of stuff related to tests execution, including before/after hooks)

// before all tests - enable desired CDP domains
before((done) => {
  // ... some unrelated promises code above
  .then(() => Cypress.automation('remote:debugger:protocol', { command: 'Profiler.enable', params: {} }))
  .then(() => Cypress.automation('remote:debugger:protocol', { command: 'Profiler.startPreciseCoverage', params: {
    callCount: false,
    detailed: true
  }}))
  .then(()=>done());
});

// Take metrics after each test
afterEach(function (done) {
  const currentTest = this.currentTest;
  Cypress.automation('remote:debugger:protocol', { command: 'Profiler.takePreciseCoverage', params: {}})
    .then(async function (cdpCommandData) {
      // do something cool with the data
    })
    .then(()=>done());  
});

That should work in a similar fashion for Profiler heap usage and all other domains. Hope that helps