Skip to content

Commit fcf3756

Browse files
committed
feat: add cy.env(), allowCypressEnv, and deprecate Cypress.env()
Split up Deprecations + features in changelog Split up other features
1 parent 0573474 commit fcf3756

File tree

289 files changed

+1544
-485
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

289 files changed

+1544
-485
lines changed

cli/CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
<!-- See the ../guides/writing-the-cypress-changelog.md for details on writing the changelog. -->
2-
## 15.9.1
2+
## 15.10.0
33

44
_Released 01/27/2026 (PENDING)_
55

6+
**Deprecations:**
7+
8+
[`Cypress.env()](https://docs.cypress.io/api/cypress-api/env) is now deprecated and will be removed in a future major release of Cypress. To understand why, and how to migrate, please read our [Migration Guide](https://on.cypress.io/cypress-env-migration). Addressed in [#33181](https://github.com/cypress-io/cypress/pull/33181).
9+
10+
**Features:**
11+
12+
- Introduced a new [`cy.env()`](https://docs.cypress.io/api/commands/env) command that can be used to asynchronously and securely access Cypress environment variables. Addressed in [#33181](https://github.com/cypress-io/cypress/pull/33181).
13+
- Added a [`allowCypressEnv`](https://docs.cypress.io/app/references/configuration#Global) configuration option that disallows use of the deprecated `Cypress.env()` API. Addressed in [#33181](https://github.com/cypress-io/cypress/pull/33181).
14+
615
**Misc:**
716

817
- The icon in the 'Open in IDE' button in the command log is now the correct size. Addresses [#32779](https://github.com/cypress-io/cypress/issues/32779). Addressed in [#33217](https://github.com/cypress-io/cypress/pull/33217).

cli/types/cypress.d.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ declare namespace Cypress {
512512
* Returns all environment variables set with CYPRESS_ prefix or in "env" object in "cypress.config.{js,ts,mjs,cjs}"
513513
*
514514
* @see https://on.cypress.io/env
515+
* @deprecated Use {@linkcode Chainable.env cy.env()} instead.
515516
*/
516517
env(): ObjectLike
517518
/**
@@ -521,6 +522,7 @@ declare namespace Cypress {
521522
* // cypress.config.js
522523
* { "env": { "foo": "bar" } }
523524
* Cypress.env("foo") // => bar
525+
* @deprecated Use {@linkcode Chainable.env cy.env()} instead.
524526
*/
525527
env(key: string): any
526528
/**
@@ -529,13 +531,15 @@ declare namespace Cypress {
529531
* @see https://on.cypress.io/env
530532
* @example
531533
* Cypress.env("host", "http://server.dev.local")
534+
* @deprecated Use {@linkcode Chainable.env cy.env()} instead.
532535
*/
533536
env(key: string, value: any): void
534537
/**
535538
* Set values for multiple variables at once. Values are merged with existing values.
536539
* @see https://on.cypress.io/env
537540
* @example
538541
* Cypress.env({ host: "http://server.dev.local", foo: "foo" })
542+
* @deprecated Use {@linkcode Chainable.env cy.env()} instead.
539543
*/
540544
env(object: ObjectLike): void
541545

@@ -2191,6 +2195,25 @@ declare namespace Cypress {
21912195
*/
21922196
task<S = unknown>(event: string, arg?: any, options?: Partial<Loggable & Timeoutable>): Chainable<S>
21932197

2198+
/**
2199+
* Gets multiple environment variables.
2200+
* @see https://on.cypress.io/env
2201+
* @example
2202+
* cy.env(['KEY_1', 'KEY_2']).then(({ KEY_1, KEY_2 }) => { ... })
2203+
*/
2204+
env(keys: string[]): Chainable<Record<string, any>>
2205+
2206+
/**
2207+
* Gets multiple environment variables with a specific type.
2208+
* @see https://on.cypress.io/env
2209+
* @example
2210+
* cy.env<{ KEY_1: string, KEY_2: number }>(['KEY_1', 'KEY_2']).then(({ KEY_1, KEY_2 }) => {
2211+
* expect(KEY_1).to.be.a('string')
2212+
* expect(KEY_2).to.be.a('number')
2213+
* })
2214+
*/
2215+
env<T extends object>(keys: string[]): Chainable<T>
2216+
21942217
/**
21952218
* Enables you to work with the subject yielded from the previous command.
21962219
*
@@ -2934,6 +2957,18 @@ declare namespace Cypress {
29342957
* @default null
29352958
*/
29362959
baseUrl: string | null
2960+
2961+
/**
2962+
* Whether Cypress should allow [Cypress.env()](https://on.cypress.io/env) API to be available in the browser.
2963+
*
2964+
* Cypress recommends migrating to the cy.env() command and disabling this within your Cypress configuration.
2965+
*
2966+
* The use of Cypress.env() will warn and throw an error when this is set to false.
2967+
*
2968+
* This will be the default behavior in a future major version of Cypress and Cypress.env() will be removed.
2969+
* @default true
2970+
*/
2971+
allowCypressEnv: boolean
29372972
/**
29382973
* Any values to be set as [environment variables](https://on.cypress.io/environment-variables)
29392974
* @default {}

npm/grep/cypress.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import debug from 'debug'
88
const debugInstance = debug('cypress:grep:compare-results')
99

1010
export default defineConfig({
11+
allowCypressEnv: true,
1112
e2e: {
1213
defaultCommandTimeout: 1000,
1314
setupNodeEvents (on, config) {

npm/vite-dev-server/cypress.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { defineConfig } from 'cypress'
22
import { e2ePluginSetup } from '@packages/frontend-shared/cypress/e2e/e2ePluginSetup'
3+
import { setupCyInCyVariables } from '@packages/frontend-shared/cypress/tasks/cy-in-cy-variables'
34

45
export default defineConfig({
56
projectId: 'ypt4pf',
@@ -16,6 +17,13 @@ export default defineConfig({
1617
process.env.CYPRESS_INTERNAL_E2E_TESTING_SELF = 'true'
1718
process.env.CYPRESS_INTERNAL_VITE_OPEN_MODE_TESTING = 'true'
1819

20+
const { setCyInCyVariables, getCyInCyVariables } = setupCyInCyVariables()
21+
22+
on('task', {
23+
setCyInCyVariables,
24+
getCyInCyVariables,
25+
})
26+
1927
return await e2ePluginSetup(on, config)
2028
},
2129
},

npm/webpack-dev-server/cypress.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { defineConfig } from 'cypress'
2+
import { setupCyInCyVariables } from '@packages/frontend-shared/cypress/tasks/cy-in-cy-variables'
23

34
export default defineConfig({
45
projectId: 'ypt4pf',
@@ -14,6 +15,12 @@ export default defineConfig({
1415
delete process.env.CYPRESS_INTERNAL_E2E_TESTING_SELF_PARENT_PROJECT
1516
process.env.CYPRESS_INTERNAL_E2E_TESTING_SELF = 'true'
1617
const { e2ePluginSetup } = require('@packages/frontend-shared/cypress/e2e/e2ePluginSetup') as typeof import('@packages/frontend-shared/cypress/e2e/e2ePluginSetup')
18+
const { setCyInCyVariables, getCyInCyVariables } = setupCyInCyVariables()
19+
20+
on('task', {
21+
setCyInCyVariables,
22+
getCyInCyVariables,
23+
})
1724

1825
return await e2ePluginSetup(on, config)
1926
},

packages/app/cypress.config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import { defineConfig } from 'cypress'
22
import { initGitRepoForTestProject, resetGitRepoForTestProject } from './cypress/tasks/git'
33
import { writeMochaEventSnapshot, readMochaEventSnapshot } from './cypress/tasks/mochaEvents'
4+
import { setupCyInCyVariables } from '@packages/frontend-shared/cypress/tasks/cy-in-cy-variables'
45

56
export default defineConfig({
7+
/**
8+
* NOTE: currently using Cypress.env() in the test config, so we need to set allowCypressEnv to true
9+
* @percy/cypress: https://github.com/percy/percy-cypress/blob/master/index.js#L13
10+
*/
11+
allowCypressEnv: true,
612
projectId: 'ypt4pf',
713
retries: {
814
runMode: 2,
@@ -47,11 +53,15 @@ export default defineConfig({
4753
process.env.CYPRESS_INTERNAL_VITE_OPEN_MODE_TESTING = 'true'
4854
const { e2ePluginSetup } = require('@packages/frontend-shared/cypress/e2e/e2ePluginSetup')
4955

56+
const { setCyInCyVariables, getCyInCyVariables } = setupCyInCyVariables()
57+
5058
on('task', {
5159
initGitRepoForTestProject,
5260
resetGitRepoForTestProject,
5361
writeMochaEventSnapshot,
5462
readMochaEventSnapshot,
63+
setCyInCyVariables,
64+
getCyInCyVariables,
5565
})
5666

5767
return await e2ePluginSetup(on, config)

packages/app/cypress/e2e/runner/support/mochaEventsUtils.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,17 @@ export function runCypressInCypressMochaEventsTest (snapToCompare: string, done:
175175
}).then((snapshot) => {
176176
cy.task('readMochaEventSnapshot', { filename }).then((existingSnapshots: any) => {
177177
existingSnapshots ||= {}
178-
179-
if (Cypress.env('SNAPSHOT_UPDATE') === 1) {
180-
// overwrite the existing snapshot and write it to disk
181-
existingSnapshots[snapToCompare] = snapshot
182-
cy.task('writeMochaEventSnapshot', { filename, snapshots: existingSnapshots }).then(() => {
178+
cy.env(['SNAPSHOT_UPDATE']).then(({ SNAPSHOT_UPDATE }) => {
179+
if (SNAPSHOT_UPDATE === 1) {
180+
// overwrite the existing snapshot and write it to disk
181+
existingSnapshots[snapToCompare] = snapshot
182+
cy.task('writeMochaEventSnapshot', { filename, snapshots: existingSnapshots }).then(() => {
183+
bus.emit('assert:cypress:in:cypress', existingSnapshots, snapshot)
184+
})
185+
} else {
183186
bus.emit('assert:cypress:in:cypress', existingSnapshots, snapshot)
184-
})
185-
} else {
186-
bus.emit('assert:cypress:in:cypress', existingSnapshots, snapshot)
187-
}
187+
}
188+
})
188189
})
189190
})
190191
}

packages/config/src/browser.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ export function resetIssuedWarnings () {
7070
}
7171

7272
const validateNoBreakingOptions = (breakingCfgOptions: Readonly<BreakingOption[]>, cfg: any, onWarning: ErrorHandler, onErr: ErrorHandler, testingType?: TestingType) => {
73-
breakingCfgOptions.forEach(({ name, errorKey, newName, isWarning, value }) => {
74-
if (_.has(cfg, name)) {
73+
breakingCfgOptions.forEach(({ name, errorKey, newName, isWarning, value, shouldDisplayOrThrow }) => {
74+
if (_.has(cfg, name) && (!shouldDisplayOrThrow || shouldDisplayOrThrow(cfg[name]))) {
7575
if (value && cfg[name] !== value) {
7676
// Bail if a value is specified but the config does not have that value.
7777
return

packages/config/src/options.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const BREAKING_OPTION_ERROR_KEY: Readonly<AllCypressErrorNames[]> = [
2828
'VIDEO_UPLOAD_ON_PASSES_REMOVED',
2929
'RENAMED_CONFIG_OPTION',
3030
'EXPERIMENTAL_STUDIO_REMOVED',
31+
'CYPRESS_ENV_DEPRECATION',
3132
] as const
3233

3334
type ValidationOptions = {
@@ -91,6 +92,10 @@ export interface BreakingOption {
9192
* Whether to show the error message in the launchpad
9293
*/
9394
showInLaunchpad?: boolean
95+
/**
96+
* Whether to display or throw the error message based on the configuration value present.
97+
*/
98+
shouldDisplayOrThrow?: (value: any) => boolean
9499
}
95100

96101
const isValidConfig = (testingType: string, config: any, opts: ValidationOptions) => {
@@ -177,6 +182,12 @@ const driverConfigOptions: Array<DriverConfigOption> = [
177182
defaultValue: 4000,
178183
validation: validate.isNumber,
179184
overrideLevel: 'any',
185+
}, {
186+
name: 'allowCypressEnv',
187+
defaultValue: true,
188+
validation: validate.isBoolean,
189+
overrideLevel: 'never',
190+
requireRestartOnChange: 'server',
180191
}, {
181192
name: 'downloadsFolder',
182193
defaultValue: 'cypress/downloads',
@@ -643,6 +654,13 @@ export const breakingOptions: Readonly<BreakingOption[]> = [
643654
errorKey: 'EXPERIMENTAL_STUDIO_REMOVED',
644655
isWarning: true,
645656
},
657+
{
658+
name: 'allowCypressEnv',
659+
errorKey: 'CYPRESS_ENV_DEPRECATION',
660+
// Display this warning if the value is not present or is explicitly false
661+
shouldDisplayOrThrow: (value: any) => value !== false,
662+
isWarning: true,
663+
},
646664
] as const
647665

648666
export const breakingRootOptions: Array<BreakingOption> = [

packages/config/src/project/utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,14 @@ export function mergeDefaults (
413413
additionalIgnorePattern,
414414
}
415415

416+
// we want the allowCypressEnv option to be inherited by e2e/component config when evaluating
417+
// breaking options in order to correctly hide the error that Cypress.env() is deprecated when allowCypressEnv is false
418+
// unless the value is explicitly set
419+
config.allowCypressEnv = config.allowCypressEnv ?? true
420+
if (!_.has(config[testingType], 'allowCypressEnv') && _.isObject(config[testingType])) {
421+
config[testingType].allowCypressEnv = config.allowCypressEnv
422+
}
423+
416424
// split out our own app wide env from user env variables
417425
// and delete envFile
418426
config.env = parseEnv(config, { ...cliConfig.env, ...options.env }, resolved)

0 commit comments

Comments
 (0)