diff --git a/docs/api/commands/exec.mdx b/docs/api/commands/exec.mdx
index 7c46fdc9d7..ea7fe4e82d 100644
--- a/docs/api/commands/exec.mdx
+++ b/docs/api/commands/exec.mdx
@@ -138,7 +138,7 @@ cy.exec('man bear pig', { failOnNonZeroExit: false }).then((result) => {
})
```
-#### Specify environment variables
+#### Specify system environment variables
```javascript
cy.exec('echo $USERNAME', { env: { USERNAME: 'johndoe' } })
diff --git a/docs/api/commands/prompt.mdx b/docs/api/commands/prompt.mdx
index ca39d64b95..4fc51d75f1 100644
--- a/docs/api/commands/prompt.mdx
+++ b/docs/api/commands/prompt.mdx
@@ -347,43 +347,43 @@ Placeholders let you use dynamic and sensitive values while maintaining cache ef
```typescript
describe('Campaign Management', () => {
it('creates multiple campaigns with different discount percentages', () => {
- const adminPassword = Cypress.env('ADMIN_PASSWORD')
-
- const campaigns = [
- { name: 'Fall Sale', discountPct: 10 },
- { name: 'Spring Sale', discountPct: 15 },
- ]
-
- // This loop demonstrates the caching benefit:
- // - First iteration: AI generates and caches the code
- // - Subsequent iterations: Reuse cached code with different placeholder values
- campaigns.forEach((campaign) => {
- const campaignName = campaign.name
- const campaignDiscountPct = campaign.discountPct
-
- cy.prompt(
- [
- `Visit ${Cypress.env('ADMIN_URL')}/admin/login`,
- // Using {{adminPassword}} prevents this sensitive value from being sent to AI
- 'Type {{adminPassword}} into the password field',
- 'Click Sign In',
- 'Open the Promotions tab',
- 'Click to create a new campaign',
- // Using {{campaignName}} and {{campaignDiscountPct}}
- // allows for high performance caching with different values
- 'Type {{campaignName}} into the name field',
- 'Set discount to {{campaignDiscountPct}}% for all products',
- 'Save the campaign',
- 'Verify the campaign "{{campaignName}}" appears in the list',
- ],
- {
- placeholders: {
- adminPassword,
- campaignName,
- campaignDiscountPct,
- },
- }
- )
+ cy.task('getSecret', 'ADMIN_PASSWORD').then((adminPassword) => {
+ const campaigns = [
+ { name: 'Fall Sale', discountPct: 10 },
+ { name: 'Spring Sale', discountPct: 15 },
+ ]
+
+ // This loop demonstrates the caching benefit:
+ // - First iteration: AI generates and caches the code
+ // - Subsequent iterations: Reuse cached code with different placeholder values
+ campaigns.forEach((campaign) => {
+ const campaignName = campaign.name
+ const campaignDiscountPct = campaign.discountPct
+
+ cy.prompt(
+ [
+ `Visit ${Cypress.env('ADMIN_URL')}/admin/login`,
+ // Using {{adminPassword}} prevents this sensitive value from being sent to AI
+ 'Type {{adminPassword}} into the password field',
+ 'Click Sign In',
+ 'Open the Promotions tab',
+ 'Click to create a new campaign',
+ // Using {{campaignName}} and {{campaignDiscountPct}}
+ // allows for high performance caching with different values
+ 'Type {{campaignName}} into the name field',
+ 'Set discount to {{campaignDiscountPct}}% for all products',
+ 'Save the campaign',
+ 'Verify the campaign "{{campaignName}}" appears in the list',
+ ],
+ {
+ placeholders: {
+ adminPassword,
+ campaignName,
+ campaignDiscountPct,
+ },
+ }
+ )
+ })
})
})
})
@@ -567,7 +567,7 @@ describe('Feature: User Registration', () => {
],
{
placeholders: {
- password: Cypress.env('PASSWORD'),
+ password: 'PASSWORD_PLACEHOLDER', // Use cy.task('getSecret', 'PASSWORD') to get the actual value
},
}
)
@@ -687,20 +687,20 @@ The commands in the generated code may look like:
### Login flow
```javascript
-const password = Cypress.env('adminPassword')
-
-cy.prompt(
- [
- 'visit the login page',
- 'type "user@example.com" in the email field',
- 'type {{password}} in the password field',
- 'click the login button',
- 'verify we are redirected to the dashboard',
- ],
- {
- placeholders: { password },
- }
-)
+cy.task('getSecret', 'ADMIN_PASSWORD').then((password) => {
+ cy.prompt(
+ [
+ 'visit the login page',
+ 'type "user@example.com" in the email field',
+ 'type {{password}} in the password field',
+ 'click the login button',
+ 'verify we are redirected to the dashboard',
+ ],
+ {
+ placeholders: { password },
+ }
+ )
+})
```
### E-commerce checkout
@@ -794,7 +794,7 @@ cy.origin('https://cloud.cypress.io/login', () => {
],
{
placeholders: {
- password: Cypress.env('PASSWORD'),
+ password: 'PASSWORD_PLACEHOLDER',
},
}
)
diff --git a/docs/api/commands/task.mdx b/docs/api/commands/task.mdx
index aecb1f2aaa..2a28cfa755 100644
--- a/docs/api/commands/task.mdx
+++ b/docs/api/commands/task.mdx
@@ -420,7 +420,7 @@ cy.task('queryDatabase', { dbName, query })
```ts
import mysql from 'mysql'
// the connection strings for different databases could
-// come from the Cypress configuration or from environment variables
+// come from system environment variables
const connections = {
stagingA: {
host: 'staging.my.co',
@@ -507,6 +507,43 @@ on('task', {
:::
+### Accessing secrets securely
+
+You can use `cy.task()` to securely access secrets from the Node.js process environment variables. This is the recommended approach for accessing sensitive values like passwords, API keys, or tokens, as they remain in the Node.js process and are not exposed to the browser.
+
+:::cypress-config-plugin-example
+
+```ts
+on('task', {
+ getSecret(secretName: string) {
+ const secret = process.env[secretName]
+
+ if (!secret) {
+ throw new Error(`Secret ${secretName} is not set`)
+ }
+
+ return secret
+ },
+})
+```
+
+:::
+
+```javascript
+// in test
+cy.task('getSecret', 'API_KEY').then((apiKey) => {
+ cy.request({
+ method: 'POST',
+ url: 'https://api.example.com/data',
+ headers: {
+ Authorization: `Bearer ${apiKey}`,
+ },
+ })
+})
+```
+
+In CI environments, you can set these secrets as environment variables in your CI provider's secret management system.
+
## Rules
diff --git a/docs/api/commands/wrap.mdx b/docs/api/commands/wrap.mdx
index ca80331d16..df29d1cf78 100644
--- a/docs/api/commands/wrap.mdx
+++ b/docs/api/commands/wrap.mdx
@@ -126,16 +126,15 @@ import { userService } from '../../src/_services/user.service'
it('can assert against resolved object using .should', () => {
cy.log('user service login')
const username = Cypress.env('username')
- const password = Cypress.env('password')
-
- // wrap the promise returned by the application code
- cy.wrap(userService.login(username, password))
- // check the yielded object
- .should('be.an', 'object')
- .and('have.keys', ['firstName', 'lastName', 'username', 'id', 'token'])
- .and('contain', {
- username: 'test',
- firstName: 'Test',
+ cy.task('getSecret', 'PASSWORD').then((password) => {
+ // wrap the promise returned by the application code
+ cy.wrap(userService.login(username, password))
+ // check the yielded object
+ .should('be.an', 'object')
+ .and('have.keys', ['firstName', 'lastName', 'username', 'id', 'token'])
+ .and('contain', {
+ username: 'test',
+ firstName: 'Test',
lastName: 'User',
})
diff --git a/docs/api/cypress-api/env.mdx b/docs/api/cypress-api/env.mdx
index 6e977a7d39..8e6b5d69ed 100644
--- a/docs/api/cypress-api/env.mdx
+++ b/docs/api/cypress-api/env.mdx
@@ -36,7 +36,7 @@ specs.
Difference between OS-level and Cypress environment variables
In Cypress, "environment variables" are variables that are accessible via
-`Cypress.env`. These are not the same as OS-level environment variables.
+`Cypress.env` and the browser. These are not the same as OS-level environment variables.
However,
[it is possible to set Cypress environment variables from OS-level environment variables](/app/references/environment-variables#Option-3-CYPRESS_).
@@ -76,8 +76,8 @@ Set multiple environment variables with an object literal.
```ts
{
env: {
- foo: 'bar',
- baz: 'quux',
+ apiUrl: 'https://api.example.com',
+ apiVersion: 'v1',
},
}
```
@@ -85,7 +85,7 @@ Set multiple environment variables with an object literal.
:::
```javascript
-Cypress.env() // => {foo: 'bar', baz: 'quux'}
+Cypress.env() // => {apiUrl: 'https://api.example.com', apiVersion: 'v1'}
```
### Name
@@ -102,13 +102,13 @@ command line. Cypress will automatically convert values into Number or Boolean.
:::
```javascript
-CYPRESS_HOST=laura.dev CYPRESS_IS_CI=true CYPRESS_MY_ID=123 cypress run
+CYPRESS_HOST=laura.dev CYPRESS_IS_CI=true CYPRESS_API_VERSION=123 cypress run
```
```javascript
Cypress.env('HOST') // => "laura.dev"
Cypress.env('IS_CI') // => true
-Cypress.env('MY_ID') // => 123
+Cypress.env('API_VERSION') // => 123
```
### Name and Value
@@ -129,8 +129,7 @@ only be in effect for the remainder of the tests _in the same spec file._
```ts
{
env: {
- foo: 'bar',
- baz: 'quux',
+ host: 'laura.dev',
},
}
```
@@ -152,8 +151,8 @@ Cypress.env('host') // => http://server.dev.local
```ts
{
env: {
- foo: 'bar',
- baz: 'quux',
+ host: 'laura.dev',
+ apiVersion: 'v1',
},
}
```
@@ -163,10 +162,10 @@ Cypress.env('host') // => http://server.dev.local
```javascript
Cypress.env({
host: 'http://server.dev.local',
- foo: 'foo',
+ apiVersion: 'v2',
})
-Cypress.env() // => {foo: 'foo', baz: 'quux', host: 'http://server.dev.local'}
+Cypress.env() // => {apiVersion: 'v2', host: 'http://server.dev.local'}
```
### From a plugin
@@ -181,8 +180,10 @@ of the tests in your spec run.
:::cypress-config-plugin-example
```js
-config.env.sharedSecret =
- process.env.NODE_ENV === 'qa' ? 'hoop brick tort' : 'sushi cup lemon'
+config.env.apiBaseUrl =
+ process.env.NODE_ENV === 'qa'
+ ? 'https://api-qa.example.com'
+ : 'https://api.example.com'
return config
```
@@ -190,16 +191,21 @@ return config
:::
```js
-// cypress/e2e/secrets.cy.js
-describe('Environment variable set in plugin', () => {
- let sharedSecret
+// cypress/e2e/api-tests.cy.js
+describe('API tests', () => {
+ let apiBaseUrl
before(() => {
- sharedSecret = Cypress.env('sharedSecret')
+ apiBaseUrl = Cypress.env('apiBaseUrl')
})
- it.only('can be accessed within test.', () => {
- cy.log(sharedSecret)
+ it('can make requests to the correct environment', () => {
+ cy.request({
+ method: 'GET',
+ url: `${apiBaseUrl}/users`,
+ }).then((response) => {
+ expect(response.status).to.eq(200)
+ })
})
})
```
diff --git a/docs/api/node-events/browser-launch-api.mdx b/docs/api/node-events/browser-launch-api.mdx
index a1507e2d1f..ecc3b49a7a 100644
--- a/docs/api/node-events/browser-launch-api.mdx
+++ b/docs/api/node-events/browser-launch-api.mdx
@@ -54,7 +54,7 @@ following properties:
| `preferences` | `object` | An object describing browser preferences. Differs between browsers. See [Change browser preferences](#Change-browser-preferences) for details. |
| `args` | `string[]` | An array of strings that will be passed as command-line args when the browser is launched. Has no effect on Electron. See [Modify browser launch arguments](#Modify-browser-launch-arguments) for details. |
| `extensions` | `string[]` | An array of paths to folders containing unpacked WebExtensions to be loaded before the browser starts. See [Add browser extensions](#Add-browser-extensions) for details. |
-| `env` | `object` | An object of environment variables to pass to the launched browser. See [Configure browser environment](#Configure-browser-environment) for details. |
+| `env` | `object` | An object of system environment variables to pass to the launched browser. See [Configure browser environment](#Configure-browser-environment) for details. |
## Usage
@@ -162,7 +162,7 @@ Here are preferences available for the currently supported browsers:
:::info
-If you want to ignore Chrome preferences altogether, you can set `IGNORE_CHROME_PREFERENCES` as an environment variable when running Cypress.
+If you want to ignore Chrome preferences altogether, you can set `IGNORE_CHROME_PREFERENCES` as a system environment variable when running Cypress.
:::
@@ -206,7 +206,7 @@ line switches. The supported switches depend on the Electron version, see
[Electron documentation](https://www.electronjs.org/docs/api/command-line-switches).
You can pass Electron-specific launch arguments using the
-`ELECTRON_EXTRA_LAUNCH_ARGS` environment variable. For example, to disable HTTP
+`ELECTRON_EXTRA_LAUNCH_ARGS` system environment variable. For example, to disable HTTP
browser cache and ignore certificate errors, you can set the environment
variables before running Cypress like below:
diff --git a/docs/api/node-events/configuration-api.mdx b/docs/api/node-events/configuration-api.mdx
index 6181f91110..bb102fa277 100644
--- a/docs/api/node-events/configuration-api.mdx
+++ b/docs/api/node-events/configuration-api.mdx
@@ -104,18 +104,27 @@ you.
const fs = require('fs-extra')
const path = require('path')
-function getConfigurationByFile(file) {
- const pathToConfigFile = path.resolve('..', 'config', `${file}.json`)
+function getConfigurationByFile(file, projectRoot) {
+ const pathToConfigFile = path.join(
+ projectRoot,
+ 'cypress',
+ 'config',
+ `${file}.json`
+ )
return fs.readJson(pathToConfigFile)
}
-// plugins file
-module.exports = (on, config) => {
- // accept a configFile value or use development by default
- const file = config.env.configFile || 'development'
+// cypress.config.js or cypress.config.ts
+module.exports = {
+ e2e: {
+ setupNodeEvents(on, config) {
+ // accept a environment value or use development by default
+ const file = config.env.environment || 'development'
- return getConfigurationByFile(file)
+ return getConfigurationByFile(file, config.projectRoot)
+ },
+ },
}
```
@@ -126,15 +135,15 @@ cypress run
```
```shell
-cypress run --env configFile=qa
+cypress run --env environment=qa
```
```shell
-cypress run --env configFile=staging
+cypress run --env environment=staging
```
```shell
-cypress run --env configFile=production
+cypress run --env environment=production
```
Each of these environments would read in the configuration at these files:
diff --git a/docs/app/continuous-integration/aws-codebuild.mdx b/docs/app/continuous-integration/aws-codebuild.mdx
index d51931ff7a..a7678b496f 100644
--- a/docs/app/continuous-integration/aws-codebuild.mdx
+++ b/docs/app/continuous-integration/aws-codebuild.mdx
@@ -340,7 +340,7 @@ phases:
:::caution
Dashboard analytics are dependent on your CI environment reliably providing
-commit SHA data (typically via an environment variable) which is not always
+commit SHA data (typically via a system environment variable) which is not always
present by default. This is not a problem for most users, but if you are facing
integration issues with your CodeBuild setup, please make sure the git
information is being sent properly by following
diff --git a/docs/app/continuous-integration/github-actions.mdx b/docs/app/continuous-integration/github-actions.mdx
index a07b66a2c3..95dc42ccfe 100644
--- a/docs/app/continuous-integration/github-actions.mdx
+++ b/docs/app/continuous-integration/github-actions.mdx
@@ -502,7 +502,7 @@ To see additional how-to examples, you can also refer to our
### Re-run jobs passing with empty tests
We recommend passing the `GITHUB_TOKEN` secret (created by the GH Action
-automatically) as an environment variable. This will allow the accurate
+automatically) as a system environment variable in CI/CD. This will allow the accurate
identification of each build to avoid confusion when re-running a build.
```yml
@@ -527,8 +527,8 @@ jobs:
### Pull requests commit message is `merge SHA into SHA`
-You can overwrite the commit message sent to Cypress Cloud by setting an
-environment variable. See
+You can overwrite the commit message sent to Cypress Cloud by setting a
+system environment variable in CI/CD. See
[Issue #124](https://github.com/cypress-io/github-action/issues/124) for more
details.
diff --git a/docs/app/continuous-integration/gitlab-ci.mdx b/docs/app/continuous-integration/gitlab-ci.mdx
index 863a5e35bf..e0501da654 100644
--- a/docs/app/continuous-integration/gitlab-ci.mdx
+++ b/docs/app/continuous-integration/gitlab-ci.mdx
@@ -157,7 +157,7 @@ assign it to the `build` stage.
stages:
- build
-## Set environment variables for folders in "cache" job settings
+## Set system environment variables for folders in "cache" job settings
## for npm modules and Cypress binary
variables:
npm_config_cache: '$CI_PROJECT_DIR/.npm'
@@ -201,7 +201,7 @@ stages:
- build
- test
-## Set environment variables for folders in "cache" job settings
+## Set system environment variables for folders in "cache" job settings
## for npm modules and Cypress binary
variables:
npm_config_cache: '$CI_PROJECT_DIR/.npm'
diff --git a/docs/app/continuous-integration/overview.mdx b/docs/app/continuous-integration/overview.mdx
index eebb47a9c5..031be7c47d 100644
--- a/docs/app/continuous-integration/overview.mdx
+++ b/docs/app/continuous-integration/overview.mdx
@@ -165,7 +165,7 @@ the server like this:
}
```
-When working with local `https` in webpack, set an environment variable to allow
+When working with local `https` in webpack, set a system environment variable to allow
local certificate:
```json
@@ -534,7 +534,7 @@ like `git show -s --pretty=%B` to get commit message, see
Under some environment setups (e.g. `docker`/`docker-compose`) if the `.git`
directory is not available or mounted, you can pass all git related information
-under custom environment variables.
+under custom system environment variables.
- Branch: `COMMIT_INFO_BRANCH`
- Message: `COMMIT_INFO_MESSAGE`
@@ -546,7 +546,7 @@ under custom environment variables.
If the commit information is missing in the Cypress Cloud run then
[GitHub Integration](/cloud/integrations/source-control/github) or other
tasks might not work correctly. To see the relevant Cypress debug logs, set the
-environment variable `DEBUG` on your CI machine and inspect the terminal output
+system environment variable `DEBUG` on your CI machine and inspect the terminal output
to see why the commit information is unavailable.
```shell
@@ -555,7 +555,7 @@ DEBUG=commit-info,cypress:server:record
#### CI Build Information
-In some newer CI providers, Cypress can't map the environment variables required
+In some newer CI providers, Cypress can't map the system environment variables required
to link back to builds or pull requests. In this case we provided users some
environment variables to help pass that information along.
@@ -569,7 +569,7 @@ appropriate place.
#### Custom Environment Variables
You can also set custom environment variables for use in your tests. These
-enable your code to reference dynamic values.
+enable your test code and browser to reference dynamic values.
```shell
export "EXTERNAL_API_SERVER=https://corp.acme.co"
@@ -666,7 +666,7 @@ the tools below should be available.
Xvfb :99 &
```
-Second, set the X11 address in an environment variable
+Second, set the X11 address in a system environment variable
```shell
export DISPLAY=:99
diff --git a/docs/app/core-concepts/best-practices.mdx b/docs/app/core-concepts/best-practices.mdx
index 4b73c30b9e..72e9e2be63 100644
--- a/docs/app/core-concepts/best-practices.mdx
+++ b/docs/app/core-concepts/best-practices.mdx
@@ -440,8 +440,7 @@ email's functionality and visual style:
without involving the UI. Your server would have to expose this endpoint.
4. You could also use `cy.request()` to a 3rd party email recipient server that
exposes an API to read off emails. You will then need the proper
- authentication credentials, which your server could provide, or you could use
- environment variables. Some email services already provide
+ authentication credentials, which your server could provide via cy.task. Some email services already provide
Cypress plugins to access emails.
## Having Tests Rely On The State Of Previous Tests
diff --git a/docs/app/core-concepts/open-mode.mdx b/docs/app/core-concepts/open-mode.mdx
index f9cc79d9d8..58e4b52ada 100644
--- a/docs/app/core-concepts/open-mode.mdx
+++ b/docs/app/core-concepts/open-mode.mdx
@@ -92,7 +92,7 @@ When your project records to [Cypress Cloud](/cloud/get-started/introduction), t
- **Authentication:** Sign in to Cypress Cloud using the profile in the top right.
- **Branch scoping:** Cypress reads your current git branch to scope results. It first shows runs from that branch. If no runs are found, it falls back to the default branch, then to all branches.
-- **Git information:** To scope runs correctly, the app needs git metadata. Ensure git is available and that you send git information to Cypress Cloud. In some environments, such as `docker` or `docker-compose`, pass git information with [custom environment variables](/app/continuous-integration/overview#Git-information).
+- **Git information:** To scope runs correctly, the app needs git metadata. Ensure git is available and that you send git information to Cypress Cloud. In some environments, such as `docker` or `docker-compose`, pass git information with [custom system environment variables](/app/continuous-integration/overview#Git-information).
- **Run titles:** Each run title comes from the git commit message. Click a run to open it in Cypress Cloud.
{
### Can I override environment variables or create configuration for different environments?
Yes, you can pass configuration to Cypress via environment variables, CLI
-arguments, JSON files and other means.
+arguments and other means.
[Read the Environment Variables guide.](/app/references/environment-variables)
diff --git a/docs/app/guides/authentication-testing/amazon-cognito-authentication.mdx b/docs/app/guides/authentication-testing/amazon-cognito-authentication.mdx
index b147a98980..cb24359e4c 100644
--- a/docs/app/guides/authentication-testing/amazon-cognito-authentication.mdx
+++ b/docs/app/guides/authentication-testing/amazon-cognito-authentication.mdx
@@ -159,10 +159,21 @@ const awsConfig = require('./aws-exports-es5.js')
```js
{
env: {
- cognito_username: process.env.AWS_COGNITO_USERNAME,
- cognito_password: process.env.AWS_COGNITO_PASSWORD,
awsConfig: awsConfig.default
- }
+ },
+ setupNodeEvents(on, config) {
+ on('task', {
+ getAuthCredentials() {
+ const username = process.env.AWS_COGNITO_USERNAME
+ const password = process.env.AWS_COGNITO_PASSWORD
+ if (!username || !password) {
+ throw new Error('AWS_COGNITO_USERNAME and AWS_COGNITO_PASSWORD must be set')
+ }
+ return { username, password }
+ },
+ })
+ return config
+ },
}
```
@@ -253,10 +264,9 @@ describe('Cognito, cy.origin() login', function () {
cy.task('db:seed')
// login via Amazon Cognito via cy.origin()
- cy.loginByCognito(
- Cypress.env('cognito_username'),
- Cypress.env('cognito_password')
- )
+ cy.task('getAuthCredentials').then(({ username, password }) => {
+ cy.loginByCognito(username, password)
+ })
})
it('shows onboarding', function () {
@@ -454,10 +464,9 @@ describe('Cognito, programmatic login', function () {
cy.task('db:seed')
// Programmatically login via Amazon Cognito API
- cy.loginByCognitoApi(
- Cypress.env('cognito_username'),
- Cypress.env('cognito_password')
- )
+ cy.task('getAuthCredentials').then(({ username, password }) => {
+ cy.loginByCognitoApi(username, password)
+ })
})
it('shows onboarding', function () {
diff --git a/docs/app/guides/authentication-testing/auth0-authentication.mdx b/docs/app/guides/authentication-testing/auth0-authentication.mdx
index 91a084ea05..e19719c10d 100644
--- a/docs/app/guides/authentication-testing/auth0-authentication.mdx
+++ b/docs/app/guides/authentication-testing/auth0-authentication.mdx
@@ -90,13 +90,30 @@ require('dotenv').config()
```js
{
env: {
- auth0_username: process.env.AUTH0_USERNAME,
- auth0_password: process.env.AUTH0_PASSWORD,
auth0_domain: process.env.REACT_APP_AUTH0_DOMAIN,
auth0_audience: process.env.REACT_APP_AUTH0_AUDIENCE,
auth0_scope: process.env.REACT_APP_AUTH0_SCOPE,
auth0_client_id: process.env.REACT_APP_AUTH0_CLIENTID,
- auth0_client_secret: process.env.AUTH0_CLIENT_SECRET,
+ },
+ setupNodeEvents(on, config) {
+ on('task', {
+ getAuthCredentials() {
+ const username = process.env.AUTH0_USERNAME
+ const password = process.env.AUTH0_PASSWORD
+ if (!username || !password) {
+ throw new Error('AUTH0_USERNAME and AUTH0_PASSWORD must be set')
+ }
+ return { username, password }
+ },
+ getSecret(secretName) {
+ const secret = process.env[secretName]
+ if (!secret) {
+ throw new Error(`Secret ${secretName} is not set`)
+ }
+ return secret
+ },
+ })
+ return config
},
}
```
@@ -177,10 +194,10 @@ describe('Auth0', function () {
beforeEach(function () {
cy.task('db:seed')
cy.intercept('POST', '/graphql').as('createBankAccount')
- cy.loginToAuth0(
- Cypress.env('auth0_username'),
- Cypress.env('auth0_password')
- )
+
+ cy.task('getAuthCredentials').then(({ username, password }) => {
+ cy.loginToAuth0(username, password)
+ })
cy.visit('/')
})
@@ -256,23 +273,23 @@ Cypress.Commands.add(
(username: string, password: string) => {
cy.log(`Logging in as ${username}`)
const client_id = Cypress.env('auth0_client_id')
- const client_secret = Cypress.env('auth0_client_secret')
const audience = Cypress.env('auth0_audience')
const scope = Cypress.env('auth0_scope')
- cy.request({
- method: 'POST',
- url: `https://${Cypress.env('auth0_domain')}/oauth/token`,
- body: {
- grant_type: 'password',
- username,
- password,
- audience,
- scope,
- client_id,
- client_secret,
- },
- }).then(({ body }) => {
+ cy.task('getSecret', 'AUTH0_CLIENT_SECRET').then((client_secret) => {
+ cy.request({
+ method: 'POST',
+ url: `https://${Cypress.env('auth0_domain')}/oauth/token`,
+ body: {
+ grant_type: 'password',
+ username,
+ password,
+ audience,
+ scope,
+ client_id,
+ client_secret,
+ },
+ }).then(({ body }) => {
const claims = jwt.decode(body.id_token)
const {
nickname,
@@ -309,6 +326,7 @@ Cypress.Commands.add(
window.localStorage.setItem('auth0Cypress', JSON.stringify(item))
cy.visit('/')
+ })
})
}
)
@@ -324,10 +342,9 @@ onboarding process and logout.
describe('Auth0', function () {
beforeEach(function () {
cy.task('db:seed')
- cy.loginByAuth0Api(
- Cypress.env('auth0_username'),
- Cypress.env('auth0_password')
- )
+ cy.task('getAuthCredentials').then(({ username, password }) => {
+ cy.loginByAuth0Api(username, password)
+ })
})
it('shows onboarding', function () {
@@ -641,14 +658,16 @@ Cypress.Commands.add('loginByAuth0Api', (username, password) => {
cy.exec('curl -4 icanhazip.com')
.its('stdout')
.then((ip) => {
- cy.request({
- method: 'DELETE',
- url: `https://${Cypress.env(
- 'auth0_domain'
- )}/api/v2/anomaly/blocks/ips/${ip}`,
- auth: {
- bearer: Cypress.env('auth0_mgmt_api_token'),
- },
+ cy.task('getSecret', 'AUTH0_MGMT_API_TOKEN').then((token) => {
+ cy.request({
+ method: 'DELETE',
+ url: `https://${Cypress.env(
+ 'auth0_domain'
+ )}/api/v2/anomaly/blocks/ips/${ip}`,
+ auth: {
+ bearer: token,
+ },
+ })
})
})
diff --git a/docs/app/guides/authentication-testing/azure-active-directory-authentication.mdx b/docs/app/guides/authentication-testing/azure-active-directory-authentication.mdx
index b23fe59cd8..e65b55cefe 100644
--- a/docs/app/guides/authentication-testing/azure-active-directory-authentication.mdx
+++ b/docs/app/guides/authentication-testing/azure-active-directory-authentication.mdx
@@ -66,15 +66,7 @@ To have access to test user credentials within our tests, we need to configure
Cypress to use the
[Azure Active Directory](https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/active-directory-whatis)
user credentials for a given user in your tenant. These user credentials should
-be set in the `cypress.env.json` file inside the root directory of your project.
-
-```json title="cypress.env.json"
-{
- "aad_username": "AAD_USERNAME",
- "aad_password": "AAD_PASSWORD",
- "aad_name": "AAD_NAME"
-}
-```
+be set as OS-level environment variables (e.g., in your `.env` file or CI secret management).
Also, to authenticate with Azure Active Directory, you'll need to enable the
[`experimentalModifyObstructiveThirdPartyCode`](/app/guides/cross-origin-testing#Modifying-Obstructive-Third-Party-Code)
@@ -87,7 +79,27 @@ authentication workflow will enter an infinite redirect loop.
{
e2e: {
experimentalModifyObstructiveThirdPartyCode: true
- }
+ },
+ setupNodeEvents(on, config) {
+ on('task', {
+ getAuthCredentials() {
+ const username = process.env.AAD_USERNAME
+ const password = process.env.AAD_PASSWORD
+ if (!username || !password) {
+ throw new Error('AAD_USERNAME and AAD_PASSWORD must be set')
+ }
+ return { username, password }
+ },
+ getSecret(secretName) {
+ const secret = process.env[secretName]
+ if (!secret) {
+ throw new Error(`Secret ${secretName} is not set`)
+ }
+ return secret
+ },
+ })
+ return config
+ },
}
```
@@ -145,10 +157,12 @@ function loginViaAAD(username: string, password: string) {
// Ensure Microsoft has redirected us back to the sample app with our logged in user.
cy.url().should('equal', 'http://localhost:3000/')
- cy.get('#welcome-div').should(
- 'contain',
- `Welcome ${Cypress.env('aad_username')}!`
- )
+ cy.task('getAuthCredentials').then(({ username }) => {
+ cy.get('#welcome-div').should(
+ 'contain',
+ `Welcome ${username}!`
+ )
+ })
}
Cypress.Commands.add('loginToAAD', (username: string, password: string) => {
@@ -173,22 +187,25 @@ as a user via Azure Active Directory and run a basic sanity check.
describe('Azure Active Directory Authentication', () => {
beforeEach(() => {
// log into Azure Active Directory through our sample SPA using our custom command
- cy.loginToAAD(Cypress.env('aad_username'), Cypress.env('aad_password'))
+ cy.task('getAuthCredentials').then(({ username, password }) => {
+ cy.loginToAAD(username, password)
+ })
cy.visit('http://localhost:3000')
})
it('verifies the user logged in has the correct name', () => {
- cy.get('#table-body-div td:contains("name") + td').should(
- 'contain',
- `${Cypress.env('aad_name')}`
- )
+ cy.task('getSecret', 'AAD_NAME').then((name) => {
+ cy.get('#table-body-div td:contains("name") + td').should('contain', name)
+ })
})
it('verifies the user logged in has the correct preferred name', () => {
- cy.get('#table-body-div td:contains("preferred_username") + td').should(
- 'contain',
- `${Cypress.env('aad_username')}`
- )
+ cy.task('getAuthCredentials').then(({ username }) => {
+ cy.get('#table-body-div td:contains("preferred_username") + td').should(
+ 'contain',
+ username
+ )
+ })
})
})
```
@@ -230,10 +247,9 @@ Cypress.Commands.add('loginToAAD', (username: string, password: string) => {
// this is a very basic form of session validation for this demo.
// depending on your needs, something more verbose might be needed
cy.visit('http://localhost:3000')
- cy.get('#welcome-div').should(
- 'contain',
- `Welcome ${Cypress.env('aad_username')}!`
- )
+ cy.task('getAuthCredentials').then(({ username }) => {
+ cy.get('#welcome-div').should('contain', `Welcome ${username}!`)
+ })
},
}
)
diff --git a/docs/app/guides/authentication-testing/google-authentication.mdx b/docs/app/guides/authentication-testing/google-authentication.mdx
index 669ba0f43f..faae8b9cbe 100644
--- a/docs/app/guides/authentication-testing/google-authentication.mdx
+++ b/docs/app/guides/authentication-testing/google-authentication.mdx
@@ -105,9 +105,20 @@ require('dotenv').config()
```js
{
env: {
- googleRefreshToken: process.env.GOOGLE_REFRESH_TOKEN,
googleClientId: process.env.REACT_APP_GOOGLE_CLIENTID,
- googleClientSecret: process.env.REACT_APP_GOOGLE_CLIENT_SECRET,
+ },
+ setupNodeEvents(on, config) {
+ on('task', {
+ getGoogleAuthCredentials() {
+ const clientSecret = process.env.REACT_APP_GOOGLE_CLIENT_SECRET
+ const refreshToken = process.env.GOOGLE_REFRESH_TOKEN
+ if (!clientSecret || !refreshToken) {
+ throw new Error('REACT_APP_GOOGLE_CLIENT_SECRET and GOOGLE_REFRESH_TOKEN must be set')
+ }
+ return { clientSecret, refreshToken }
+ },
+ })
+ return config
},
}
```
@@ -134,16 +145,17 @@ The `loginByGoogleApi` command will execute the following steps:
```jsx title="cypress/support/commands.js"
Cypress.Commands.add('loginByGoogleApi', () => {
cy.log('Logging in to Google')
- cy.request({
- method: 'POST',
- url: 'https://www.googleapis.com/oauth2/v4/token',
- body: {
- grant_type: 'refresh_token',
- client_id: Cypress.env('googleClientId'),
- client_secret: Cypress.env('googleClientSecret'),
- refresh_token: Cypress.env('googleRefreshToken'),
- },
- }).then(({ body }) => {
+ cy.task('getGoogleAuthCredentials').then(({ clientSecret, refreshToken }) => {
+ cy.request({
+ method: 'POST',
+ url: 'https://www.googleapis.com/oauth2/v4/token',
+ body: {
+ grant_type: 'refresh_token',
+ client_id: Cypress.env('googleClientId'),
+ client_secret: clientSecret,
+ refresh_token: refreshToken,
+ },
+ }).then(({ body }) => {
const { access_token, id_token } = body
cy.request({
diff --git a/docs/app/guides/authentication-testing/okta-authentication.mdx b/docs/app/guides/authentication-testing/okta-authentication.mdx
index 432db2bd1f..2e55e46f94 100644
--- a/docs/app/guides/authentication-testing/okta-authentication.mdx
+++ b/docs/app/guides/authentication-testing/okta-authentication.mdx
@@ -46,11 +46,22 @@ require('dotenv').config()
```js
{
env: {
- auth_username: process.env.AUTH_USERNAME,
- auth_password: process.env.AUTH_PASSWORD,
okta_domain: process.env.REACT_APP_OKTA_DOMAIN,
okta_client_id: process.env.REACT_APP_OKTA_CLIENTID,
},
+ setupNodeEvents(on, config) {
+ on('task', {
+ getAuthCredentials() {
+ const username = process.env.AUTH_USERNAME
+ const password = process.env.AUTH_PASSWORD
+ if (!username || !password) {
+ throw new Error('AUTH_USERNAME and AUTH_PASSWORD must be set')
+ }
+ return { username, password }
+ },
+ })
+ return config
+ },
}
```
@@ -120,7 +131,9 @@ is in the .
describe('Okta', function () {
beforeEach(function () {
cy.task('db:seed')
- cy.loginByOkta(Cypress.env('okta_username'), Cypress.env('okta_password'))
+ cy.task('getAuthCredentials').then(({ username, password }) => {
+ cy.loginByOkta(username, password)
+ })
})
it('verifies signed in user does not have a bank account', function () {
@@ -238,10 +251,9 @@ process and logout.
describe('Okta', function () {
beforeEach(function () {
cy.task('db:seed')
- cy.loginByOktaApi(
- Cypress.env('auth_username'),
- Cypress.env('auth_password')
- )
+ cy.task('getAuthCredentials').then(({ username, password }) => {
+ cy.loginByOktaApi(username, password)
+ })
})
it('shows onboarding', function () {
diff --git a/docs/app/guides/authentication-testing/social-authentication.mdx b/docs/app/guides/authentication-testing/social-authentication.mdx
index c66ec5b9f3..767cc52a18 100644
--- a/docs/app/guides/authentication-testing/social-authentication.mdx
+++ b/docs/app/guides/authentication-testing/social-authentication.mdx
@@ -110,23 +110,31 @@ configuration option must be enabled.
To have access to test user credentials within our tests we need to configure
Cypress to use the social username, password, and name environment variables set
-in the `cypress.env.json` file or by one of our
-[supported methods](/app/references/environment-variables#Setting).
+as OS-level environment variables (e.g., in your `.env` file or CI secret management).
-```json title='cypress.env.json'
+:::cypress-config-example
+
+```js
{
- "GOOGLE_USERNAME": "",
- "GOOGLE_PASSWORD": "",
- "GOOGLE_NAME": "",
- "MICROSOFT_USERNAME": "",
- "MICROSOFT_PASSWORD": "",
- "MICROSOFT_NAME": "",
- "FACEBOOK_USERNAME": "",
- "FACEBOOK_PASSWORD": "",
- "FACEBOOK_NAME": ""
+ setupNodeEvents(on, config) {
+ on('task', {
+ getAuthCredentials(provider) {
+ const username = process.env[`${provider}_USERNAME`]
+ const password = process.env[`${provider}_PASSWORD`]
+ const name = process.env[`${provider}_NAME`]
+ if (!username || !password || !name) {
+ throw new Error(`${provider}_USERNAME, ${provider}_PASSWORD, and ${provider}_NAME must be set`)
+ }
+ return { username, password, name }
+ },
+ })
+ return config
+ },
}
```
+:::
+
### Login with `cy.origin()`
We'll write a custom command called `loginToAuth0ViaSocial` to perform a
@@ -283,24 +291,24 @@ Cypress.Commands.add(
switch (SOCIAL_PROVIDER) {
case 'microsoft':
- logIntoMicrosoft(
- Cypress.env('MICROSOFT_USERNAME'),
- Cypress.env('MICROSOFT_PASSWORD'),
- Cypress.env('MICROSOFT_NAME')
+ cy.task('getAuthCredentials', 'MICROSOFT').then(
+ ({ username, password, name }) => {
+ logIntoMicrosoft(username, password, name)
+ }
)
break
case 'google':
- logIntoGoogle(
- Cypress.env('GOOGLE_USERNAME'),
- Cypress.env('GOOGLE_PASSWORD'),
- Cypress.env('GOOGLE_NAME')
+ cy.task('getAuthCredentials', 'GOOGLE').then(
+ ({ username, password, name }) => {
+ logIntoGoogle(username, password, name)
+ }
)
break
case 'facebook':
- logIntoFacebook(
- Cypress.env('FACEBOOK_USERNAME'),
- Cypress.env('FACEBOOK_PASSWORD'),
- Cypress.env('FACEBOOK_NAME')
+ cy.task('getAuthCredentials', 'FACEBOOK').then(
+ ({ username, password, name }) => {
+ logIntoFacebook(username, password, name)
+ }
)
break
default:
@@ -360,24 +368,24 @@ Cypress.Commands.add(
() => {
switch (SOCIAL_PROVIDER) {
case 'microsoft':
- logIntoMicrosoft(
- Cypress.env('MICROSOFT_USERNAME'),
- Cypress.env('MICROSOFT_PASSWORD'),
- Cypress.env('MICROSOFT_NAME')
+ cy.task('getAuthCredentials', 'MICROSOFT').then(
+ ({ username, password, name }) => {
+ logIntoMicrosoft(username, password, name)
+ }
)
break
case 'google':
- logIntoGoogle(
- Cypress.env('GOOGLE_USERNAME'),
- Cypress.env('GOOGLE_PASSWORD'),
- Cypress.env('GOOGLE_NAME')
+ cy.task('getAuthCredentials', 'GOOGLE').then(
+ ({ username, password, name }) => {
+ logIntoGoogle(username, password, name)
+ }
)
break
case 'facebook':
- logIntoFacebook(
- Cypress.env('FACEBOOK_USERNAME'),
- Cypress.env('FACEBOOK_PASSWORD'),
- Cypress.env('FACEBOOK_NAME')
+ cy.task('getAuthCredentials', 'FACEBOOK').then(
+ ({ username, password, name }) => {
+ logIntoFacebook(username, password, name)
+ }
)
break
default:
@@ -389,22 +397,19 @@ Cypress.Commands.add(
cy.visit('http://localhost:3000')
switch (SOCIAL_PROVIDER) {
case 'microsoft':
- cy.get('h6.dropdown-header').should(
- 'contain',
- Cypress.env('MICROSOFT_NAME')
- )
+ cy.task('getAuthCredentials', 'MICROSOFT').then(({ name }) => {
+ cy.get('h6.dropdown-header').should('contain', name)
+ })
break
case 'google':
- cy.get('h6.dropdown-header').should(
- 'contain',
- Cypress.env('GOOGLE_NAME')
- )
+ cy.task('getAuthCredentials', 'GOOGLE').then(({ name }) => {
+ cy.get('h6.dropdown-header').should('contain', name)
+ })
break
case 'facebook':
- cy.get('h6.dropdown-header').should(
- 'contain',
- Cypress.env('FACEBOOK_NAME')
- )
+ cy.task('getAuthCredentials', 'FACEBOOK').then(({ name }) => {
+ cy.get('h6.dropdown-header').should('contain', name)
+ })
break
default:
throw new Error('no social provider configured!')
diff --git a/docs/app/guides/migration/protractor-to-cypress.mdx b/docs/app/guides/migration/protractor-to-cypress.mdx
index c2aae3cfde..60933ac4aa 100644
--- a/docs/app/guides/migration/protractor-to-cypress.mdx
+++ b/docs/app/guides/migration/protractor-to-cypress.mdx
@@ -1034,7 +1034,9 @@ You can use your own custom commands in any of your tests:
```js
it('should display the username of a logged in user', () => {
- cy.login('Matt', Cypress.env('password'))
+ cy.task('getSecret', 'PASSWORD').then((password) => {
+ cy.login('Matt', password)
+ })
cy.get('.username').contains('Matt')
})
```
diff --git a/docs/app/guides/screenshots-and-videos.mdx b/docs/app/guides/screenshots-and-videos.mdx
index 6036951956..3d4545e01b 100644
--- a/docs/app/guides/screenshots-and-videos.mdx
+++ b/docs/app/guides/screenshots-and-videos.mdx
@@ -140,7 +140,7 @@ a bigger video file size and higher quality video.
:::info
If you are an FFmpeg pro and want to see all the settings and debug messages
-during the encoding, run Cypress with the following environment variable:
+during the encoding, run Cypress with the following system environment variable:
```shell
DEBUG=cypress:server:video
diff --git a/docs/app/references/advanced-installation.mdx b/docs/app/references/advanced-installation.mdx
index a1af89407c..a26348dbad 100644
--- a/docs/app/references/advanced-installation.mdx
+++ b/docs/app/references/advanced-installation.mdx
@@ -304,7 +304,7 @@ Cypress will then attempt to download a binary with this format:
## Download path template
-Starting with Cypress 9.3.0, you can use the `CYPRESS_DOWNLOAD_PATH_TEMPLATE`
+You can use the `CYPRESS_DOWNLOAD_PATH_TEMPLATE`
environment variable to download the Cypress binary from a custom URL that's
generated based on endpoint, version, platform and architecture.
diff --git a/docs/app/references/command-line.mdx b/docs/app/references/command-line.mdx
index 68c073654e..64ab131cb7 100644
--- a/docs/app/references/command-line.mdx
+++ b/docs/app/references/command-line.mdx
@@ -271,7 +271,7 @@ cypress run --browser /usr/bin/chromium
This value should be automatically detected for most CI providers and is
unnecessary to define unless Cypress is unable to determine it.
-Typically, this is defined as an environment variable within your CI provider,
+Typically, this is defined as a system environment variable within your CI provider,
defining a unique "build" or "run".
```shell
@@ -482,7 +482,7 @@ cypress run --record --key
```
If you set the **Record Key** as the environment variable `CYPRESS_RECORD_KEY`,
-you can omit the `--key` flag. You'd typically set this environment variable
+you can omit the `--key` flag. You may want to set this environment variable
when running in
[Continuous Integration](/app/continuous-integration/overview).
diff --git a/docs/app/references/configuration.mdx b/docs/app/references/configuration.mdx
index 49410cf1a6..2bb9b5f2fc 100644
--- a/docs/app/references/configuration.mdx
+++ b/docs/app/references/configuration.mdx
@@ -454,8 +454,7 @@ highlighted to show where the value has been set via the following ways:
- [Cypress configuration file](/app/references/configuration)
- The
[Cypress environment file](/app/references/environment-variables#Option-2-cypressenvjson)
-- System
- [environment variables](/app/references/environment-variables#Option-3-CYPRESS_)
+- [System set environment variables prefixed with `CYPRESS_`](/app/references/environment-variables#Option-3-CYPRESS_)
- [Command Line arguments](/app/references/command-line)
- [setupNodeEvents](#setupNodeEvents)
diff --git a/docs/app/references/environment-variables.mdx b/docs/app/references/environment-variables.mdx
index 79fc15dd83..916aff21e1 100644
--- a/docs/app/references/environment-variables.mdx
+++ b/docs/app/references/environment-variables.mdx
@@ -13,7 +13,7 @@ sidebar_label: 'Environment Variables'
Difference between OS-level and Cypress environment variables
In Cypress, "environment variables" are variables that are accessible via
-`Cypress.env`. These are not the same as OS-level environment variables.
+`Cypress.env` in your tests or the browser. These are not the same as OS-level environment variables.
However,
[it is possible to set Cypress environment variables from OS-level environment variables](/app/references/environment-variables#Option-3-CYPRESS_).
@@ -43,8 +43,7 @@ cy.request(Cypress.env('EXTERNAL_API')) // points to a dynamic env var
Using 'baseUrl'
-Environment variables are great at pointing to external services and servers, or
-storing password or other credentials.
+Environment variables are great at pointing to external services and servers.
However, you **do not** need to use environment variables to point to the origin
and domain under test. Use `baseUrl` instead of environment variables.
@@ -275,7 +274,7 @@ You can use the `--env` argument for
:::caution
-Multiple values must be separated by a comma, not a space. In some shells, like Windows PowerShell, you may need to surround the key/value pair with quotes: `--env "cyuser=dummyUser,cypassword=dummyPassword"`.
+Multiple values must be separated by a comma, not a space. In some shells, like Windows PowerShell, you may need to surround the key/value pair with quotes: `--env "host=kevin.dev.local,api_server=http://localhost:8888/api/v1"`.
:::
diff --git a/docs/app/references/migration-guide.mdx b/docs/app/references/migration-guide.mdx
index 07dcf66600..82867a5ebb 100644
--- a/docs/app/references/migration-guide.mdx
+++ b/docs/app/references/migration-guide.mdx
@@ -773,7 +773,9 @@ describe('workflow', { testIsolation: false }, () => {
it('logs in', () => {
cy.visit('https://example.cypress.io/log-in')
cy.get('username').type('User1')
- cy.get('password').type(Cypress.env('User1_password'))
+ cy.task('getSecret', 'USER1_PASSWORD').then((password) => {
+ cy.get('password').type(password)
+ })
cy.get('button#login').click()
cy.contains('User1')
})