Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically accept geolocation permission / mock geolocation for tests #2671

Open
akozmanyan1 opened this issue Oct 29, 2018 · 42 comments
Open
Labels
E2E Issue related to end-to-end testing type: feature New feature that does not currently exist type: unexpected behavior User expected result, but got another

Comments

@akozmanyan1
Copy link

akozmanyan1 commented Oct 29, 2018

Current behavior:

I have a map and a button in my app. When user clicks the button it identifies it’s current position and map pin moves to the current location of the user. This does not happen when running cypress tests.

Desired behavior:

When running cypress test I want to be able to get the location of user and the map to show that position changes.

Steps to reproduce:

I created a simple POC – https://github.com/akozmanyan1/geolocation-bug
Just download and run it - '$ npm run serve'.
You also will need to open cypress - '$ ./node_modules/.bin/cypress open'

Versions

Cypress - Running Electron 59
OS: Windows 10
browser: Google Chrome - Version 69.0.3497.100

@jennifer-shehane jennifer-shehane added the stage: needs investigating Someone from Cypress needs to look at this label Oct 29, 2018
@jennifer-shehane
Copy link
Member

Hey @akozmanyan1, thanks so much for providing a reproducible repo!

I notice that when running this locally, my browser prompts for permission to access my location. My guess is that Cypress is not handling this dialog properly when automated.

screen shot 2018-10-29 at 11 54 19 am

Can you disable the checking for permission on this dialog when run within Cypress? I would like to verify that this is indeed the problem.

Here's some instruction on identifying whether you app is running within Cypress.

@jennifer-shehane jennifer-shehane added stage: awaiting response Potential fix was proposed; awaiting response and removed stage: needs investigating Someone from Cypress needs to look at this labels Oct 29, 2018
@akozmanyan1
Copy link
Author

Hi @jennifer-shehane , I don't think I can disable this alert. This is Chrome asking for permission to share the location with the website. The problem is that no such dialog appears in Cypress. In addition, I can't find if there is a switch in Cypress which can turn this off this dialog.

@jennifer-shehane
Copy link
Member

@akozmanyan1 This is definitely something that Cypress should handle and can handle by utilizing Chrome debugger protocol.

There aren't any really easy workarounds for this.

You could search for ways to emulate geolocation in Chrome, but none of those solutions are going to work in CI when run in Electron.

@jennifer-shehane jennifer-shehane changed the title Finding user loaction on google maps Automatically accept geolocation permission / mock geolocation for tests Oct 30, 2018
@jennifer-shehane jennifer-shehane added type: feature New feature that does not currently exist type: unexpected behavior User expected result, but got another stage: ready for work The issue is reproducible and in scope and removed stage: awaiting response Potential fix was proposed; awaiting response labels Oct 30, 2018
@swapab
Copy link

swapab commented Nov 22, 2018

@akozmanyan1 Did you found a way to mock/stub navigator.geolocation ?

@nico2che
Copy link

nico2che commented Dec 4, 2018

@swapab with the stub method you can mock a position or an error

For example, an approach like

function fakeLocation(latitude, longitude) {
  return {
    onBeforeLoad(win) {
      cy.stub(win.navigator.geolocation, "getCurrentPosition", (cb, err) => {
        if (latitude && longitude) {
          return cb({ coords: { latitude, longitude } });
        }
        throw err({ code: 1 }); // 1: rejected, 2: unable, 3: timeout
      });
    }
  };
}

And

cy.visit("http://www.cypress.io", fakeLocation()); // Error
cy.visit("http://www.cypress.io", fakeLocation(48, 2)); // France

But better with cypress commands

@attilavago
Copy link

I'd like to see this fixed too. With the rise of PWAs location detection, notifications, etc, is becoming a common feature, and we should be able to emulate user behaviour with these dialogues.

@andrewhl
Copy link

I'm having the same issue. I've attempted to use the following method:

plugins/index.js

module.exports = (on, config) => {
    on('before:browser:launch', (browser = {}, args) => {
        args.push('--disable-search-geolocation-disclosure');
        return args;
    })
}

But the UI popup for authorizing geolocation still appears in Chrome.

@jasonlimantoro
Copy link

Any updates on this?

I tried to use a geolocation API for form auto-filling, but I cannot test it with Cypress because of the geolocation permission popup.

@attilavago
Copy link

Any updates on this?

I tried to use a geolocation API for form auto-filling, but I cannot test it with Cypress because of the geolocation permission popup.

My use-case is very similar.

@jasonlimantoro
Copy link

For now, I finally decided to use the stubbed method as per @nico2che 's answer.

cy.fixture('location.json').as('fakeLocation');
cy.get('@fakeLocation').then(fakeLocation => {
  cy
    .visit('some-url', {
      onBeforeLoad (win) {
	cy
	  .stub(win.navigator.geolocation, 'getCurrentPosition')
	  .callsFake((cb) => {
             return cb(fakeLocation);
	  });
      },
  });
});

where location.json is a fixture for fake location

{
  "coords": {
    "latitude": 48,
    "longitude": 2
  }
}

The difference is that I use it in the onBeforeLoad option argument for visit()'s method.
And I use it in the beforeAll() hooks, in my specs.

It works for me now.
Hope it helps.

@emahuni
Copy link

emahuni commented May 28, 2019

Any news on this feature?

@tymondesigns
Copy link

Chrome 75 has now been "fixed" where it will never timeout as long as this prompt is there. https://bugs.chromium.org/p/chromium/issues/detail?id=957596

Therefore this no longer works even with stubbing the location.

We have had to rollback to Chrome 74 for now, but we need to fix this asap

@ravihlb
Copy link

ravihlb commented Aug 19, 2019

Edit: it seems that calling the command directly inside cy.visit() messes up the request recognition? cy.wait() just stopped working when I did that (something to do with promisses).
As a workaround, I still added the stub as a custom command and did as follows:

cy.visit('url')
cy.on('window:before:load', (win) => { cy.mockGeolocation(win, lat, long) })

And it seems to be working properly now, logging every XHR request on the console and cy.wait() works again.


I managed to pull it out by mixing up @nico2che's answer a bit also:
I first added it as a Cypress Command

Cypress.Commands.add("mockGeolocation", (windowObj, lat, long) => {
    cy.stub(windowObj.navigator.geolocation, "getCurrentPosition", (cb) => {
        return cb({ coords: { "latitude": lat, "longitude": long } });
    })
})

And implemented in the spec as:

cy.visit('url', { onBeforeLoad: (win) => { cy.mockGeolocation(win, lat, long) } })

@yes-mam
Copy link

yes-mam commented Sep 17, 2019

We have implemented the above and it works fine for triggering and mocking out the getCurrentLocation response, however, one thing we are having trouble with is testing the state of the DOM while the getCurrentLocation stub runs.

@smlkA
Copy link

smlkA commented Sep 18, 2019

UPDATED: I could start my test with following custom command:

Cypress.Commands.add('visitWithMockGeolocation', (url, latitude = 54, longitude = 39) => {
  const mockGeolocation = (win, latitude, longitude) => {
    cy.stub(win.navigator.geolocation, 'getCurrentPosition', cb => {
      return cb({ coords: { latitude, longitude } });
    });
  };
  cy.visit(url, {
    onBeforeLoad: win => {
      mockGeolocation(win, latitude, longitude);
    }
  });
});

@ravihlb Hi, I have tried your both approaches and got error:

Uncaught CypressError: Cypress detected that you returned a promise from a command while also invoking one or more cy commands in that promise.

The command that returned the promise was:

 > cy.visit()

The cy command you invoked inside the promise was:

 > cy.mockGeolocation()

Because Cypress commands are already promise-like, you don't need to wrap them or return your own promise.

@victor-battestin
Copy link

victor-battestin commented Oct 2, 2019

Hello guys, I'm also in with this same problem. Any updates about a solution for cypress interact with chrome alerts and notifications? I tried all the solutions proposed by those guys above and none worked for my case. 😢

@ciekawy
Copy link

ciekawy commented Oct 9, 2019

I have a slightly other question. As I also got the permission popup (along with a write files one) and that is actually expected by me - I wonder if there is (or will be) a way to either control those popups or simulate behavior with config/mock to either enable or disable particular feature during test execution.

I also found those popups to appear only on first execution thus I assume the answer is being stored in Cypress browser profile.

@erik-east
Copy link

I managed to simplify the solution suggested by @nico2che and @jasonlimantoro by using cy.window() cypress method.

Cypress.Commands.add('mockGeolocation', (latitude = 30, longitude = -98) => {
	cy.window().then(($window) =>  {
		cy.stub($window.navigator.geolocation, 'getCurrentPosition', (callback) => {
	   		return callback({ coords: { latitude, longitude } });
		});
	});
});

And this way it can be used anywhere without using an onBeforeLoad promise if it's needed in a specific test rather than the whole suite.

cy.mockGeolocation();

@bahmutov
Copy link
Contributor

bahmutov commented Dec 12, 2019 via email

@kamranayub
Copy link

Hey all! I've just released a package to control browser permissions in Cypress which should address this issue! 🎉

In short, it uses the before:browser:launch event to handle sending the right preference settings/values to Chrome, Edge, and Firefox.

The package is cypress-browser-permissions and you can read the introduction post here!

https://dev.to/kamranayub/controlling-browser-permissions-in-cypress-end-to-end-tests-5d7b

@jennifer-shehane It would be amazing to see this built into the core.

@PriyaKR
Copy link

PriyaKR commented Aug 21, 2020

I managed to simplify the solution suggested by @nico2che and @jasonlimantoro by using cy.window() cypress method.

Cypress.Commands.add('mockGeolocation', (latitude = 30, longitude = -98) => {
	cy.window().then(($window) =>  {
		cy.stub($window.navigator.geolocation, 'getCurrentPosition', (callback) => {
	   		return callback({ coords: { latitude, longitude } });
		});
	});
});

And this way it can be used anywhere without using an onBeforeLoad promise if it's needed in a specific test rather than the whole suite.

cy.mockGeolocation();

I have tried out this work around and have done a cy.visit() to a page after cy.mockGeolocation() command but the visit commands doesn't seems to use the mocked geo location and still shows the page based on the current location.

@ThaiChingChong
Copy link

Tried the above commands to mock the geo location as well but it doesn't work for me too. :( The page I am currently testing still behave as though the user is on current location

@mpelzsherman
Copy link

@eerkkk 's solution works fine for me if I call cy.mockGeolocation() after cy.visit().

@IpelaTech
Copy link

For future reference, another solution (I'm using this as I'm working on the new component testing and there isn't a visit method), I use this to mock/stub/etc the geolocation inside the test:

//*.spec.js
    it("foo", () => {
        cy.window().then(win => {
            win.navigator.geolocation.getCurrentPosition = function() {
                
            };
        });
    });

@alastorohlin
Copy link

Hi, I was facing the same problem, but I found this plugin and it works great 🚀

I hope this helps someone else.

@mmitchell3590
Copy link

mmitchell3590 commented Jul 23, 2021

I am also using the cypress-browser-permissions plugin, but it would be great if Cypress could support this without an additional plugin. It is not ideal especially when there are limitations like this #5240 when using multiple plugins that modify the 'on' event

Also, as of 8.0.0 the default behavior for cypress run is headless mode, and plugins do not work there.
image

@owenashurst
Copy link

Hi, I was facing the same problem, but I found this plugin and it works great 🚀

I hope this helps someone else.

It's @kamranayub's. He posted about it a few posts above.

@prodrammer
Copy link

I wonder if relying on location state violates the recommendations in Cypress' docs on Conditional Testing.

If we don't have a way to override chrome's default behavior in a reliable way, abstracting geolocation as a service in our applications and providing an override via localStorage in the service might be a way to avoid flaky tests.

Something like:

if (locationOverrideExists()) {
 // return location from local storage...
} else {
 // get live location data
}

@gaikwadamolraj
Copy link

I encountered with same problem but mocking was not worked for me.
My case:
Application use geo location for the login purpose. So with mock it not worked for me.

Solution:
I used below package and followed the steps and its works for me.

https://www.npmjs.com/package/cypress-visit-with-custom-geolocation

Working sample example:
https://github.com/gaikwadamolraj/cypress-visit-with-custom-geolocation

@cypress-bot cypress-bot bot added stage: backlog and removed stage: ready for work The issue is reproducible and in scope labels Apr 29, 2022
@ArCiGo
Copy link

ArCiGo commented Aug 9, 2022

@eerkkk Do you have a sample using your solution?

@gaikwadamolraj
Copy link

@eerkkk Do you have a sample using your solution?

Yes you can check this repo: https://github.com/gaikwadamolraj/custom-geo-location-cypress

I created a sample working html file and currently pkg is using in cypress.

@marijana-rukavina
Copy link

Why did mocking it like this stopped working? I am curious, it was working fine in our tests until recently 🤔

I managed to simplify the solution suggested by @nico2che and @jasonlimantoro by using cy.window() cypress method.

Cypress.Commands.add('mockGeolocation', (latitude = 30, longitude = -98) => {
	cy.window().then(($window) =>  {
		cy.stub($window.navigator.geolocation, 'getCurrentPosition', (callback) => {
	   		return callback({ coords: { latitude, longitude } });
		});
	});
});

And this way it can be used anywhere without using an onBeforeLoad promise if it's needed in a specific test rather than the whole suite.

cy.mockGeolocation();

I have tried out this work around and have done a cy.visit() to a page after cy.mockGeolocation() command but the visit commands doesn't seems to use the mocked geo location and still shows the page based on the current location.

@ter1203
Copy link

ter1203 commented Oct 20, 2022

I am now working on a test coverage that should check if the modal is displayed when the user location is disabled.
The problem is how to check the user location is disabled or not with Cypress. Is there anyway for that?
Or how to mock location disable status with Cypress?

@janswist
Copy link

4 years and still it's not implemented despite interest?

@Khalester
Copy link

Khalester commented May 4, 2023

I came up with this custom cypress command, it seems to work and doesn't show the popup.

Cypress.Commands.add(
  "mockGeolocation",
  (coords: { latitude: number; longitude: number }) => {
    cy.window().then((win) => {
      cy.wrap(
        Cypress.automation("remote:debugger:protocol", {
          command: "Browser.grantPermissions",
          params: {
            permissions: ["geolocation"],
            origin: win.location.origin,
          },
        }),
      );
    });
    
    console.debug(
      `cypress::setGeolocationOverride with position ${JSON.stringify(coords)}`,
    );
    
    cy.log("**setGeolocationOverride**").then(() =>
      Cypress.automation("remote:debugger:protocol", {
        command: "Emulation.setGeolocationOverride",
        params: {
          latitude: coords.latitude,
          longitude: coords.longitude,
          accuracy: 50,
        },
      }),
    );
  },
);

Then use it like this
cy.mockGeolocation({ latitude: 0, longitude: 0 })

EDIT

Versions

  • OS: macOS 13.3.1 (22E261)
  • cypress 12.8.1
  • Browser: Chrome Version 113.0.5672.63 (Official Build) (arm64)

EDIT 2

This is the command for mocking denied Geolocation permissions

Cypress.Commands.add("mockGeolocationDenied", () => {
  cy.window().then((win) => {
    cy.wrap(
      Cypress.automation("remote:debugger:protocol", {
        command: "Browser.grantPermissions",
        params: {
          permissions: [],
          origin: win.location.origin,
        },
      }),
    );
  });
});

@nagash77 nagash77 added the E2E Issue related to end-to-end testing label May 4, 2023
@PrabhurajGMastery
Copy link

PrabhurajGMastery commented May 9, 2023

@Khalester
Hi.. if i launch cypress with your code i get "permission can't be granted to opaque origins"
Was really hoping for your code to work
May i get your repo for analysis ?

@muralinaidud
Copy link

HI All is there a way to click allow when the test runs ?

@agospranzetti
Copy link

@jennifer-shehane whats the update here ? there is still not a viable solution ? with the above Im also getting "permission can't be granted to opaque origins"

@Zach-Costa
Copy link

@PrabhurajGMastery @muralinaidud @agospranzetti

Got this working - Cypress no longer likes you setting origin to win.location.origin, it wants to know exactly what site is getting the location permission. If I explicitly include the expected origin, it passes without issue.

@trav-giv
Copy link

I came up with this custom cypress command, it seems to work and doesn't show the popup.

Cypress.Commands.add(
  "mockGeolocation",
  (coords: { latitude: number; longitude: number }) => {
    cy.window().then((win) => {
      cy.wrap(
        Cypress.automation("remote:debugger:protocol", {
          command: "Browser.grantPermissions",
          params: {
            permissions: ["geolocation"],
            origin: win.location.origin,
          },
        }),
      );
    });
    
    console.debug(
      `cypress::setGeolocationOverride with position ${JSON.stringify(coords)}`,
    );
    
    cy.log("**setGeolocationOverride**").then(() =>
      Cypress.automation("remote:debugger:protocol", {
        command: "Emulation.setGeolocationOverride",
        params: {
          latitude: coords.latitude,
          longitude: coords.longitude,
          accuracy: 50,
        },
      }),
    );
  },
);

Then use it like this cy.mockGeolocation({ latitude: 0, longitude: 0 })

EDIT

Versions

  • OS: macOS 13.3.1 (22E261)
  • cypress 12.8.1
  • Browser: Chrome Version 113.0.5672.63 (Official Build) (arm64)

EDIT 2

This is the command for mocking denied Geolocation permissions

Cypress.Commands.add("mockGeolocationDenied", () => {
  cy.window().then((win) => {
    cy.wrap(
      Cypress.automation("remote:debugger:protocol", {
        command: "Browser.grantPermissions",
        params: {
          permissions: [],
          origin: win.location.origin,
        },
      }),
    );
  });
});

This solution has worked for me. Big thanks to @Khalester 🎉

Cypress version: 13.12.0
Chrome Version: 126

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
E2E Issue related to end-to-end testing type: feature New feature that does not currently exist type: unexpected behavior User expected result, but got another
Projects
None yet
Development

No branches or pull requests