cypress-io / cypress

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

Cypress test works in Chrome but not in the Electron window #26120

Closed styriandev closed 1 year ago

styriandev commented 1 year ago

Current behavior

I tried to update cypress from version 10.6.0 to 12.8.0 and after that I faced some interesting problems that only occur on version 12.x.x. Mainly if we are executing a test in the chrome browser everything works like a charm. However, if we are executing the same test in electron it doesn't work. The test fails already in the beforeEach block which we are using to setup a project. We are doing this using a so called "cypress bridge" which is in general a native window event that triggers some actions in the application. We are using this procedure to make it faster to setup the app for the test.

We needed to make sure that our application is being set up by checking a loader that is displayed. By that we made sure that the loading was done.

In the code that looks like this:

export const openTestProject = (filename = 'someFileWithData') => {
  cy.fixture(filename, 'base64').then(result => {
    // setup the local storage with the data of the fixture so that it can be used in the app 
    window.localStorage.setItem('fileContent', result);
    cy.window().then(wind => {
      const zoomToDefine = 1;
      const customEvent = new CustomEvent('cypressBridgeEvent', { additionalData });
      wind.dispatchEvent(customEvent);
      // App should start loading
      getOverlayLoader().should('exist');
      // App should be finished with loading
      getOverlayLoader().should('not.exist');
    });
  });
};

The bridge on the application side looks like this:

    if (window['Cypress']) {
      window.addEventListener('openProject',  async (event: CustomEvent) => {
        const zoomLevel = event.detail?.zoom;
        ElementNameChecker.resetElementNames();
        IdGenerator.reset();
        this.openProject$.next({
          zoomLevel: zoomLevel
        });
      }, false);
    }

I already tried to change it to set something on the window object and check that attribute in the test but this leads to the same issue. I also tried to reinstall the whole package again and deleted the cypress data in AppData to make sure that no old scripts are in the cache. Is it possible that this happens because of the electron version that we are using? While Chrome and Edge is on V111 Electron is still 106? image

Desired behavior

The test works in the same way like in Chrome

Test code to reproduce

Unfortunally on a clean repo this does not happen. However here is the code that leads to this issue (I hope this helps)

// page object
export const openTestProject = (filename = 'projectname') => {
  cy.fixture(filename, 'base64').then(result => {
    window.localStorage.setItem('fileContent', result);
    window.localStorage.setItem('fileName', filename);
    cy.window().then(wind => {
      const zoomToDefine = 1;
      const customEvent = new CustomEvent('openProject', { detail: { zoom: zoomToDefine } });
      wind.dispatchEvent(customEvent);
      getOverlayLoader().should('exist');
      getOverlayLoader().should('not.exist');
    });
  });
};

// in the test
  before(() => {
    cy.visit('/#/layout');
    openTestProject();
  });

Cypress Version

12.8.0

Node version

Node V18.14.2

Operating System

Windows 10

Debug Logs

Command:   assert
index-da5ba0a6.js:104173 Actual:    [data-cy=overlayLoader]
index-da5ba0a6.js:104173 Expected:  [data-cy=overlayLoader]
index-da5ba0a6.js:104173 Message:   expected [data-cy=overlayLoader] to exist in the DOM
index-da5ba0a6.js:104173 Error:     AssertionError: Expected to find element: `[data-cy=overlayLoader]`, but never found it.
    at applyChainer (http://localhost:4200/__cypress/runner/cypress_runner.js:135326:22)
    at http://localhost:4200/__cypress/runner/cypress_runner.js:135364:16
    at arrayReduce (http://localhost:4200/__cypress/runner/cypress_runner.js:25789:21)
    at Function.reduce (http://localhost:4200/__cypress/runner/cypress_runner.js:34841:14)
    at applyChainers (http://localhost:4200/__cypress/runner/cypress_runner.js:135342:68)
From previous event:
    at Context.shouldFn (http://localhost:4200/__cypress/runner/cypress_runner.js:135368:63)
    at Context.should (http://localhost:4200/__cypress/runner/cypress_runner.js:135385:23)
    at http://localhost:4200/__cypress/runner/cypress_runner.js:130900:39
    at assertions (http://localhost:4200/__cypress/runner/cypress_runner.js:131149:16)
From previous event:
    at $Cy.verifyUpcomingAssertions (http://localhost:4200/__cypress/runner/cypress_runner.js:131161:62)
    at onRetry (http://localhost:4200/__cypress/runner/cypress_runner.js:149852:15)
    at retryQuery (http://localhost:4200/__cypress/runner/cypress_runner.js:149865:10)
    at http://localhost:4200/__cypress/runner/cypress_runner.js:150015:17
From previous event:
    at CommandQueue.runCommand (http://localhost:4200/__cypress/runner/cypress_runner.js:149974:8)
    at next (http://localhost:4200/__cypress/runner/cypress_runner.js:150174:19)
    at http://localhost:4200/__cypress/runner/cypress_runner.js:150195:16
From previous event:
    at next (http://localhost:4200/__cypress/runner/cypress_runner.js:150174:39)
From previous event:
    at http://localhost:4200/__cypress/runner/cypress_runner.js:163750:77
From previous event:
    at CommandQueue.run (http://localhost:4200/__cypress/runner/cypress_runner.js:163745:21)
    at CommandQueue.run (http://localhost:4200/__cypress/runner/cypress_runner.js:150238:15)
    at $Cy.runQueue (http://localhost:4200/__cypress/runner/cypress_runner.js:151162:14)
    at cy.<computed> [as visit] (http://localhost:4200/__cypress/runner/cypress_runner.js:151244:12)
    at __stackReplacementMarker (http://localhost:4200/__cypress/runner/cypress_runner.js:150670:13)
    at runnable.fn (http://localhost:4200/__cypress/runner/cypress_runner.js:151423:19)
    at callFn (http://localhost:4200/__cypress/runner/cypress_runner.js:104964:21)
    at ../driver/node_modules/mocha/lib/runnable.js.Runnable.run (http://localhost:4200/__cypress/runner/cypress_runner.js:104951:7)
    at http://localhost:4200/__cypress/runner/cypress_runner.js:158412:30
From previous event:
    at Object.onRunnableRun (http://localhost:4200/__cypress/runner/cypress_runner.js:158395:53)
    at $Cypress.action (http://localhost:4200/__cypress/runner/cypress_runner.js:147632:28)
    at Runnable.run (http://localhost:4200/__cypress/runner/cypress_runner.js:156392:13)
    at next (http://localhost:4200/__cypress/runner/cypress_runner.js:105466:10)
    at http://localhost:4200/__cypress/runner/cypress_runner.js:105510:5
    at timeslice (http://localhost:4200/__cypress/runner/cypress_runner.js:99436:27)

Other

No response

AtofStryker commented 1 year ago

Hey @styriandev. Thank you for opening an issue. I don't think I've seen this behavior before. I saw your note about "Unfortunately on a clean repo this does not happen", but is there a way I can reproduce this on my end? It might be very challenging without a reproduction 😅 .

styriandev commented 1 year ago

@AtofStryker Thanks for the answer. Yeah, I understand that a reproduction repo would be helpful. Unfortunally I am unable to reproduce it in a new repository. I can think of any issue that happens because of some tooling that we are using - but the main part that is happening after this bridge is some async operation that is triggered by a NGRX Store. Right now I found out that this issue already happens in version 10.10.0, was there something changed in regards to Electron within this version? When downgrading to 10.9.0 everything works correctly. Regarding to the change log the only change that has something to do with it is https://github.com/cypress-io/cypress/issues/18480 for the version 11, but I cannot see why this should produce this behaviour.

Update on this: I just saw that Electron was updatet by that version from 19 to 21 - so I am going to check if there is some change within this version that produces this issue. The only change that might produce this is the following: Default Changed: renderers without nodeIntegration: true are sandboxed by default

Actually I detected a further interesting point: If I move the setup that calls this bridge into a it block the local storage isn't set if I am working in Electron. This issue does not happen when the test is executed in Chrome. Therefore I am pretty sure that this is something that happens in the interaction between cypress and electron.

Is there any known issues related to that?

AtofStryker commented 1 year ago

@styriandev It sounds like the electron upgrade from v19 to v21 in 10.10.0 might be of issue here. Any idea if Electron has any issues open related to what you are experiencing?

Electron I believe is currently on v23. I am curious if something like this has been fixed and if bumping electron would fix the issue.

styriandev commented 1 year ago

@AtofStryker Thank you! But can I modify the Electron version used by Cypress without manually compiling Cypress?

AtofStryker commented 1 year ago

@styriandev unfortunately no. The only thing I think we can do here since we do not have a reproduction is to see what happens when we upgrade to a newer version of electron and if this issue resolves. Were you able to find any issues open related to what you are experiencing? I took a brief glance but didn't see anything.

styriandev commented 1 year ago

@AtofStryker The only breaking change that I could think of is the following from version 20 (https://www.electronjs.org/blog/electron-20-0):

Default Changed: renderers without nodeIntegration: true are sandboxed by default Previously, renderers that specified a preload script defaulted to being unsandboxed. This meant that by default, preload scripts had access to Node.js. In Electron 20, this default has changed. Beginning in Electron 20, renderers will be sandboxed by default, unless nodeIntegration: true or sandbox: false is specified.

If your preload scripts do not depend on Node, no action is needed. If your preload scripts do depend on Node, either refactor them to remove Node usage from the renderer, or explicitly specify sandbox: false for the relevant renderers.

Is there something in the cy.get function that requires node? From the point how I understand Cypress this should not be the case but just to be sure.

styriandev commented 1 year ago

Ok, I was finally able to find the route cause of this problem. The root of the problem is still unclear to me but I was able to fix it.

In the e2e.ts file, that is used in the setup, we added browser permissions for copy/paste. We did this in the following way:

Cypress.automation('remote:debugger:protocol', {
  command: 'Browser.grantPermissions',
  params: {
    permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'],
    // make the permission tighter by allowing the current origin only
    // like "http://localhost:56978"
    origin: window.location.origin
  }
});

The thing is that in electron this created some issues that are visible in the browser console: image

The issue is "cypress_runner.js:7442 Unhandled rejection Error: Browser context management is not supported." and only happens in electron. The same issue happend already in Cypress 10.6.0. but after the update to 10.10.0 this seems to create problems in the test execution.

I was able to fix the problem by checking if the browser is started in electron before assigning the additional permissionins. I did that in the following way:


if (Cypress.browser.name !== 'electron') {

  Cypress.automation('remote:debugger:protocol', {
    command: 'Browser.grantPermissions',
    params: {
      permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'],
      // make the permission tighter by allowing the current origin only
      // like "http://localhost:56978"
      origin: window.location.origin
    }
  });

}

After adding the additional if the tests worked through like before. As written initially, it seems like the new versions do take that browser error more serious or seem to have some broken logic because of that error. When I have got some time I am trying to reproduce this issue in a clean repository to find out if that happens also there.

dannyskoog commented 1 year ago

I'm also experiencing issues when updating from Cypress 10.9.0 to 10.10.0 (or later) and using Electron (106). But my problem is a bit different. And it has do with Firebase authentication and its onAuthStateChanged listener never firing after a successful login. I know that Firebase stores its auth data in IndexedDB, so that interaction might be the culprit when using Electron 106. But so far I haven't been able to find any solutions or workarounds for this.

AtofStryker commented 1 year ago

@styriandev interesting. I wonder if this is something we can mitigate in the future by having a more first-class API to set browser permissions as opposed to leverage Cypress.automation('remote:debugger:protocol'), which isn't heavily documented and is going to go directly through CDP, which leaves everyone up to the mercy of the implementing browser in this case.

AtofStryker commented 1 year ago

@dannyskoog would you be able to open a separate issue? It seems electron related but for a different reason.

styriandev commented 1 year ago

@AtofStryker I agree with you. Even though that browser permissions for copying and pasting is an edge case it would be great to have a better API there. As the actual issue is solved, should I create a seperate ticket for the feature suggestion or should I let this ticket open?

AtofStryker commented 1 year ago

seems related to #18675

AtofStryker commented 1 year ago

@styriandev I think that is the right action, ie create a separate ticket and close this issue. I'm checking to see if we have a first class API issue for permissions, but I don't think we do so go ahead and create one and we can link it up once I either a) find it or b) create it and link it.

dannyskoog commented 1 year ago

@dannyskoog would you be able to open a separate issue? It seems electron related but for a different reason.

Yes. And I will make sure to put in more details in it as well 👍

AtofStryker commented 1 year ago

@styriandev I am going to go ahead and close this issue as discussed above