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

Overriding baseUrl from cypress.env.json #909

Closed
Vages opened this issue Nov 14, 2017 · 38 comments
Closed

Overriding baseUrl from cypress.env.json #909

Vages opened this issue Nov 14, 2017 · 38 comments
Milestone

Comments

@Vages
Copy link

Vages commented Nov 14, 2017

Current behavior:

Setting baseUrl in cypress.env.json leads to the key baseUrl being created inside env (the environment variables), not in the existing baseUrl being overridden at the configuration variable level.

Desired behavior:

baseUrl should be overridden, the behavior matching that stated in the section "Overriding Configuration" in the environment variables documentation. (Note: Overriding via the command line works as expected.)

How to reproduce/Test Code:

cypress.json:

{
  "baseUrl": "http://example.com/setFromCypress.json",
  "env": {
    "someVariable": "originalValue"
  }
}

cypress.env.json:

{
  "baseUrl": "http://example.com/setFromCypress.env.json",
  "someVariable": "newValue"
}

Screenshot of the resulting configuration:
screen shot 2017-11-13 at 11 28 44

Additional Info (images, stack traces, etc)

This issue was initially posted and phrased as a question on StackOverflow: https://stackoverflow.com/q/47262338/1858138

  • Operating System: Mac OS 10.13.1 (17B48)
  • Cypress Version: Cypress package version: 1.0.3
  • Browser Version: Chrome 60
@Vages Vages changed the title Overriding baseUrl from cypress.env.json does not work Overriding baseUrl from cypress.env.json Nov 14, 2017
@jennifer-shehane jennifer-shehane added the stage: needs investigating Someone from Cypress needs to look at this label Nov 14, 2017
@brian-mann
Copy link
Member

This isn't a bug. cypress.env.json is strictly for environment variables - not configuration.

https://docs.cypress.io/guides/guides/environment-variables.html#Setting-Environment-Variables

To override configuration you'll need to override it from the command line or by using special environment variables prefixed with CYPRESS_<config var>

@dwelle
Copy link

dwelle commented Nov 14, 2017

From docs, I got the same idea as OP. This part in particular:

If your environment variables match a standard configuration key, then instead of setting an environment variable they will instead override the configuration value.

Plus, the cypress runner configuartion "settings" tab doesn't do a good job in explaining that even though env has higher precedence than e.g. cypress.json or default, it doesn't override it at all...

@Vages
Copy link
Author

Vages commented Nov 14, 2017

That is the exact place in the docs that gave me the idea, @dwelle.

Too bad it’s not intended to be a feature; this could be of real use to my team.

@Vages
Copy link
Author

Vages commented Nov 14, 2017

But if this is set in stone, I can update the docs to express this more clearly. Just say the word, @brian-mann.

@DanielSwain
Copy link

DanielSwain commented Nov 15, 2017

@brian-mann I'm writing this in order to learn, and I'm respectfully requesting that this issue be re-examined. From the docs:
Option #2: cypress.env.json
You can create your own cypress.env.json file that Cypress will automatically check. Values in here will overwrite conflicting values in cypress.json.

This strategy is useful because if you add cypress.env.json to your .gitignore file, the values in here can be different for each developer machine.

// cypress.env.json

{
  "host": "veronica.dev.local",
  "api_server": "http://localhost:8888/api/v1/"
}

This documentation states that values in cypress.env.json will overwrite conflicting values in cypress.json, and here one of your team members @jennifer-shehane writes:

So, in the Desktop app, when you click into a project, there is a 'Config' tab. That will show you what the baseUrl has been set to and where it is being set from (whether cypress.json, cypress.env.json, set from environment variables or set as CLI arguments) - . . .

I develop on a Vagrant box, and in my current project I have a base url of 127.0.0.1:8090 (with the Vagrant configuration file mapping port 8090 to port 80). It seems that baseUrl should be able to be set from the cypress.env.json file. For now, then, I'm doing the following:
cyEnv = Cypress.env()
Cypress.config("baseUrl", cyEnv.baseUrl)

with cypress.env.json including {"baseUrl": "127.0.0.1:8090"}

Am I engaging in an anti-pattern here? I would be glad to propose updated documentation if you would prefer not to allow baseUrl to be directly overridden from cypress.env.json.

@brian-mann
Copy link
Member

brian-mann commented Nov 15, 2017

The documentation could be more specific.

Conflicting environment variables will be overriden.

It doesn't make sense to add baseUrl to cypress.env.json because its not an environment variable, its a configuration value. cypress.json and cypress.env.json are two different things with different use cases.

If we made cypress.env.json override configuration we might as well call it cypress2.json. As it stands, cypress.json needs a nested env key to specify environment variables, whereas cypress.env.json requires a flat structure where everything becomes an environment variable. These structures are incompatible.

Honestly cypress.env.json was not a good idea and it's worth investigating just to remove this entirely. It sounds like people need more complex configuration + env variable needs than what is currently implemented.

Likely we can just add a new plugin event called config (we've talked about this internally) and then you can have your own logic to rewrite Cypress' configuration values and environment variables based on whatever logic you want to write in JS, including support async reads. This way you have more programmatic control of whatever it is you want to change.

@Vages
Copy link
Author

Vages commented Nov 16, 2017

A file which allows any team member to locally override configuration variables and whose internal structure is the same as that of cypress.json would fulfill all of my team's needs at this point. By the internal structure being the same, I mean that configuration variables are specified at the first level of the tree structure, and environment variables are defined inside of an object at the key env.

By the way, I am very glad that we are having this discussion. Even if I don't get my way here, re-opening the discussion makes me feel like Cypress' creators are genuinely interested in hearing their users' opinions.

@DanielSwain
Copy link

@brian-mann I think that the concept of cypress.env.json was a good one but that perhaps expanding it would be more than adequate at this point (as opposed to creating a new plugin event right now). Changing the names/function of the files as follows might be a good approach.

  • Change cypress.json to cypress.conf.json

  • Have a cypress.conf.local.json file whose key structure can match and override cypress.conf.json as @Vages suggests and which would not be checked into source control

This structure would allow you to keep the cypress.env.json for backward compatibility at this point, but it could be deprecated and then eventually eliminated (same for cypress.json).

@brian-mann
Copy link
Member

There have been several other issues opened where people want multiple config files based on their environment - QA, dev, staging, prod. I need to read those to come up with a solution that covers all of these use cases.

I think what we're going to do is deprecate cypress.env.json and then give you a new command line option called: configFile.

This is what you will use to specify a different config file other than cypress.json.

However I would make the logic that this other file overrides conflicting values found in cypress.json but that cypress.json is still read in as the defaults.

Adding a programmatic event for config is also going to go in - which gives you even more flexibility to control things using regular JS code as opposed to files and our own arbitrary logic.

@DanielSwain
Copy link

DanielSwain commented Nov 16, 2017

@brian-mann

However I would make the logic that this other file overrides conflicting values found in cypress.json but that cypress.json is still read in as the defaults.

Thank you - that would be great. A question though: If an overriding config file, as described above, can only be invoked through a command line option, then could it be easily invoked in the local test runner? (Still learning here.) Alternatively (and as I mentioned in my last comment), if cypress.conf.json was the default config file and you then looked for any other file of the form cypress.conf.xxxxxxx.json and used it to override cypress.conf.json, then the ".xxxxxxxx" could be ".local", ".dev", ".staging", ".prod", or whatever. Thanks for your consideration and for this outstanding product.

@bahmutov
Copy link
Contributor

If I may offer some advice. Overriding configuration settings via environment variables or config files seems like a separate question from running Cypress.

Can we all agree that every configuration setting can be set via an environment variable? If not, open an issue for that setting.

So the real question is then how to conveniently start cypress with particular (maybe large) set of environment variables to test some specific baseUrl and timeout etc. For this you can use 3rd party tools like my own as-a - this is the tool we use internally to quickly run any tool with many many custom environment variables.

Just create a file ~/.as-a/.as-a.ini and put different blocks of settings there. For example, if you want to have a custom baseUrl pointing at staging server and viewport resolution 600 by 700 pixels (see https://docs.cypress.io/guides/references/configuration.html#Environment-Variables)

[staging-tablet]
CYPRESS_BASE_URL=https://my-staging.domain.com
CYPRESS_VIEWPORT_WIDTH=600
CYPRESS_VIEWPORT_HEIGHT=700

Then run Cypress with command as-a staging-tablet node_modules/.bin/cypress run

No need to create hierarchy of configuration JSON files.

@jennifer-shehane
Copy link
Member

I created a new issue in our docs to document being clearer about environment variables being overridden here: cypress-io/cypress-documentation#236. Our documentation is open source and contributions are welcome. 😄

@DanielSwain
Copy link

DanielSwain commented Nov 16, 2017

Thank you @jennifer-shehane. I think I've been confused between baseUrl and the environment variable host. I realize that baseUrl could be made anything (and in this recipe it actually includes the host), but perhaps it would be more clear if cy.visit() worked like this:
cy.visit(host + baseUrl + url)

@DanielSwain
Copy link

DanielSwain commented Nov 16, 2017

A bit more explanation: I'm coming from a Django background, and any Django settings variable of the form xxxxx_url (e.g. static_url = "/static/") never includes the host - the xxxxx_url is always added on to the host as I've proposed above. I hope you'll consider this idea concerning cy.visit() (and I suppose it would apply to cy.request() too). Here's another example (point 2) of melding baseUrl and the host. I think it would be easier to address the above issue in the docs if cy.request(host + baseUrl + url) was implemented.

@jennifer-shehane jennifer-shehane added the stage: proposal 💡 No work has been done of this issue label Nov 16, 2017
@brian-mann
Copy link
Member

So it sounds like this issue has diverged into 3 distinct issues. I'd like to get buy in that I have this correct and if so I will open these three issues:

  1. We will remove cypress.env.json entirely as a new breaking version.
  2. We will expose a new config event in the new Plugin API which has landed as of 1.1.0. This will be used to programmatically alter any of the configuration or environment variables. Essentially this will be the "last step" before these values are sent to the Driver for use in your tests. This would enable you to do virtually anything you want in any kind of capacity. Read in files from your file system, merge, deep merge, overrwrite, hell make an http request to some server for the values, do whatever it is you want to do.
  3. Add a new configFile CLI option which enables you to point to another arbitrary cypress.json file. The use would be cypress run --config-file ./cypress.staging.json. These values would override the ones found in cypress.json. This enables you to have per machine or per environment files you can control from the CLI or from the Module API.

@DanAtShenTech as per your latest comment regarding baseUrl and host. I'm not yet seeing the use case or value of this. I guess I understand that you are using host as we imagined baseUrl would be used. However it seems like the easiest thing for you to do is just to simply use Cypress.Commands.override for both cy.request and cy.visit and then manage this logic yourself. You could just require node's URL module to resolve the arguments passed in and then massage the baseUrl and the host yourself.

If I'm off on this, it would be helpful to have a more concrete example. Perhaps write the code you wish you had (it can be pseudo code). Or maybe even paste some real Cypress code.

@jennifer-shehane jennifer-shehane removed stage: needs investigating Someone from Cypress needs to look at this stage: proposal 💡 No work has been done of this issue labels Nov 27, 2017
@DanielSwain
Copy link

DanielSwain commented Dec 4, 2017

Thank you @brian-mann and @jennifer-shehane. At this point I don't have more to offer (as far as code or pseudo code) than what I mentioned above about how Django handles things. My purpose in making the suggestion was not to say that things should simply be done as they are in Django because that is the environment that I work in, but rather, I made the suggestion because the Django approach seems the most logical way to handle things since every request goes to some host. However, if Node has a BaseURL concept that is not the same as Django's (in which the base url does not include the host but, rather, comes after the host), then of course a JS testing framework has to lean toward implementing things in a way that makes sense to Node users. That said, the Django ecosystem is quite large, and I'd like to see other Django developers using Cypress. I do think that how Django separates host from base url makes the most sense from a logical standpoint, and so that is why I think it would be a good way for Cypress to handle things in the proposed manner as well.

@DanielSwain
Copy link

I just noticed the interchangeable use of "host" and "baseUrl" in point number 2 here. "baseUrl" seems, therefore, to currently be redundant with "host". Perhaps "baseUrl" could simply be eliminated if you would not like to use it in the above-mentioned fashion.

@brian-mann
Copy link
Member

brian-mann commented Dec 4, 2017

We took the concept of baseUrl from other JS e2e testing tools (like protractor) - they all work the same way.

baseUrl means "anything you want prefixed before your own values".

It could mean: `http://localhost:1234"

It could also mean: "http://localhost:1234/foo/bar/baz"

Whatever it is, we just toss it before what you pass to us in cy.visit and cy.request.

What I was saying is that you can construct and normalize this behavior yourself.

If you wanted to split out the path from host - you could make baseUrl just the host, and then set a new env var for path.

Upon doing that you could then do something like:

// cypress/support/index.js

const url = require('url')
const baseUrl = Cypress.config('baseUrl')
const path = Cypress.env("PATH")

// reset baseUrl by combining the two
Cypress.config("baseUrl", url.resolve(baseUrl, path))

@tzookb
Copy link

tzookb commented Dec 8, 2017

Read the whole discussion and surely seems like a workaround we all need

How would you suggest for having the baseUrl different for each env:

John machine:
john.example.com

Dana machine:
dana.example.com

QA machine:
qa.example.com

I can manipulate it with tricks with env file,
but it doesnt look good.

any other suggestions??

Thanks!

@bahmutov
Copy link
Contributor

bahmutov commented Dec 8, 2017 via email

@brian-mann
Copy link
Member

@tzookb yah, using CLI args or our module API would override baseUrl for you based on the environment you specify. That's exactly what the use case of baseUrl is. So not seeing how our current implementation isn't working for you.

Regardless this is about to land and you'll have the ability to programmatically alter configuration and environment variables in code. #1042

@bahmutov
Copy link
Contributor

bahmutov commented Dec 8, 2017

@tzookb here is how we do it - pointing same tests at different baseUrl locally, on CI, on staging and after deploying to production: https://www.cypress.io/blog/2017/05/30/cypress-and-immutable-deploys/

@brian-mann brian-mann added this to the 1.2.0 milestone Dec 12, 2017
@brian-mann
Copy link
Member

Released in 1.2.0.

@Konstruktour
Copy link

brian-mann commented on 22 Nov 2017

Add a new configFile CLI option which enables you to point to another arbitrary cypress.json file. The use would be cypress run --config-file ./cypress.staging.json. These values would override the ones found in cypress.json. This enables you to have per machine or per environment files you can control from the CLI or from the Module API.

This isn't implemented in 1.4.1? Because using it results in an error 'unknown option' :(

@brian-mann
Copy link
Member

@Konstruktour no but you can do this yourself with the Plugin Configuration API.

https://docs.cypress.io/api/plugins/configuration-api.html#

We'll likely do this at some point - but I'd like to make it part of breaking changes because currently there are too many ways to set things and its overwhelming for users.

@facetcounter
Copy link

Just for the record, I tried setting CYPRESS_baseUrl in cypress.env.json before I found this thread; that seemed intuitive with the idea of having cypress.env.json override environmental variables set in other ways and with the idea that environmental variables could override config values...

I like the solution of just having a local equivalent to cypress.json and pointing to it using a command line option, and I understand that this solves the same issue in a different way.

The reason I'm mentioning this is that I ended up with a set of environment variables in my runtime that didn't reflect my current configuration... in other words I could see a CYPRESS_baseUrl env var, but the value set in my config file was not overridden. It seems mildly valuable to have this tie be unambiguous if possible.

@brian-mann
Copy link
Member

@facetcounter I understand what you're going for - it makes sense in theory... but doing it this way is really just creating another cypress.overrides.json which was/is not the point of cypress.env.json.

We are determined to remove support for cypress.env.json in a breaking version down the road and we are instead going to support a --config-file argument.

The best programmatic option for you today is the Plugin Configuration API. https://docs.cypress.io/api/plugins/configuration-api.html#

You can literally do anything you want with both config and env vars programmatically under your control in any fashion you desire. That is now the best solution.

@kud
Copy link

kud commented Nov 6, 2018

Glad to see I ain't the only one to have a problem with this. (see my issue above)

To be fair, the best way could/should be to accept a javascript file retuning json instead of a json file. Like that we could use for instance process.env or localEnv = require('cypress.local.js) for baseUrl or whatever. :)

@westhomas
Copy link

westhomas commented Mar 28, 2019

We use docker .env files everywhere in our workflow. So I decided to keep that pattern with our cypress tests. Here's my solution:

// plugins/index.js

const fs = require('fs')
const path = require('path')

module.exports = (on, config) => {
    // Parse the .env file and inject the settings into the Cypress.env object
    // Example .env file:
    // SETTING_ONE=123
    // SETTING_TWO=blah
    
    const file = fs.readFileSync(path.resolve('.env'), 'utf-8')
    file.split('\n').map(line => {
        if (line.trim() !== '') {
            const [key, value] = line.split('=')
            config.env[key] = value
        }
    })
    return config
}

@jaypan
Copy link

jaypan commented Apr 5, 2019

It doesn't really make sense to me that the testing URL cannot be set per environment in the environment settings. I want to commit cypress.json to GIT, and have cypress.env.json handle environment specific variables. The URL of the testing server is dependent upon the environment. If I put that URL into cypress.json, and commit it to Git, then it will only work on environments that happen to use that same URL. This doesn't make sense to me.

I ended up hacking together a solution based on westhomas' post above. With the below code, if you've set baseUrl in cypress.env.json, it will override whatever you may have set in cypress.json:

const fs = require('fs');
const path = require('path');

module.exports = (on, config) => {
  "use strict";

  const file = fs.readFileSync(path.resolve('cypress.env.json'), 'utf-8');
  file.split('\n').map((line) => {
    if (line.trim() !== '') {
      const matches = line.match(/"baseUrl": "(.+)?"/);
      if (matches) {
        config.baseUrl = matches[1];
      }
    }
  });

  return config;
};

@westhomas
Copy link

@jaypan Nice. I was a bit surprised to find out there was no ability to overwrite settings in a cascading manner out-of-the-box. I think that would be helpful.

@pietmichal
Copy link

Any ETA on making this less confusing?

@AndreKR
Copy link

AndreKR commented Nov 9, 2019

Do I understand this correctly? Setting the baseUrl locally should be done using a plugin? Is there some kind of built-in plugin that can read it from a file, maybe even from cypress.env.json?

@dwelle
Copy link

dwelle commented Nov 9, 2019

@AndreKR if you don't need to do any overriding, just set it once in cypress.json. If you do, then do it in cypress/plugins/index.js, see https://docs.cypress.io/api/plugins/configuration-api.html#Usage.

@AndreKR
Copy link

AndreKR commented Nov 9, 2019

But cypress.json also contains global settings, like the screen size, that I commit to the repository, while the URL is a local setting. What do I put in .gitignore?

@PawelWesolowski
Copy link

It is a terrible idea to remove support for .env.json simply because people are using this strategy to setup local runs. Look at popularity of https://www.npmjs.com/package/dotenv - people like to create a local .dev file and gitignore it.
Problem with baseUrl is that it should be an env variable in the first place - not a config variable. Even in the docs it says:

baseUrl can be set in your configuration file (cypress.json by default) - and then you can use an environment variable to override it.
CYPRESS_baseUrl=https://staging.app.com cypress run

Cypress users need to override it very often hence it should be an env var - not a config var.

@markjaquith
Copy link

markjaquith commented Jan 26, 2020

This has been annoying me for years.

EDIT

This seems like a supported solution:

cypress/plugins/index.js

module.exports = (on, config) => {
  const baseUrl = config.env.baseUrl || null;

  if (baseUrl) {
    config.baseUrl = baseUrl;
  }

  return config;
}

cypress.env.json:

{
	"baseUrl": "https://whatever.test",
}
My first attempt, which was very hacky This is probably a bit of a hack, but this works, for now. This is probably unsupported and a code time bomb, so caveat emptor:

cypress/support/index.js:

// Allow baseUrl override.
import './baseUrl'

// Import commands.js using ES2015 syntax:
import './commands'

`cypress/support/baseUrl.js':

// Allow a 'baseUrl' env var in cypress.env.json to override the Cypress baseUrl.
const baseUrl = Cypress.env('baseUrl');
baseUrl && Cypress.config('baseUrl', baseUrl);

cypress.env.json:

{
	"baseUrl": "https://whatever.test",
}

NOTE: You cannot set baseUrl in cypress.json if you do this (or rather, if you do, it will have to exist on the user's system, which defeats the whole purpose of letting them set it to something that exists on their system).

Because this is done in support, and outside spec files, Cypress.config() calls "stick", and stay set through all spec runs.

When you use the GUI to run tests, it's a little weird, because it first loads on localhost:some-port and then updates to your https://whatever.test. But it seems to work fine.

Ultimately, I wish Cypress would let you override config locally in a more elegant way. #5218 is probably the way forward.

@jennifer-shehane
Copy link
Member

This issue will be closed to further comment as the exact issue here was resolved and tested in version 1.2.0.

If you're experiencing a bug similar to this in Cypress or want to request a change in behavior in how Cypress works today, please open a new issue with a fully reproducible example that we can run. Closed issues are not the best place for discussion of wanted feature changes.

@cypress-io cypress-io locked as resolved and limited conversation to collaborators Jan 28, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests