Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/api/commands/exec.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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' } })
Expand Down
106 changes: 53 additions & 53 deletions docs/api/commands/prompt.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
}
)
})
})
})
})
Expand Down Expand Up @@ -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
},
}
)
Expand Down Expand Up @@ -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 "[email protected]" 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 "[email protected]" 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
Expand Down Expand Up @@ -794,7 +794,7 @@ cy.origin('https://cloud.cypress.io/login', () => {
],
{
placeholders: {
password: Cypress.env('PASSWORD'),
password: 'PASSWORD_PLACEHOLDER',
},
}
)
Expand Down
39 changes: 38 additions & 1 deletion docs/api/commands/task.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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

<HeaderRequirements />
Expand Down
19 changes: 9 additions & 10 deletions docs/api/commands/wrap.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
})

Expand Down
46 changes: 26 additions & 20 deletions docs/api/cypress-api/env.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ specs.
<strong>Difference between OS-level and Cypress environment variables</strong>

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_).

Expand Down Expand Up @@ -76,16 +76,16 @@ Set multiple environment variables with an object literal.
```ts
{
env: {
foo: 'bar',
baz: 'quux',
apiUrl: 'https://api.example.com',
apiVersion: 'v1',
},
}
```

:::

```javascript
Cypress.env() // => {foo: 'bar', baz: 'quux'}
Cypress.env() // => {apiUrl: 'https://api.example.com', apiVersion: 'v1'}
```

### Name
Expand All @@ -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
Expand All @@ -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',
},
}
```
Expand All @@ -152,8 +151,8 @@ Cypress.env('host') // => http://server.dev.local
```ts
{
env: {
foo: 'bar',
baz: 'quux',
host: 'laura.dev',
apiVersion: 'v1',
},
}
```
Expand All @@ -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
Expand All @@ -181,25 +180,32 @@ 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
```

:::

```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)
})
})
})
```
Expand Down
6 changes: 3 additions & 3 deletions docs/api/node-events/browser-launch-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.

:::

Expand Down Expand Up @@ -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:

Expand Down
Loading