Learn how to log into different environments with Cypress, protecting sensitive data, such as username and password
In another blog post of the Pinches of Cypress series, I wrote about how to change the baseUrl via the command line with Cypress.
But what about sensitive data, such as credentials for authentication in the same application, deployed in different environments? That's precisely what I'll teach you in this content!
Let's imagine an application deployed in three different environments, each with its specific credentials:
- Local (your computer)
- Staging
- Production
The user's email and password are required to log into the application (in any of the environments).
Let's also assume that in the Cypress configuration file (cypress.json
, or cypress.config.js
â from version 10 onwards), the baseUrl
points by default to the local development environment, as shown below:
{
"baseUrl": "http://localhost:3000"
}
Above is the configuration file before Cypress version 10.
// cypress/config/cypress.config.js (from version 10 onwards)
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000'
}
})
Above is the configuration file from Cypress version 10 onwards.
However, the staging and production environments have the following URLs, respectively:
https://example.staging.com
https://example.com
Let's also assume that in the package.json
file, we have the following scripts for running tests in each specific environment:
"scripts": {
"test": "cypress run",
"test:staging": "cypress run --config baseUrl=https://example.staging.com --env environment=staging",
"test:prod": "cypress run --config baseUrl=https://example.com --env environment=prod"
}
The test
script executes the tests against the local environment since the baseUrl
is not overridden.
In the test:staging
and test:prod
scripts, the baseUrl
is overridden via the command line. Additionally, we pass a variable called environment
with a value that identifies each environment (staging
and prod
).
In the local environment, we can have an unversioned file (included in the .gitignore
file) called cypress.env.json
, which would have the following structure:
{
"LOCAL_USER": {
"email": "local@user.com",
"password": "the-password-of-the-above-user"
},
"STAGING_USER": {
"email": "some-user@example.staging.com",
"password": "the-password-of-the-above-user"
},
"PROD_USER": {
"email": "another-user@example.com",
"password": "the-password-of-the-above-user"
}
}
Note: I also recommend creating a versioned file called cypress.env.example.json
with example values so other team members can use it as a template for creating their own unversioned cypress.env.json
files.
Note 2: In a continuous integration environment, such values (STAGING_USER
and PROD_USER
) could be set as secrets, with the prefix CYPRESS_
, i.e., CYPRESS_STAGING_USER
and CYPRESS_PROD_USER
, with their respective values.
Now that we have the credentials let's implement a custom command for logging in based on the environment where the tests are being executed.
// cypress/support/commands.js
Cypress.Commands.add('login', () => {
cy.log(`Logging into the ${Cypress.env('environment') ? Cypress.env('environment') : 'local'} environment`)
if (Cypress.env('environment') === 'staging') {
Cypress.env('user', Cypress.env('STAGING_USER'))
} else if (Cypress.env('environment') === 'prod') {
Cypress.env('user', Cypress.env('PROD_USER'))
} else {
Cypress.env('user', Cypress.env('LOCAL_USER'))
}
cy.visit('/login')
cy.get('[data-cy="emailField"]')
.should('be.visible')
.type(Cypress.env('user').email, { log: false })
cy.get('[data-cy="passwordField"]')
.should('be.visible')
.type(Cypress.env('user').password, { log: false })
cy.contains('button', 'Login')
.should('be.visible')
.click()
})
In the login custom command, we dynamically set the user
variable depending on the environment passed via the command line in the npm script. Thus, we can use this variable in commands that enter the user's email and password.
Additionally, we log the phrase "Logging into the [environment] environment" in the Cypress command log, depending on the environment where the tests are running. If the environment
variable is passed, we use it; otherwise, the default is the local
value.
So, if the value of the environment
variable is, for example, staging
, the following would be "printed" in the Cypress command log:
Logging into the staging environment.
And the login test would look something like this:
// cypress/integration/login.spec.js (before Cypress version 10)
// or cypress/e2e/login.cy.js (from Cypress version 10 onwards)
it('logs in', () => {
cy.login()
cy.get('[data-cy="avatar"]')
.should('be.visible')
})
As you can see, in the actual test, we only call the custom command cy.login
, which authenticates the application with the correct credentials.
That's it! I hope you learned something new.
For more details on how Cypress works, I recommend reading the official documentation.
Did you like the content? Leave a comment.
This blog post was originally published in Portuguese at the Talking About Testing blog.
Would you like to learn Cypress in a hands-on course?
I introduce you to my newest course, ð² Cypress, from Zero to the Cloud âïž.
I hope you like it, and happy testing!
Top comments (2)
Thanks for article, really helpful, now I do have a doubt, hopefully you can help me understand
So you mention to use for instance
CYPRESS_STAGING_USER
in let's say github actions, I know thatCYPRESS_
will be removed once it is read by cypress but my two questions are:Should I get the secrets like this?
run: echo "CYPRESS_STAGING_USER=${{secrets.CYPRESS_STAGING_USER}}" >> $GITHUB_ENV
?once I got the env in a continuos integration setup, and in this case where the cypress.env.json is not versioned there, where do these values are coming from if they are not in the repo?
.type(Cypress.env('user').email
.type(Cypress.env('user').password
No, your secret on GitHub action will already be in a JSON format.