cypress: Unable to switch userAgent during test run

Current behavior:

I’m testing an application that, as many these days, has a responsive design. Based on the viewport the layout changes. However, for some parts, it’s also important that the user agent is set to mobile in order to trigger certain functionality. Cypress provides an userAgent option in cypress.json which can be used for this. Since my test suite contains tests for both desktop and mobile scenario’s I would like to set the userAgent option during test runs. By default, I just leave it empty which takes care of the desktop scenarios. When a mobile scenario is run I want userAgent to be set to a mobile one, and switch back once a desktop scenario is encountered again. I’m using Cypress.config('userAgent', 'value') in my spec files in order to do so.

Steps to reproduce:

Test code:

describe('A certain page', () => { 
  describe('on mobile', () => {
    before(() => {
      console.log(Cypress.config('userAgent')); // outputs: null
      Cypress.config('userAgent', 'mobile_value'); // set userAgent
      console.log(Cypress.config('userAgent')); //outputs: mobile_value
      setUp(); //setup function where cookies and viewport (iphone-6) are set and cy.visit is called
    });

    it('should exhibit mobile behaviour', () => {
      cy.get('something').should('be.mobile.functionality');
    });
  });
}); 

Based on the above code I would expect to get the mobile version of my app served, but I’m still getting the desktop version through a mobile viewport. When I set the userAgent in cypress.json directly everything does work as expected (I get the mobile app served). So the functionality is working, but I can’t seem to trigger it during a test run with Cypress.config('userAgent').

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 29
  • Comments: 40 (9 by maintainers)

Commits related to this issue

Most upvoted comments

Using npm scripts shouldn’t be a problem. We do it like this:

    "cypress:prod": "CYPRESS_baseUrl=http://localhost:8080 cypress run --env userAgent=mobile",
    "cypress:prod:tablet": "CYPRESS_baseUrl=http://localhost:8080 cypress run --env userAgent=tablet",
    "cypress:ci": "cypress:prod && cypress:prod:tablet",

I’ve used the above suggestion in my plugins/index.js:

module.exports = (on, config) => {
    // create new config settings
    const configOverride = {};
    if (config.env.userAgent === 'mobile') {
        configOverride.userAgent = 'Mozilla/5.0 (Linux; Android 6.0.1; SM-G532G Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.83 Mobile Safari/537.36';
        configOverride.testFiles = 'mobile/**/*.*';
    } else if (config.env.userAgent === 'tablet') {
        configOverride.userAgent = 'Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10';
        configOverride.testFiles = 'tablet/**/*.*';
    } else {
        configOverride.userAgent = 'none';
    }

    return Object.assign({}, config, configOverride);
};

Here is another workaround mentioned here, may be useful in some case:

before(() => {
    cy.visit(url, {
        onBeforeLoad: win => {
            Object.defineProperty(win.navigator, 'userAgent', {
                value: 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
            });
        },
    });
});

It would be nice if this can be mentioned in the online docs, it took me a while before figuring out what was wrong

You cannot change the user agent in the middle of a test. What you can do is split up the mobile tests from the desktop tests and then run those groups separately via a different cypress run --config userAgent=...

The only way for us to be able to change the userAgent on the fly is at the network layer. We could make that work but that’s really more applicable to the network stubbing rewrite.

I’d like to chime in my + 1. This is a must have feature 😬

I also lost one good hour trying to figure out why neither the second argument for describe or it didn’t respect the user agent.

I finally found this issue (and, to be fair, this on the documentation, stating that userAgent would be ignored), but a much better developer experience would to be thrown an error about this, warning that you were trying to change an immutable value during the test run.

It’s also confusing in the documentation that if you pass { browser: { userAgent: 'xxx' } you’ll get a faded blue test stating “Ignored due to browser”, because it’s actually conditioning the test run, and not changing browser options.

Regarding workarounds, I managed to set my user agent doing it like this:

cy.visit('/my-url', {
    headers: { 'user-agent': 'mobile' }
});

However, unfortunately that still doesn’t cut it for our tests because it will send that header only in the first request, but not honor it on any page redirects; so if your test clicks on a button and gets redirected to another page, the redirect won’t have those headers.

Had I known this beforehand, I wouldn’t have switched our test suite to Cypress. It’s really a big deal for us because we use things like browser.device.mobile? in our Rails app all the time, to conditionally render elements or even alter redirect urls based on the browser’s user agent.

The other workaround posted here by @luokuning unfortunately doesn’t work in Cypress 6.

Related issue that would make it an error to try to use Cypress.config on a non-overridable property: #3422

I don’t see this mentioned in the Cypress.config documentation. Granted, I haven’t seen many other people talking about this, so it may be low priority, but some mention of this limitation in the docs could be helpful.

Thanks for your help!

There MUST be a dynamic way to set the user agent rather than thru a command line for each userAgent, isn’t it? None of the workarounds mentioned above that uses plugin/index worked for me (maybe I am doing something wrong, of course)… but isn’t over there any clear documentation on how to dynamically (based on spec name, relative path, something like this) set a user agent before the run starts?

I am also an enterprise client and this little thing is killing me (I love you tool, but there must be something over there that I am not fully catching) 🙏

Another bump for clearer documentation on this (or some alert in the UI). It also took me some time to get here and figure out that this isn’t possible.

It’s been talked about in other issues - and I think the general consensus was to freeze or lock properties that cannot be changed so that it would throw and you’d immediately know.

Verified.

  • Following test with cypress.json as {}:
describe('A certain page', () => {
  before(() => {
    console.log(Cypress.config('userAgent' as any)); // outputs: null
    Cypress.config('userAgent' as any, 'mobile_value'); // set userAgent
    console.log(Cypress.config('userAgent' as any)); //outputs: mobile_value
  });

  it('should exhibit mobile behaviour', () => {
    console.log(navigator.userAgent); // Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
  });
}); 
  • Same test with cypress.json as {"userAgent": "testValue"}:
describe('A certain page', () => {
  before(() => {
    console.log(Cypress.config('userAgent' as any)); // outputs: testValue
    Cypress.config('userAgent' as any, 'mobile_value'); // set userAgent
    console.log(Cypress.config('userAgent' as any)); //outputs: mobile_value
  });

  it('should exhibit mobile behaviour', () => {
    console.log(navigator.userAgent); // testValue
  });
}); 

Here is another workaround mentioned here, may be useful in some case:

before(() => {
    cy.visit(url, {
        onBeforeLoad: win => {
            Object.defineProperty(win.navigator, 'userAgent', {
                value: 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
            });
        },
    });
});

I was trying to use this method to trigger my React frontend to render the mobile page, but it didn’t work. Any idea?

Maybe you should also change the viewport size, such as cy.viewport('iphone-x').

Its a limitation that exists for a few different config values - basically anything that’s not directly under Cypress’s control like things such as timeouts or environment variables, etc.

Cypress.config(...) is a way of modifying the config but certain changes won’t be respected because those values have to be applied during different stages sometimes before the browser is even launched.

My workaround was to have a separated config file and manually running the tests for desktop and mobile. I know, it sucks but someone might find this useful.

I have my regular configuration with all my options on the root folder called cypress.json and I have a separated one called cypress-mobile.json that has all the same things, but has an extra property as you can see here:

{
  "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1"
}

So on my project package.json on the scripts section now I have two entries:

"scripts": {
  "test": "cypress open",
  "test:mobile": "cypress open --config-file cypress-mobile.json",
}

Hope it helps someone.

And another bump from me. I’m a paying customer and I wasted several hours till I was directed by someone to this issue.

Issue was reported al,ost a year ago. If you can’t change the functionality to match the docs then please change the docs to match the functionality. —> PR here: https://github.com/cypress-io/cypress-documentation/pull/1681