feat(testing): support cypress v16 - #36953
Open
leosvelperez wants to merge 27 commits into
Open
Conversation
✅ Deploy Preview for nx-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for nx-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Contributor
|
View your CI Pipeline Execution ↗ for commit b83d996
☁️ Nx Cloud last updated this comment at |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
leosvelperez
force-pushed
the
nxc-4544
branch
2 times, most recently
from
September 9, 2026 09:29
92c7510 to
7acdee2
Compare
leosvelperez
force-pushed
the
nxc-4544
branch
from
September 9, 2026 11:23
7acdee2 to
fca69e9
Compare
Contributor
There was a problem hiding this comment.
Nx Cloud has identified a flaky task in your failed CI:
🔂 Since the failure was identified as flaky, we triggered a CI rerun by adding an empty commit to this branch.
🔔 Heads up, your workspace has pending recommendations ↗ to auto-apply fixes for similar failures.
🎓 Learn more about Self-Healing CI on nx.dev
leosvelperez
force-pushed
the
nxc-4544
branch
from
September 10, 2026 08:13
e10d5c2 to
187df43
Compare
Cypress 16 is the latest major. `@nx/cypress` now accepts it as a peer (`>= 13 < 17`), and the generators install Cypress `^16.0.0`, `@cypress/vite-dev-server` `^8.0.0`, `@cypress/webpack-dev-server` `^6.0.0` and Vite `^8.0.0` (the floor of the Vite dev server) on new setups. A workspace that already has Cypress 15 keeps receiving the 15-compatible versions through the version map, as 13 and 14 do. `nx migrate` moves Cypress 15 workspaces to 16 together with the dev server packages. The repo itself runs Cypress 16: the root pin, the hardcoded pnpm path in the graph e2e tsconfig, and the e2e fixtures that read env values (`Cypress.env()` throws in 16, so they use `cy.env()`).
…s v16 Cypress 16 renamed `experimentalMemoryManagement` to `manageBrowserMemory`, replaced `experimentalFastVisibility` with `visibilityStrategy`, and removed `experimentalSourceRewriting`, `allowCypressEnv` and `execTimeout`. It also turned `getCookie`, `getCookies`, `getAllCookies`, `getAllLocalStorage` and `getAllSessionStorage` into queries, so an existing `Cypress.Commands.overwrite()` of any of them fails at runtime. Two migrations gated on Cypress 16 cover this. The first rewrites the option keys in the Cypress config's top level and `e2e`/`component` blocks, keeping the key's quoting. It reports as next steps what it cannot decide: a non-literal `experimentalFastVisibility` value, a removed `execTimeout` (the `cy.task()` replacement uses `taskTimeout`) and a removed `experimentalSourceRewriting: true` (`removeSRIAttributes` is the replacement for Subresource Integrity workarounds). The second renames the overwrite calls to `overwriteQuery()` and hands the callback review to an AI prompt, since a query callback must return a function rather than a chainable.
…cypress v16 Cypress 16 dropped the `cypress/angular-zoneless` entry point; its `cypress/angular` harness mounts with zoneless change detection, requires Angular 21 or later, and bootstraps through `@angular/platform-browser` instead of `@angular/platform-browser-dynamic`. The Angular component testing generator now writes `cypress/angular` on Cypress 16 and keeps `cypress/angular-zoneless` for zoneless projects on Cypress 15.8 to 15.x, and the component test generator only adds `@angular/platform-browser-dynamic` for Cypress versions below 16. A migration gated on Cypress 16 rewrites existing `cypress/angular-zoneless` module references in Cypress projects. The generator spec drops the Vite 7 pin it carried while Cypress lacked Vite 8 support.
Cypress 16 removed `Cypress.env()`, `cy.exec()` and `.end()`, dropped `env` from test-configuration overrides, and stopped accepting `blockHosts` and the viewport options through `Cypress.config()` during a test. The replacement for each depends on where the value comes from (`cy.env()` versus the new `expose` sources, a `cy.task()` in `setupNodeEvents`), so a prompt migration gated on Cypress 16 walks an agent through them and through the component testing changes (zoneless Angular harness, Vite 8, Electron deprecation).
The supported range becomes `>= 13 < 17`. The environment variables section explains how tests read `env` values on Cypress 16 (`cy.env()`, since `Cypress.env()` was removed) and when `expose` applies. The component testing guide notes the zoneless `cypress/angular` harness and what that means for zone-based Angular applications.
…d range floor `getDeclaredPackageVersion` coerces a range to its first version, so a range spanning majors (`>=15.20.1 <17`) always reads as the lower major even when the upper one is installed. `assertSupportedPackageVersion` already prefers the installed version when it satisfies the declared range; extract that resolution into `getResolvedPackageVersion` so version selection can use the same answer as the floor check.
… declared range `getInstalledCypressVersion(tree)` read only the declared range, so a range spanning Cypress 15 and 16 selected the Cypress 15 dependency set and, for zoneless Angular projects, the `cypress/angular-zoneless` import that Cypress 16 no longer ships. The floor check in the same generator run already resolved the installed version first; use the shared devkit helper so both agree.
…rget `cypressProjectConfigs` picked the first target using `@nx/cypress:cypress`, so a project with an `e2e` and a `component-test` target on separate config files only had the first one migrated. Enumerate every such target and yield each distinct config path once.
…g options
The config options migration only dispatched `PropertyAssignment` nodes with
an identifier or string name, so `{ experimentalFastVisibility }` and
`['experimentalMemoryManagement']: true` kept the removed option with no
next step while the package update moved the workspace to Cypress 16.
Shorthand and statically computed keys are now handled: a renamed shorthand
becomes `manageBrowserMemory: experimentalMemoryManagement`, a removed
shorthand is deleted, and a shorthand `experimentalFastVisibility` or
`experimentalSourceRewriting` is reported since its value is unknown.
The `quoteLike` helper is dropped: an unquoted key is valid in every config
and `formatFiles` normalizes the style anyway, so the quoted-keys test could
not observe it. The test now states what it covers, that quoted legacy keys
are recognized.
The zoneless mount import and query command overwrite migrations visited only the roots of projects owning a Cypress config, so a shared library exporting the mount helper or registering an overwrite kept the removed `cypress/angular-zoneless` import or the obsolete `overwrite()` call. Both migrations now scan every non-ignored JavaScript and TypeScript file in the workspace once, with the same string prefilter, matching the workspace-wide scan of `rewrite-internal-subpath-imports`.
…nternal entry point Lets plugin version gates check what is installed while keeping their own declared-range fallback for the fresh-install path.
…2.1 component testing The guard only rejected declared ranges wholly below 15.20.1, so a range spanning the floor such as `>=15.0.0 <17` passed with Cypress 15.17.0 installed and the generator wrote a component testing setup that cannot load Angular's Babel 8 dependencies. The guard now checks the installed version when it satisfies the declared range and keeps the range check for workspaces with nothing installed.
Asserts the `23.3.0-cypress-16` group applies to Cypress 15 only and lands `^16.0.0` with the matching dev servers, and that each gated 23.3.0 migration requires the landing version, mirroring how nx evaluates `requires`.
…ed major With nothing installed, the resolved Cypress version was the floor of the declared range, so `>=15.20.1 <17` selected the Cypress 15 tuple and the zoneless Angular generator wrote the `cypress/angular-zoneless` import that the Cypress 16 a clean install resolves no longer exports. The resolver now follows the highest supported major the range reaches. The zoneless floor check in the Angular generator shares the guard's installed-vs-range decision: an installed version decides, otherwise only a range capped below 15.8.0 fails, so `>=14 <15.10` no longer throws for a floor the install never produces.
The config resolver followed an exported variable only when it held an
object literal, so `const config = defineConfig({...}); export default
config` resolved to nothing and the migration skipped the file without a
word. The resolver now unwraps `defineConfig()` and follows same-file
variables at any depth, which also reaches the two earlier migrations that
share it. The migration edits `e2e`/`component` blocks held in variables
and lists a config it cannot resolve as a next step when it mentions one of
the changed options.
…lling
The query-overwrite migration renamed every `Cypress.Commands.overwrite()`
spelled with dot access, so `Cypress.Commands['overwrite']('getCookie', ...)`
survived into Cypress 16 and a file with its own `Cypress` value was
rewritten as if it called the global. Both workspace-wide migrations now
skip a file that binds the identifier they rely on (`Cypress`, or `require`
for the CommonJS rewrite) as a runtime value, list it as a next step, and
the query migration recognizes the static bracket spellings. Ambient
declarations, `declare global { namespace Cypress { ... } }` included, and
type-only imports do not count as bindings.
The mount migration no longer returns the install callback: the migration
runner discards it and installs from the package.json diff.
…he version resolver
…d version `latest` and `next` never satisfy a semver range, so the resolver ignored the install and the Angular 22.1 component-testing guard let an installed Cypress 15.17 through. The tag resolved to that install, so the installed version now decides for both; with nothing installed the resolver still falls back to `latestKnownVersion`.
…ent once A variable holding the block for both testing types resolved to the same node twice, so its edits applied twice and corrupted the file.
…indings
`const Fake = class Cypress {}` and `function require() {}` expressions
bind the name for the migrations that skip shadowed files, same as the
declaration forms.
`getResolvedPackageVersion` normalized the declared range with `semver.coerce`, which returns the first numeric tuple in the string. A range that lists its upper bound first, such as `<16 >=15.8.0`, resolved to `16.0.0` instead of `15.8.0`. The Cypress generators read that value when nothing is installed, so a zoneless Angular project on such a range got the Cypress 16 `cypress/angular` mount import and the Vite dev server that peers on Cypress 16, while a clean install lands on Cypress 15. The floor is `semver.minVersion` of the range. A range nothing satisfies has no minimum and falls back to the coerced declaration as before. A prerelease floor (`^16.0.0-beta.1`) is below `16.0.0`, so the Angular generator's mount selection compares majors instead. An installed `16.0.0-beta.1` hit the same comparison.
The heading read as if every supported Cypress version lost the API. Cypress 13 to 15 keep it.
…nent testing guard The Angular 22.1 guard read `@angular/core` from the declared range with `semver.coerce`, which takes the first number in the string. A range that lists its upper bound first, such as `<22.1 >=22.0`, read as 22.1 and the guard required Cypress 15.20.1 for a workspace that can only install Angular 22.0. An installed Angular that satisfied the range was ignored. The guard resolves the version the same way the Cypress generators do: the installed version when it satisfies the declared range, otherwise the range's lowest bound.
…nent testing guard [Self-Healing CI Rerun]
…s trailing trivia `removeProperty` in the Cypress 16 config options migration looked for the separator only at `property.getEnd()`, which stops before trailing trivia. A comment, a space or a line break between the value and the comma left the comma behind, and the migrated config failed to parse with "Property assignment expected". Scan forward across the trivia and delete through the comma when it is the next token.
The branch also runs when an installed Cypress no longer satisfies a repinned range, and an unsatisfiable range resolves to the coerced declaration rather than a floor.
leosvelperez
force-pushed
the
nxc-4544
branch
from
September 10, 2026 08:39
187df43 to
b83d996
Compare
leosvelperez
marked this pull request as ready for review
September 10, 2026 09:24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Current Behavior
@nx/cypressdeclares a peer range of>= 13 < 16, so installing Cypress 16 fails peer resolution andnx migratenever offers the bump. Generators install Cypress 15. For zoneless Angular projects they import the mount helper fromcypress/angular-zoneless, an entry point Cypress 16 does not ship. Nothing rewrites the config options and commands Cypress 16 removed or renamed.Expected Behavior
Cypress 16 installs with the plugin, generators scaffold projects on it, and
nx migratemoves a workspace on Cypress 15 to 16. The migration updates the renamed config options, the command overwrites that became queries, and the zoneless Angular mount import. The changes that need judgment go to the AI migration instructions. Cypress 13 to 15 keep working.Related Issue(s)
NXC-4544
Implementation Notes
Cypress.Commands.overwrite()rewrite is hybrid. The rename tooverwriteQueryis mechanical. The prompt handles a callback that chains.then()or callscy.*, which must return a function.keystrokeDelaydefaults to 0, down from 10. The migration does not restore 10, to keep the upstream default.latestandnextincluded. Otherwise a range counts as the highest supported major it reaches, where a clean install lands. Its floor is the lowest bound. A Cypress 16 prerelease, declared or installed, selects the 16 harness.defineConfig()and thee2e/componentblocks through same-file variables. An unresolved config becomes a next step. Spreads are not followed.Cypressorrequireis skipped and reported. Ambient and type-only declarations are not bindings.cypressProjectConfigsyields each@nx/cypress:cypresstarget's config.Migration coverage
packageJsonUpdatesgated on Cypress 15experimentalMemoryManagement,experimentalFastVisibility,experimentalSourceRewriting,allowCypressEnv,execTimeoutupdate-cypress-16-config-optionsupdate-cypress-16-query-command-overwritesrenamesoverwritetooverwriteQuery, the prompt adapts callbackscypress/angular-zonelessremoved,@cypress/angular-zonelessdeprecatedupdate-angular-zoneless-mount-importrewrites imports and drops the dependency@angular/platform-browser-dynamicreplacedCypress.env(),envtest overrides,cy.exec(),cy.end()create-ai-instructions-for-cypress-16. Each call needs a decision a transform cannot make, such as sensitive vs public value or the task bodyCypress.config()viewport andblockHostswrites, CoffeeScript,autoSpyOutputsandautoDetectChangeskeystrokeDelay, Node floor, Electron deprecation@nx/viteand@nx/nextcypress infooutput