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.
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')
.
Verified.
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
});
});
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
});
});
Cypress version: 3.1.0
I'm experiencing what seems to be the same behavior as @hdjong1. If I log the userAgent
value after setting it inside a test using Cypress.config('userAgent', 'mobile_UA_string')
, it logs the mobile UA string as expected, but has no effect on my app. However, if I specify the same UA string in cypress.json and relaunch the Cypress app, the UA detection works as expected. Modifying the userAgent
string from inside tests is clearly updating the config value, but it seems like that updated value isn't being passed the app being tested.
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.
@brian-mann Thanks for the quick reply and suggestion!
Is this limitation unique to setting the userAgent
value or am I misunderstanding the purpose of Cypress.config
as a method for modifying config values during tests?
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.
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!
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.
Makes sense. That would be much quicker than consulting documentation, which would be convenient.
Next time it comes up you can mention the random guy in issue #2100 who supports the freeze/lock and throw idea if you need a tie breaker. In fact, popup my profile pic when it throws with the caption "No."
So I see above that there _might_ be a way to run cypress run
with a config value. Should this work:
cypress run --config 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'
?
I'm getting a huge error in the UI when I attempt to run that.
@nobleach The value you're specifying for userAgent
contains several characters that aren't being parsed how you want. I'm not sure the best solution, but my kind of hacky solution was to pass a simplified string to the run/open command and check for that string when loading plugins
"cypress:dev:mobile": "cypress open --env configFile=dev,userAgent=mobile",
In the plugins export, if "mobile" is specified, then I add the full user agent string to the config that gets returned
if (config.env.userAgent === 'mobile') {
data.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/15A356 Safari/604.1';
}
It doesn't feel very pretty, but the user agent needs to be specified before Cypress loads the config and this is what I came up with.
I suppose that's viable. I'm interested in how others handle it too. An adaptive app that renders a different experience for mobile/tablet/desktop isn't that far outside of reality, is it?
I very much appreciate you sharing your approach, so thank you!
I've been migrating my tests to Cypress and I'm facing this issue where we have 3 platforms and each needs a different user agent among other specific configurations that are caught and injected in our plugin, the same spec is run for each platform, so we do a loop before starting each spec suite. We have a fixture organized by platform, so we can assert easily.
Doing it manually is not a viable solution, I thought I could also move away from gulp and just use npm run scripts, but so far the only way I could accomplish this is to have all the different configs in gulp.
Is there any other approach you can suggest us?
Thanks
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);
};
It would be nice if this can be mentioned in the online docs, it took me a while before figuring out what was wrong
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.
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
Related issue that would make it an error to try to use Cypress.config
on a non-overridable property: #3422
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)',
});
},
});
});
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?
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')
.
same problem for us / annoying not to find this in the official docs but within the issues.
fixed with @luokuning 's solution - thanks a lot
Most helpful comment
Using npm scripts shouldn't be a problem. We do it like this:
I've used the above suggestion in my
plugins/index.js
: