How To Load Cypress Settings Per Environment

Load the testing configuration and environment variables from separate JSON files.

Let's say you want to separate Cypress configuration and Cypress.env variables for each environment. You might want to store the values in separate JSON files like local.settings.json, staging.settings.json, prod.settinga.json, etc. Here is how you can load these settings in Cypress v12.

🎁 You can find the source code for this post in the repo bahmutov/cypress-load-env-by-name-example.

Application

My application is a page that shows a <h1> greeting. If it is running on staging staging.acme.co, then it shows the greeting Hi (staging), and locally it shows Hi (local) text. To simulate staging and production environments I made several self-signed SSL certificates following the blog post Post not found: cypress-hosts-options:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ mkcert -key-file ./.cert/staging-key.pem -cert-file ./.cert/staging-cert.pem "staging.acme.co"

Created a new certificate valid for the following names 📜
- "staging.acme.co"

The certificate is at "./.cert/staging-cert.pem" and the key at "./.cert/staging-key.pem" ✅

$ mkcert -key-file ./.cert/prod-key.pem -cert-file ./.cert/prod-cert.pem "acme.co"

Created a new certificate valid for the following names 📜
- "acme.co"

The certificate is at "./.cert/prod-cert.pem" and the key at "./.cert/prod-key.pem" ✅

Different environments are running locally by starting with self-signed certificates:

package.json
1
2
3
4
5
6
7
{
"scripts": {
"start": "serve public",
"start:staging": "serve public --ssl-cert ./.cert/staging-cert.pem --ssl-key ./.cert/staging-key.pem",
"start:prod": "serve public --ssl-cert ./.cert/prod-cert.pem --ssl-key ./.cert/prod-key.pem"
}
}

Cypress setup

Let's put the baseUrl and the expected greeting text into 3 different JSON files, one per environment we want to test.

local.settings.json
1
2
3
4
5
6
{
"baseUrl": "http://localhost:3000",
"env": {
"greeting": "Hi (local)"
}
}
staging.settings.json
1
2
3
4
5
6
{
"baseUrl": "https://staging.acme.co:3000",
"env": {
"greeting": "Hi (staging)"
}
}
prod.settings.json
1
2
3
4
5
6
{
"baseUrl": "https://acme.co:3000",
"env": {
"greeting": "Hi"
}
}

We want to load these settings based on the name "local", "staging", "prod". We can implement this logic in our cypress.config.js file

cypress.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const { defineConfig } = require('cypress')

module.exports = defineConfig({
// point fake "staging" and "prod" URLs
// back at our local SSL servers
hosts: {
'staging.acme.co': '127.0.0.1',
'acme.co': '127.0.0.1',
},
e2e: {
// default baseUrl, etc
supportFile: false,
fixturesFolder: false,
setupNodeEvents(on, config) {
const environmentName = config.env.environmentName || 'local'
const environmentFilename = `./${environmentName}.settings.json`
console.log('loading %s', environmentFilename)
const settings = require(environmentFilename)
// TODO merge the settings with the config object
}
}
})

Super, we got the JSON with an object. It has the baseUrl and env object. We probably want to overwrite the baseUrl and merge the environment objects. Here is our complete setupNodeEvents callback code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
setupNodeEvents(on, config) {
const environmentName = config.env.environmentName || 'local'
const environmentFilename = `./${environmentName}.settings.json`
console.log('loading %s', environmentFilename)
const settings = require(environmentFilename)
if (settings.baseUrl) {
config.baseUrl = settings.baseUrl
}
if (settings.env) {
config.env = {
...config.env,
...settings.env,
}
}
console.log('loaded settings for environment %s', environmentName)

// IMPORTANT: return the updated config object
// for Cypress to use it
return config
}

It is very important to return the updated config object to the caller, so Cypress knows to use the changes configuration.

We only have a single test.

cypress/e2e/spec.cy.js
1
2
3
4
it('checks the page', () => {
cy.visit('/')
cy.contains('h1', Cypress.env('greeting'))
})

Local server

Let's start the local server

1
$ npm start

Now let's open Cypress with the local settings

1
2
3
$ npx cypress open --env environmentName=local
loading ./local.settings.json
loaded settings for environment local

Testing the local application

Close Cypress and the application and start the staging app. First, the app:

1
2
3
4
$ npm run start:staging

> [email protected] start:staging
> serve public --ssl-cert ./.cert/staging-cert.pem --ssl-key ./.cert/staging-key.pem

Then open Cypress

1
2
3
$ npx cypress open --env environmentName=staging
loading ./staging.settings.json
loaded settings for environment staging

Testing the staging application

Finally, let's start the production application

1
2
3
4
$ npm run start:prod

> [email protected] start:prod
> serve public --ssl-cert ./.cert/prod-cert.pem --ssl-key ./.cert/prod-key.pem

And open Cypress with production settings

1
2
3
$ npx cypress open --env environmentName=prod
loading ./prod.settings.json
loaded settings for environment prod

Testing the production application

Beautiful, isn't it.

See also