misc: Remove migration UI, warnings, and errors for changes pre-Cypress 10 (v15) (#31629)

* begin removing migration code

* remove migration query

* add back actions

* remove more migration references

* more migration removals

* remove call to migration

* remove gqp-Migration file

* updates to remove migration

* update type

* remove a lot more migration code - especially errors

* Remove other errors/warnins for versions before 10.x

* update error snapshots

* fix some tests

* index on remove-migration: 49fa75ab78 Merge branch 'remove-migration' of https://github.com/cypress-io/cypress into remove-migration

* index on remove-migration: 49fa75ab78 Merge branch 'remove-migration' of https://github.com/cypress-io/cypress into remove-migration

* index on remove-migration: 49fa75ab78 Merge branch 'remove-migration' of https://github.com/cypress-io/cypress into remove-migration

* remove more system tests and references

* fix call to refreshMetaState which was lost at initialization

* rearrange some system tests to not be dependent on migration

* update welcome version test

* fix wording of messaging

* skip irrelevant test

* fix failing assertion

* remove tests around cypress-plugin-retries

* Remove test from config_spec

* Remove screenshot from snapshot

* fix tests + remove more migration projects

* remove + update system tests

* remove + update system tests

* remove some invalid dev-server: start errors

* remove references and tests around pluginsFile which was removed

* remove some more invalid config examples

* Add changelog entry

* fix changelog link

* fix unit test

* index on remove-migration: 490ddb134c Merge branch 'release/15.0.0' into remove-migration

* index on remove-migration: 490ddb134c Merge branch 'release/15.0.0' into remove-migration

* index on remove-migration: 490ddb134c Merge branch 'release/15.0.0' into remove-migration

* run all binary/windows tests on this branch

* Update packages/scaffold-config/test/unit/detect.spec.ts

Co-authored-by: Bill Glesias <bglesias@gmail.com>

* Update packages/scaffold-config/test/unit/detect.spec.ts

Co-authored-by: Bill Glesias <bglesias@gmail.com>

---------

Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com>
Co-authored-by: Bill Glesias <bglesias@gmail.com>
This commit is contained in:
Jennifer Shehane
2025-05-12 13:17:32 -04:00
committed by GitHub
parent f79d1fa965
commit 10c6656ed6
457 changed files with 240 additions and 13359 deletions

View File

@@ -38,7 +38,7 @@ mainBuildFilters: &mainBuildFilters
- /^release\/\d+\.\d+\.\d+$/
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- 'update-v8-snapshot-cache-on-develop'
- 'breaking/remove_webpack_4'
- 'remove-migration'
# usually we don't build Mac app - it takes a long time
# but sometimes we want to really confirm we are doing the right thing
@@ -49,7 +49,7 @@ macWorkflowFilters: &darwin-workflow-filters
- equal: [ develop, << pipeline.git.branch >> ]
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
- equal: [ 'breaking/remove_webpack_4', << pipeline.git.branch >> ]
- equal: [ 'remove-migration', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
@@ -60,7 +60,7 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters
- equal: [ develop, << pipeline.git.branch >> ]
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
- equal: [ 'breaking/remove_webpack_4', << pipeline.git.branch >> ]
- equal: [ 'remove-migration', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
@@ -83,7 +83,7 @@ windowsWorkflowFilters: &windows-workflow-filters
- equal: [ develop, << pipeline.git.branch >> ]
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
- equal: [ 'breaking/remove_webpack_4', << pipeline.git.branch >> ]
- equal: [ 'remove-migration', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
@@ -157,7 +157,7 @@ commands:
name: Set environment variable to determine whether or not to persist artifacts
command: |
echo "Setting SHOULD_PERSIST_ARTIFACTS variable"
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "breaking/remove_webpack_4" ]]; then
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "remove-migration" ]]; then
export SHOULD_PERSIST_ARTIFACTS=true
fi' >> "$BASH_ENV"
# You must run `setup_should_persist_artifacts` command and be using bash before running this command
@@ -1160,23 +1160,6 @@ commands:
CYPRESS_INSTALL_BINARY=~/cypress/cypress.zip npm install --legacy-peer-deps ~/cypress/cypress.tgz
fi
working_directory: /tmp/<<parameters.repo>>
- run:
name: Scaffold new config file
working_directory: /tmp/<<parameters.repo>>
environment:
CYPRESS_INTERNAL_FORCE_SCAFFOLD: "1"
command: |
if [[ -f cypress.json ]]; then
rm -rf cypress.json
echo 'module.exports = { e2e: {} }' > cypress.config.js
fi
- run:
name: Rename support file
working_directory: /tmp/<<parameters.repo>>
command: |
if [[ -f cypress/support/index.js ]]; then
mv cypress/support/index.js cypress/support/e2e.js
fi
- run:
name: Print Cypress version
working_directory: /tmp/<<parameters.repo>>

View File

@@ -18,6 +18,10 @@ _Released 07/01/2025 (PENDING)_
- [`tsx`](https://tsx.is/) is now used in all cases to run the Cypress config, replacing [ts-node](https://github.com/TypeStrong/ts-node) for TypeScript and Node for commonjs/ESM. This should allow for more interoperability for users who are using any variant of ES Modules. Addresses [#8090](https://github.com/cypress-io/cypress/issues/8090), [#15724](https://github.com/cypress-io/cypress/issues/15724), [#21805](https://github.com/cypress-io/cypress/issues/21805), [#22273](https://github.com/cypress-io/cypress/issues/22273), [#22747](https://github.com/cypress-io/cypress/issues/22747), [#23141](https://github.com/cypress-io/cypress/issues/23141), [#25958](https://github.com/cypress-io/cypress/issues/25958), [#25959](https://github.com/cypress-io/cypress/issues/25959), [#26606](https://github.com/cypress-io/cypress/issues/26606), [#27359](https://github.com/cypress-io/cypress/issues/27359), [#27450](https://github.com/cypress-io/cypress/issues/27450), [#28442](https://github.com/cypress-io/cypress/issues/28442), [#30318](https://github.com/cypress-io/cypress/issues/30318), [#30718](https://github.com/cypress-io/cypress/issues/30718), [#30907](https://github.com/cypress-io/cypress/issues/30907), [#30915](https://github.com/cypress-io/cypress/issues/30915), [#30925](https://github.com/cypress-io/cypress/issues/30925), [#30954](https://github.com/cypress-io/cypress/issues/30954) and [#31185](https://github.com/cypress-io/cypress/issues/31185).
**Misc:**
- Migration helpers and related errors are no longer shown when upgrading from Cypress versions earlier than 10.0.0. To migrate from a pre-10.0.0 version, upgrade one major version at a time to receive the appropriate guidance. Addresses [#31345](https://github.com/cypress-io/cypress/issues/31345). Addressed in [https://github.com/cypress-io/cypress/pull/31629/](https://github.com/cypress-io/cypress/pull/31629/).
## 14.3.3
_Released 5/6/2025_

View File

@@ -1,4 +1,4 @@
// checking types passed to cypress/plugins/index.js file
// checking types passed through setupNodeEvents
// does nothing
const pluginConfig: Cypress.PluginConfig = (on, config) => {}

View File

@@ -4,7 +4,7 @@ Clear, consistent, errors are one of the important parts of the Cypress experien
### @packages/errors
All error related logic for the server should be added to `@packages/errors`. This logic has been separated out from the `@packages/server` to enable strict type checking & use in other packages we have added in the `10.0-release` branch.
All error related logic for the server should be added to `@packages/errors`.
### Errors Development Workflow
@@ -78,15 +78,15 @@ In this case, `arg1` will be highlighted in yellow when printed to the terminal.
```ts
PLUGINS_FILE_ERROR: (arg1: string, arg2: Error) => {
FAKE_ERROR: (arg1: string, arg2: Error) => {
return errTemplate`\
The plugins file is missing or invalid.
The fake file is missing or invalid.
Your \`pluginsFile\` is set to ${arg1}, but either the file is missing, it contains a syntax error, or threw an error when required. The \`pluginsFile\` must be a \`.js\`, \`.ts\`, or \`.coffee\` file.
Your \`fakeFile\` is set to ${arg1}, but either the file is missing, it contains a syntax error, or threw an error when required. The \`fakeFile\` must be a \`.js\`, \`.ts\`, or \`.coffee\` file.
Or you might have renamed the extension of your \`pluginsFile\`. If that's the case, restart the test runner.
Or you might have renamed the extension of your \`fakeFile\`. If that's the case, restart the test runner.
Please fix this, or set \`pluginsFile\` to \`false\` if a plugins file is not necessary for your project.
Please fix this, or set \`fakeFile\` to \`false\` if a plugins file is not necessary for your project.
${details(arg2)}
`

View File

@@ -143,14 +143,16 @@ $ npx cypress run --env grepTags=@smoke,grepFilterSpecs=true
$ npx cypress run --env grepUntagged=true
```
You can use any way to modify the environment values `grep` and `grepTags`, except the run-time `Cypress.env('grep')` (because it is too late at run-time). You can set the `grep` value in the `cypress.json` file to run only tests with the substring `viewport` in their names
You can use any way to modify the environment values `grep` and `grepTags`, except the run-time `Cypress.env('grep')` (because it is too late at run-time). You can set the `grep` value in the `cypress.config.js` file to run only tests with the substring `viewport` in their names
```json
{
"env": {
"grep": "viewport"
}
}
```js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
env: {
grep: "viewport"
},
})
```
You can also set the `env.grep` object in the plugin file, but remember to return the changed config object:

View File

@@ -1,20 +1,7 @@
exports['config/src/index .getBreakingKeys returns list of breaking config keys 1'] = [
'blacklistHosts',
'componentFolder',
'experimentalComponentTesting',
'experimentalGetCookiesSameSite',
'experimentalJustInTimeCompile',
'experimentalNetworkStubbing',
'experimentalRunEvents',
'experimentalSessionSupport',
'experimentalSessionAndOrigin',
'experimentalShadowDomSupport',
'experimentalSkipDomainInjection',
'firefoxGcInterval',
'ignoreTestFiles',
'integrationFolder',
'pluginsFile',
'testFiles',
'videoUploadOnPasses',
]

View File

@@ -9,26 +9,15 @@ import type { TestingType } from '@packages/types'
import * as validate from './validation'
const BREAKING_OPTION_ERROR_KEY: Readonly<AllCypressErrorNames[]> = [
'COMPONENT_FOLDER_REMOVED',
'INTEGRATION_FOLDER_REMOVED',
'CONFIG_FILE_INVALID_ROOT_CONFIG',
'CONFIG_FILE_INVALID_ROOT_CONFIG_E2E',
'CONFIG_FILE_INVALID_ROOT_CONFIG_COMPONENT',
'CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_COMPONENT',
'CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_E2E',
'EXPERIMENTAL_COMPONENT_TESTING_REMOVED',
'EXPERIMENTAL_SAMESITE_REMOVED',
'EXPERIMENTAL_NETWORK_STUBBING_REMOVED',
'EXPERIMENTAL_RUN_EVENTS_REMOVED',
'EXPERIMENTAL_SESSION_SUPPORT_REMOVED',
'EXPERIMENTAL_SESSION_AND_ORIGIN_REMOVED',
'EXPERIMENTAL_SINGLE_TAB_RUN_MODE',
'EXPERIMENTAL_SHADOW_DOM_REMOVED',
'FIREFOX_GC_INTERVAL_REMOVED',
'PLUGINS_FILE_CONFIG_OPTION_REMOVED',
'VIDEO_UPLOAD_ON_PASSES_REMOVED',
'RENAMED_CONFIG_OPTION',
'TEST_FILES_RENAMED',
] as const
type ValidationOptions = {
@@ -617,74 +606,17 @@ export const options: Array<DriverConfigOption | RuntimeConfigOption> = [
*/
export const breakingOptions: Readonly<BreakingOption[]> = [
{
name: 'blacklistHosts',
errorKey: 'RENAMED_CONFIG_OPTION',
newName: 'blockHosts',
isWarning: false,
}, {
name: 'componentFolder',
errorKey: 'COMPONENT_FOLDER_REMOVED',
isWarning: false,
}, {
name: 'experimentalComponentTesting',
errorKey: 'EXPERIMENTAL_COMPONENT_TESTING_REMOVED',
isWarning: false,
}, {
name: 'experimentalGetCookiesSameSite',
errorKey: 'EXPERIMENTAL_SAMESITE_REMOVED',
isWarning: true,
}, {
name: 'experimentalJustInTimeCompile',
errorKey: 'EXPERIMENTAL_JIT_COMPILE_REMOVED',
isWarning: true,
},
{
name: 'experimentalNetworkStubbing',
errorKey: 'EXPERIMENTAL_NETWORK_STUBBING_REMOVED',
isWarning: true,
}, {
name: 'experimentalRunEvents',
errorKey: 'EXPERIMENTAL_RUN_EVENTS_REMOVED',
isWarning: true,
}, {
name: 'experimentalSessionSupport',
errorKey: 'EXPERIMENTAL_SESSION_SUPPORT_REMOVED',
isWarning: true,
}, {
name: 'experimentalSessionAndOrigin',
errorKey: 'EXPERIMENTAL_SESSION_AND_ORIGIN_REMOVED',
isWarning: true,
}, {
name: 'experimentalShadowDomSupport',
errorKey: 'EXPERIMENTAL_SHADOW_DOM_REMOVED',
isWarning: true,
}, {
name: 'experimentalSkipDomainInjection',
errorKey: 'EXPERIMENTAL_SKIP_DOMAIN_INJECTION_REMOVED',
isWarning: false,
}, {
name: 'firefoxGcInterval',
errorKey: 'FIREFOX_GC_INTERVAL_REMOVED',
isWarning: true,
}, {
name: 'ignoreTestFiles',
errorKey: 'TEST_FILES_RENAMED',
newName: 'excludeSpecPattern',
isWarning: false,
}, {
name: 'integrationFolder',
errorKey: 'INTEGRATION_FOLDER_REMOVED',
isWarning: false,
}, {
name: 'pluginsFile',
errorKey: 'PLUGINS_FILE_CONFIG_OPTION_REMOVED',
isWarning: false,
},
{
name: 'testFiles',
errorKey: 'TEST_FILES_RENAMED',
newName: 'specPattern',
isWarning: false,
}, {
name: 'videoUploadOnPasses',
errorKey: 'VIDEO_UPLOAD_ON_PASSES_REMOVED',

View File

@@ -14,14 +14,14 @@ describe('config/src/index', () => {
it('returns filter config only containing allowed keys', () => {
const keys = configUtil.allowed({
'baseUrl': 'https://url.com',
'blacklistHosts': 'breaking option',
'videoUploadOnPasses': true,
'devServerPublicPathRoute': 'internal key',
'random': 'not a config option',
})
expect(keys).to.deep.eq({
'baseUrl': 'https://url.com',
'blacklistHosts': 'breaking option',
'videoUploadOnPasses': true,
})
})
})
@@ -30,7 +30,7 @@ describe('config/src/index', () => {
it('returns list of breaking config keys', () => {
const breakingKeys = configUtil.getBreakingKeys()
expect(breakingKeys).to.include('blacklistHosts')
expect(breakingKeys).to.include('videoUploadOnPasses')
snapshot(breakingKeys)
})
})
@@ -154,12 +154,12 @@ describe('config/src/index', () => {
const errorFn = sinon.spy()
configUtil.validateNoBreakingConfig({
'experimentalNetworkStubbing': 'should break',
'experimentalSessionAndOrigin': 'should break',
configFile: 'config.js',
}, warningFn, errorFn, 'e2e')
expect(warningFn).to.have.been.calledOnceWith('EXPERIMENTAL_NETWORK_STUBBING_REMOVED', {
name: 'experimentalNetworkStubbing',
expect(warningFn).to.have.been.calledOnceWith('EXPERIMENTAL_SESSION_AND_ORIGIN_REMOVED', {
name: 'experimentalSessionAndOrigin',
newName: undefined,
value: undefined,
testingType: 'e2e',
@@ -174,14 +174,14 @@ describe('config/src/index', () => {
const errorFn = sinon.spy()
configUtil.validateNoBreakingConfig({
'blacklistHosts': 'should break',
experimentalSkipDomainInjection: true,
configFile: 'config.js',
}, warningFn, errorFn, 'e2e')
expect(warningFn).to.have.been.callCount(0)
expect(errorFn).to.have.been.calledOnceWith('RENAMED_CONFIG_OPTION', {
name: 'blacklistHosts',
newName: 'blockHosts',
expect(errorFn).to.have.been.calledOnceWith('EXPERIMENTAL_SKIP_DOMAIN_INJECTION_REMOVED', {
name: 'experimentalSkipDomainInjection',
newName: undefined,
value: undefined,
testingType: 'e2e',
configFile: 'config.js',

View File

@@ -958,17 +958,6 @@ describe('config/src/project/utils', () => {
})
})
// @see https://github.com/cypress-io/cypress/issues/6892
it('warns if experimentalGetCookiesSameSite is passed', async function () {
const warning = sinon.spy(errors, 'warning')
await this.defaults('experimentalGetCookiesSameSite', true, {
experimentalGetCookiesSameSite: true,
})
expect(warning).to.be.calledWith('EXPERIMENTAL_SAMESITE_REMOVED')
})
it('warns if experimentalJustInTimeCompile is passed', async function () {
const warning = sinon.spy(errors, 'warning')
@@ -979,16 +968,6 @@ describe('config/src/project/utils', () => {
expect(warning).to.be.calledWith('EXPERIMENTAL_JIT_COMPILE_REMOVED')
})
it('warns if experimentalSessionSupport is passed', async function () {
const warning = sinon.spy(errors, 'warning')
await this.defaults('experimentalSessionSupport', true, {
experimentalSessionSupport: true,
})
expect(warning).to.be.calledWith('EXPERIMENTAL_SESSION_SUPPORT_REMOVED')
})
it('warns if experimentalSessionAndOrigin is passed', async function () {
const warning = sinon.spy(errors, 'warning')
@@ -999,47 +978,6 @@ describe('config/src/project/utils', () => {
expect(warning).to.be.calledWith('EXPERIMENTAL_SESSION_AND_ORIGIN_REMOVED')
})
it('warns if experimentalShadowDomSupport is passed', async function () {
const warning = sinon.spy(errors, 'warning')
await this.defaults('experimentalShadowDomSupport', true, {
experimentalShadowDomSupport: true,
})
expect(warning).to.be.calledWith('EXPERIMENTAL_SHADOW_DOM_REMOVED')
})
it('warns if experimentalRunEvents is passed', async function () {
const warning = sinon.spy(errors, 'warning')
await this.defaults('experimentalRunEvents', true, {
experimentalRunEvents: true,
})
expect(warning).to.be.calledWith('EXPERIMENTAL_RUN_EVENTS_REMOVED')
})
// @see https://github.com/cypress-io/cypress/pull/9185
it('warns if experimentalNetworkStubbing is passed', async function () {
const warning = sinon.spy(errors, 'warning')
await this.defaults('experimentalNetworkStubbing', true, {
experimentalNetworkStubbing: true,
})
expect(warning).to.be.calledWith('EXPERIMENTAL_NETWORK_STUBBING_REMOVED')
})
it('warns if firefoxGcInterval is passed', async function () {
const warning = sinon.spy(errors, 'warning')
await this.defaults('firefoxGcInterval', true, {
firefoxGcInterval: true,
})
expect(warning).to.be.calledWith('FIREFOX_GC_INTERVAL_REMOVED')
})
describe('.resolved', () => {
it('sets reporter and port to cli', () => {
const obj = {

View File

@@ -1,266 +0,0 @@
exports['cypress.config.js generation should create a string when passed only a global option 1'] = `
const { defineConfig } = require('cypress')
module.exports = defineConfig({
viewportWidth: 300,
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
},
component: {
setupNodeEvents(on, config) {},
},
})
`
exports['cypress.config.js generation should create a string when passed only a e2e options 1'] = `
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
baseUrl: 'localhost:3000',
},
component: {
setupNodeEvents(on, config) {},
},
})
`
exports['cypress.config.js generation should create a string when passed only a component options 1'] = `
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
},
component: {
setupNodeEvents(on, config) {},
retries: 2,
},
})
`
exports['cypress.config.js generation should create a string for a config with global, component, and e2e options 1'] = `
const { defineConfig } = require('cypress')
module.exports = defineConfig({
viewportWidth: 300,
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
retries: 2,
baseUrl: 'localhost:300',
slowTestThreshold: 500,
},
component: {
setupNodeEvents(on, config) {},
retries: 1,
slowTestThreshold: 500,
},
})
`
exports['cypress.config.js generation should create a string when passed an empty object 1'] = `
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
},
component: {
setupNodeEvents(on, config) {},
},
})
`
exports['cypress.config.js generation should handle export default in plugins file 1'] = `
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.ts').default(on, config)
},
},
component: {
setupNodeEvents(on, config) {},
},
})
`
exports['cypress.config.js generation should exclude fields that are no longer valid 1'] = `
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
},
component: {
setupNodeEvents(on, config) {},
specPattern: 'path/to/component/folder/**/*.cy.{js,jsx,ts,tsx}',
},
})
`
exports['cypress.config.js generation should create only a component entry when no e2e specs are detected 1'] = `
const { defineConfig } = require('cypress')
module.exports = defineConfig({
component: {
setupNodeEvents(on, config) {},
},
})
`
exports['cypress.config.js generation should create only an e2e entry when no component specs are detected 1'] = `
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
},
})
`
exports['cypress.config.js generation should maintain both root level and nested non-breaking options during migration 1'] = `
import { defineConfig } from 'cypress'
export default defineConfig({
viewportWidth: 1200,
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
viewportWidth: 1600,
},
component: {
setupNodeEvents(on, config) {},
viewportWidth: 400,
},
})
`
exports['cypress.config.js generation should add custom specPattern if project has projectId 1'] = `
import { defineConfig } from 'cypress'
export default defineConfig({
projectId: 'abc1234',
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
baseUrl: 'http://localhost:3000',
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
},
component: {
setupNodeEvents(on, config) {},
},
})
`
exports['cypress.config.js generation should not add custom specPattern if project has projectId and integrationFolder 1'] = `
import { defineConfig } from 'cypress'
export default defineConfig({
projectId: 'abc1234',
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
baseUrl: 'http://localhost:3000',
specPattern: 'cypress/custom/e2e/**/*.{js,jsx,ts,tsx}',
},
component: {
setupNodeEvents(on, config) {},
},
})
`
exports['cypress.config.js generation generates correct config for component testing migration with custom testFiles glob 1'] = `
const { defineConfig } = require('cypress')
module.exports = defineConfig({
component: {
setupNodeEvents(on, config) {},
specPattern: './**/*.spec.cy.{js,ts,jsx,tsx}',
},
})
`
exports['cypress.config.js generation should create a string when passed an empty object for an ECMA Script project 1'] = `
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
},
component: {
setupNodeEvents(on, config) {},
},
})
`
exports['cypress.config.js generation generates correct config for component testing migration with custom testFiles array of glob 1'] = `
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {},
specPattern: ['cypress/e2e/**/*.spec.js', 'cypress/e2e/**/*.test.js'],
},
})
`

View File

@@ -6,7 +6,6 @@ import {
FileActions,
ProjectActions,
WizardActions,
MigrationActions,
BrowserActions,
DevActions,
AuthActions,
@@ -30,7 +29,6 @@ export class DataActions {
private _wizard: WizardActions
private _project: ProjectActions
private _electron: ElectronActions
private _migration: MigrationActions
private _browser: BrowserActions
private _servers: ServersActions
private _versions: VersionsActions
@@ -50,7 +48,6 @@ export class DataActions {
this._wizard = new WizardActions(this.ctx)
this._project = new ProjectActions(this.ctx)
this._electron = new ElectronActions(this.ctx)
this._migration = new MigrationActions(this.ctx)
this._browser = new BrowserActions(this.ctx)
this._servers = new ServersActions(this.ctx)
this._versions = new VersionsActions(this.ctx)
@@ -97,10 +94,6 @@ export class DataActions {
return this._electron
}
get migration () {
return this._migration
}
get browser () {
return this._browser
}

View File

@@ -25,7 +25,6 @@ import {
HtmlDataSource,
UtilDataSource,
BrowserApiShape,
MigrationDataSource,
RelevantRunsDataSource,
RelevantRunSpecsDataSource,
VersionsDataSource,
@@ -101,7 +100,6 @@ export class DataContext {
private _html: HtmlDataSource
private _error: ErrorDataSource
private _util: UtilDataSource
private _migration: MigrationDataSource
readonly lifecycleManager: ProjectLifecycleManager
@@ -136,7 +134,6 @@ export class DataContext {
this._html = new HtmlDataSource(this)
this._error = new ErrorDataSource(this)
this._util = new UtilDataSource(this)
this._migration = new MigrationDataSource(this)
// the lifecycle manager needs to be initialized last as it needs properties instantiated on the DataContext object
this.lifecycleManager = new ProjectLifecycleManager(this)
}
@@ -236,10 +233,6 @@ export class DataContext {
return this._util
}
get migration () {
return this._migration
}
/**
* This will be replaced with Immer, for immutable state updates.
*/

View File

@@ -1,446 +0,0 @@
/* eslint-disable no-dupe-class-members */
import path from 'path'
import debugLib from 'debug'
import { fork } from 'child_process'
import fs from 'fs-extra'
import os from 'os'
import semver from 'semver'
import type { ForkOptions } from 'child_process'
import assert from 'assert'
import _ from 'lodash'
import type { DataContext } from '..'
import { getError } from '@packages/errors'
import {
cleanUpIntegrationFolder,
formatConfig,
LegacyCypressConfigJson,
moveSpecFiles,
NonStandardMigrationError,
SpecToMove,
} from '../sources'
import {
tryGetDefaultLegacyPluginsFile,
supportFilesForMigration,
hasSpecFile,
getStepsForMigration,
getIntegrationFolder,
isDefaultTestFiles,
getComponentTestFilesGlobs,
getComponentFolder,
getIntegrationTestFilesGlobs,
getSpecPattern,
legacyOptions,
legacyIntegrationFolder,
getLegacyPluginsCustomFilePath,
} from '../sources/migration'
import { makeCoreData } from '../data'
import { LegacyPluginsIpc } from '../data/LegacyPluginsIpc'
import { hasTypeScriptInstalled, toPosix } from '../util'
const debug = debugLib('cypress:data-context:MigrationActions')
// NOTE: need the file:// prefix to avoid https://nodejs.org/api/errors.html#err_unsupported_esm_url_scheme on windows
const tsxCjs = os.platform() === 'win32' ? `file://${toPosix(require.resolve('tsx/cjs'))}` : toPosix(require.resolve('tsx/cjs'))
export function getConfigWithDefaults (legacyConfig: any) {
const newConfig = _.cloneDeep(legacyConfig)
legacyOptions.forEach(({ defaultValue, name }) => {
if (defaultValue !== undefined && legacyConfig[name] === undefined) {
newConfig[name] = typeof defaultValue === 'function' ? defaultValue() : defaultValue
}
})
return newConfig
}
export function getDiff (oldConfig: any, newConfig: any) {
// get all the values updated
const result: any = _.reduce(oldConfig, (acc: any, value, key) => {
// ignore values that have been removed
if (newConfig[key] && !_.isEqual(value, newConfig[key])) {
acc[key] = newConfig[key]
}
return acc
}, {})
// get all the values added
return _.reduce(newConfig, (acc: any, value, key) => {
// their key is in the new config but not in the old config
if (!oldConfig.hasOwnProperty(key)) {
acc[key] = value
}
return acc
}, result)
}
export async function processConfigViaLegacyPlugins (projectRoot: string, legacyConfig: LegacyCypressConfigJson, nodeVersion: string | undefined | null): Promise<LegacyCypressConfigJson> {
const pluginFile = legacyConfig.pluginsFile
? await getLegacyPluginsCustomFilePath(projectRoot, legacyConfig.pluginsFile)
: await tryGetDefaultLegacyPluginsFile(projectRoot)
debug('found legacy pluginsFile at %s', pluginFile)
return new Promise((resolve, reject) => {
// couldn't find a pluginsFile
// just bail with initial config
if (!pluginFile) {
return resolve(legacyConfig)
}
const cwd = path.join(projectRoot, pluginFile)
const childOptions: ForkOptions = {
stdio: 'inherit',
cwd: path.dirname(cwd),
env: _.omit(process.env, 'CYPRESS_INTERNAL_E2E_TESTING_SELF'),
}
const configProcessArgs = ['--projectRoot', projectRoot, '--file', cwd]
const CHILD_PROCESS_FILE_PATH = require.resolve('@packages/server/lib/plugins/child/require_async_child')
// use tsx if they've got typescript installed
// this matches the 9.x behavior, which is what we want for
// processing legacy pluginsFile (we never supported `"type": "module") in 9.x.
if (hasTypeScriptInstalled(projectRoot)) {
let tsxLoader = nodeVersion && semver.lt(nodeVersion, '20.6.0') ? `--loader ${tsxCjs}` : `--import ${tsxCjs}`
debug(`using generic ${tsxLoader} for esm and cjs with TypeScript for legacy migration.`)
if (!childOptions.env) {
childOptions.env = {}
}
if (childOptions.env.NODE_OPTIONS) {
childOptions.env.NODE_OPTIONS += ` ${tsxLoader}`
} else {
childOptions.env.NODE_OPTIONS = tsxLoader
}
}
const childProcess = fork(CHILD_PROCESS_FILE_PATH, configProcessArgs, childOptions)
const ipc = new LegacyPluginsIpc(childProcess)
childProcess.on('error', (error) => {
error = getError('LEGACY_CONFIG_ERROR_DURING_MIGRATION', cwd, error)
reject(error)
ipc.killChildProcess()
})
const legacyConfigWithDefaults = getConfigWithDefaults(legacyConfig)
ipc.on('ready', () => {
debug('legacyConfigIpc:ready')
ipc.send('loadLegacyPlugins', legacyConfigWithDefaults)
})
ipc.on('loadLegacyPlugins:reply', (modifiedLegacyConfig) => {
debug('loadLegacyPlugins:reply')
const diff = getDiff(legacyConfigWithDefaults, modifiedLegacyConfig)
// if env is updated by plugins, avoid adding it to the config file
if (diff.env) {
delete diff.env
}
const legacyConfigWithChanges = _.merge(legacyConfig, diff)
resolve(legacyConfigWithChanges)
ipc.killChildProcess()
})
ipc.on('loadLegacyPlugins:error', (error) => {
debug('loadLegacyPlugins:error')
error = getError('LEGACY_CONFIG_ERROR_DURING_MIGRATION', cwd, error)
reject(error)
ipc.killChildProcess()
})
ipc.on('childProcess:unhandledError', (error) => {
debug('childProcess:unhandledError')
reject(error)
ipc.killChildProcess()
})
})
}
export class MigrationActions {
constructor (private ctx: DataContext) { }
async initialize (config: LegacyCypressConfigJson) {
const legacyConfigForMigration = await this.setLegacyConfigForMigration(config)
this.reset(legacyConfigForMigration)
if (!this.ctx.currentProject || !legacyConfigForMigration) {
throw Error('cannot do migration without currentProject!')
}
if (this.ctx.coreData.app.isGlobalMode) {
const version = await this.locallyInstalledCypressVersion(this.ctx.currentProject)
if (!version) {
// Could not resolve Cypress. Unlikely, but they are using a
// project with Cypress that is nested more deeply than
// another project, which has a `cypress.json` but has not had
// it's node_modules installed, or it relies on a global version
// of Cypress that is missing for whatever reason.
return this.ctx.onError(getError('MIGRATION_CYPRESS_NOT_FOUND'))
}
const currentVersion = (await this.ctx.versions.versionData()).current.version
// Validate that the project being migrated has a version of Cypress compatible with the version being executed.
// This handles situations where Cypress is launched in global mode to migrate a project with an older version of
// Cypress as a dependency which could break the project when launched directly.
// For example:
// Local: 9.6.0 Global: 10.0.0 FAIL
// Local: 10.0.1 Global: 10.0.0 PASS
// Local: 12.0.0 Global: 12.0.1 FAIL
if (!semver.satisfies(version, `^${currentVersion}`)) {
return this.ctx.onError(getError('MIGRATION_MISMATCHED_CYPRESS_VERSIONS', version, currentVersion))
}
}
await this.initializeFlags()
const legacyConfigFileExist = this.ctx.migration.legacyConfigFileExists()
const filteredSteps = await getStepsForMigration(this.ctx.currentProject, legacyConfigForMigration, Boolean(legacyConfigFileExist))
this.ctx.update((coreData) => {
if (!filteredSteps[0]) {
throw Error(`Impossible to initialize a migration. No steps fit the configuration of this project.`)
}
coreData.migration.filteredSteps = filteredSteps
coreData.migration.step = filteredSteps[0]
})
}
async locallyInstalledCypressVersion (currentProject: string) {
try {
const localCypressPkgJsonPath = require.resolve(path.join('cypress', 'package.json'), {
paths: [currentProject],
})
const localCypressPkgJson = await fs.readJson(path.join(localCypressPkgJsonPath)) as { version: string }
return localCypressPkgJson?.version ?? undefined
} catch (e) {
// node_modules was not found, or some other unexpected error
// return undefined and surface the correct error.
return undefined
}
}
/**
* Figure out all the data required for the migration UI.
* This drives which migration steps need be shown and performed.
*/
private async initializeFlags () {
const legacyConfigForMigration = this.ctx.coreData.migration.legacyConfigForMigration
if (!this.ctx.currentProject || !legacyConfigForMigration) {
throw Error('Need currentProject to do migration')
}
const integrationFolder = getIntegrationFolder(legacyConfigForMigration)
const integrationTestFiles = getIntegrationTestFilesGlobs(legacyConfigForMigration)
const hasCustomIntegrationFolder = getIntegrationFolder(legacyConfigForMigration) !== legacyIntegrationFolder
const hasCustomIntegrationTestFiles = !isDefaultTestFiles(legacyConfigForMigration, 'integration')
const shouldAddCustomE2ESpecPattern = Boolean(this.ctx.migration.legacyConfigProjectId)
let hasE2ESpec = integrationFolder
? await hasSpecFile(this.ctx.currentProject, integrationFolder, integrationTestFiles)
: false
// if we don't find specs in the 9.X scope,
// let's check already migrated files.
// this allows users to stop migration halfway,
// then to pick up where they left migration off
if (!hasE2ESpec && (!hasCustomIntegrationTestFiles || !hasCustomIntegrationFolder)) {
const newE2eSpecPattern = getSpecPattern(legacyConfigForMigration, 'e2e', shouldAddCustomE2ESpecPattern)
hasE2ESpec = await hasSpecFile(this.ctx.currentProject, '', newE2eSpecPattern)
}
const componentFolder = getComponentFolder(legacyConfigForMigration)
const componentTestFiles = getComponentTestFilesGlobs(legacyConfigForMigration)
const hasCustomComponentFolder = componentFolder !== 'cypress/component'
const hasCustomComponentTestFiles = !isDefaultTestFiles(legacyConfigForMigration, 'component')
// A user is considered to "have" component testing if either
// 1. they have a default component folder (cypress/component) with at least 1 spec file
// OR
// 2. they have configured a non-default componentFolder (even if it doesn't have any specs.)
const hasSpecInDefaultComponentFolder = await hasSpecFile(this.ctx.currentProject, componentFolder, componentTestFiles)
const hasComponentTesting = (hasCustomComponentFolder || hasSpecInDefaultComponentFolder) ?? false
this.ctx.update((coreData) => {
coreData.migration.flags = {
hasCustomIntegrationFolder,
hasCustomIntegrationTestFiles,
hasCustomComponentFolder,
hasCustomComponentTestFiles,
hasCustomSupportFile: false,
hasComponentTesting,
hasE2ESpec,
hasPluginsFile: true,
shouldAddCustomE2ESpecPattern,
}
})
}
get configFileNameAfterMigration () {
return this.ctx.migration.legacyConfigFile.replace('.json', `.config.${this.ctx.lifecycleManager.fileExtensionToUse}`)
}
async createConfigFile () {
const config = await this.ctx.migration.createConfigString()
this.ctx.lifecycleManager.setConfigFilePath(this.configFileNameAfterMigration)
await this.ctx.fs.writeFile(this.ctx.lifecycleManager.configFilePath, config).catch((error) => {
throw error
})
await this.ctx.actions.file.removeFileInProject(this.ctx.migration.legacyConfigFile).catch((error) => {
throw error
})
if (this.ctx.modeOptions.configFile) {
// @ts-ignore configFile needs to be updated with the new one, so it finds the correct one
// with the new file, instead of the deleted one which is not supported anymore
this.ctx.modeOptions.configFile = this.ctx.migration.configFileNameAfterMigration
}
}
async setLegacyConfigForMigration (config: LegacyCypressConfigJson) {
assert(this.ctx.currentProject)
const legacyConfigForMigration = await processConfigViaLegacyPlugins(this.ctx.currentProject, config, this.ctx.coreData.app.nodeVersion)
this.ctx.update((coreData) => {
coreData.migration.legacyConfigForMigration = legacyConfigForMigration
})
return legacyConfigForMigration
}
async renameSpecsFolder () {
if (!this.ctx.currentProject) {
throw Error('Need to set currentProject before you can rename specs folder')
}
const projectRoot = this.ctx.path.join(this.ctx.currentProject)
const from = path.join(projectRoot, 'cypress', 'integration')
const to = path.join(projectRoot, 'cypress', 'e2e')
this.ctx.update((coreData) => {
coreData.migration.flags = {
...coreData.migration.flags,
shouldAddCustomE2ESpecPattern: true,
}
})
await this.ctx.fs.move(from, to)
}
async renameSpecFiles (beforeSpecs: string[], afterSpecs: string[]) {
if (!this.ctx.currentProject) {
throw Error('Need to set currentProject before you can rename files')
}
const specsToMove: SpecToMove[] = []
for (let i = 0; i < beforeSpecs.length; i++) {
const from = beforeSpecs[i]
const to = afterSpecs[i]
if (!from || !to) {
throw Error(`Must have matching to and from. Got from: ${from} and to: ${to}`)
}
specsToMove.push({ from, to })
}
const projectRoot = this.ctx.path.join(this.ctx.currentProject)
await moveSpecFiles(projectRoot, specsToMove)
await cleanUpIntegrationFolder(this.ctx.currentProject)
}
async renameSupportFile () {
if (!this.ctx.currentProject) {
throw Error(`Need current project before starting migration!`)
}
const result = await supportFilesForMigration(this.ctx.currentProject)
const beforeRelative = result.before.relative
const afterRelative = result.after.relative
if (!beforeRelative || !afterRelative) {
throw new NonStandardMigrationError('support')
}
await this.ctx.fs.rename(
path.join(this.ctx.currentProject, beforeRelative),
path.join(this.ctx.currentProject, afterRelative),
)
}
async finishReconfigurationWizard () {
this.ctx.lifecycleManager.refreshMetaState()
await this.ctx.lifecycleManager.refreshLifecycle()
}
async nextStep () {
const filteredSteps = this.ctx.coreData.migration.filteredSteps
const index = filteredSteps.indexOf(this.ctx.coreData.migration.step)
if (index === -1) {
throw new Error('Invalid step')
}
const nextIndex = index + 1
if (nextIndex < filteredSteps.length) {
const nextStep = filteredSteps[nextIndex]
if (nextStep) {
this.ctx.update((coreData) => {
coreData.migration.step = nextStep
})
}
} else {
await this.finishReconfigurationWizard()
}
}
async closeManualRenameWatcher () {
await this.ctx.migration.closeManualRenameWatcher()
}
async assertSuccessfulConfigMigration (migratedConfigFile: string = 'cypress.config.js') {
const actual = formatConfig(await this.ctx.file.readFileInProject(migratedConfigFile))
const configExtension = path.extname(migratedConfigFile)
const expected = formatConfig(await this.ctx.file.readFileInProject(`expected-cypress.config${configExtension}`))
if (actual !== expected) {
throw Error(`Expected ${actual} to equal ${expected}`)
}
}
reset (config?: LegacyCypressConfigJson) {
this.ctx.update((coreData) => {
coreData.migration = { ...makeCoreData().migration, legacyConfigForMigration: config }
})
}
}

View File

@@ -122,7 +122,6 @@ export class ProjectActions {
// Also clear any data associated with the linked cloud project
this.ctx.actions.cloudProject.clearCloudProject()
this.ctx.actions.migration.reset()
await this.ctx.lifecycleManager.clearCurrentProject()
resetIssuedWarnings()
await this.api.closeActiveProject()

View File

@@ -14,7 +14,6 @@ export * from './ErrorActions'
export * from './EventCollectorActions'
export * from './FileActions'
export * from './LocalSettingsActions'
export * from './MigrationActions'
export * from './NotificationActions'
export * from './ProjectActions'
export * from './ServersActions'

View File

@@ -3,7 +3,7 @@ import type { CodeGenType } from '@packages/graphql/src/gen/nxs.gen'
import fs from 'fs-extra'
import { uniq, upperFirst } from 'lodash'
import path from 'path'
import { FileExtension, getDefaultSpecFileName } from '../sources/migration/utils'
import { FileExtension, getDefaultSpecFileName } from '../util/files'
import { toPosix } from '../util'
import type { FoundSpec } from '@packages/types'

View File

@@ -1,44 +0,0 @@
/* eslint-disable no-dupe-class-members */
import type { ChildProcess } from 'child_process'
import EventEmitter from 'events'
import type { CypressError } from '@packages/errors'
import type { LegacyCypressConfigJson } from '../sources'
export class LegacyPluginsIpc extends EventEmitter {
constructor (readonly childProcess: ChildProcess) {
super()
childProcess.on('message', (msg: { event: string, args: any[] }) => {
this.emit(msg.event, ...msg.args)
})
childProcess.once('disconnect', () => {
this.emit('disconnect')
})
}
send(event: 'loadLegacyPlugins', legacyConfig: LegacyCypressConfigJson): boolean
send (event: string, ...args: any[]) {
if (this.childProcess.killed || !this.childProcess.connected) {
return false
}
return this.childProcess.send({ event, args })
}
on(event: 'ready', listener: () => void): this
on(event: 'loadLegacyPlugins:error', listener: (error: CypressError) => void): this
on(event: 'childProcess:unhandledError', listener: (legacyConfig: LegacyCypressConfigJson) => void): this
on(event: 'loadLegacyPlugins:reply', listener: (legacyConfig: LegacyCypressConfigJson) => void): this
on (evt: string, listener: (...args: any[]) => void) {
return super.on(evt, listener)
}
killChildProcess () {
this.childProcess.kill()
this.childProcess.stdout?.removeAllListeners()
this.childProcess.stderr?.removeAllListeners()
this.childProcess.removeAllListeners()
this.removeAllListeners()
}
}

View File

@@ -8,7 +8,6 @@
*/
import path from 'path'
import _ from 'lodash'
import resolve from 'resolve'
import fs from 'fs'
import { getError, CypressError, ConfigValidationFailureInfo } from '@packages/errors'
@@ -16,7 +15,7 @@ import type { DataContext } from '..'
import assert from 'assert'
import type { AllModeOptions, FoundBrowser, FullConfig, TestingType } from '@packages/types'
import { autoBindDebug } from '../util/autoBindDebug'
import { EventCollectorSource, GitDataSource, LegacyCypressConfigJson } from '../sources'
import { EventCollectorSource, GitDataSource } from '../sources'
import { OnFinalConfigLoadedOptions, ProjectConfigManager } from './ProjectConfigManager'
import pDefer from 'p-defer'
import { EventRegistrar } from './EventRegistrar'
@@ -56,23 +55,19 @@ export interface InjectedConfigApi {
export interface ProjectMetaState {
isUsingTypeScript: boolean
hasLegacyCypressJson: boolean
hasCypressEnvFile: boolean
hasValidConfigFile: boolean
hasSpecifiedConfigViaCLI: false | string
allFoundConfigFiles: string[]
needsCypressJsonMigration: boolean
isProjectUsingESModules: boolean
}
const PROJECT_META_STATE: ProjectMetaState = {
isUsingTypeScript: false,
hasLegacyCypressJson: false,
allFoundConfigFiles: [],
hasCypressEnvFile: false,
hasSpecifiedConfigViaCLI: false,
hasValidConfigFile: false,
needsCypressJsonMigration: false,
isProjectUsingESModules: false,
}
@@ -108,11 +103,6 @@ export class ProjectLifecycleManager {
async getProjectId (): Promise<string | null> {
try {
// No need to kick off config initialization if we need to migrate
if (this.ctx.migration.needsCypressJsonMigration()) {
return null
}
const contents = await this.ctx.project.getConfig()
return contents.projectId ?? null
@@ -514,37 +504,13 @@ export class ProjectLifecycleManager {
* @returns true if we can initialize and false if not
*/
private readyToInitialize (projectRoot: string): boolean {
const { needsCypressJsonMigration } = this.refreshMetaState()
const legacyConfigPath = path.join(projectRoot, this.ctx.migration.legacyConfigFile)
if (needsCypressJsonMigration && !this.ctx.isRunMode && this.ctx.fs.existsSync(legacyConfigPath)) {
return false
}
this.legacyPluginGuard()
// This calls a lot of methods that are necessary to check config-wise upfront
this.refreshMetaState()
this.configFileWarningCheck()
return this.metaState.hasValidConfigFile
}
async legacyMigration () {
try {
const legacyConfigPath = path.join(this.projectRoot, this.ctx.migration.legacyConfigFile)
// we run the legacy plugins/index.js in a child process
// and mutate the config based on the return value for migration
// only used in open mode (cannot migrate via terminal)
const legacyConfig = await this.ctx.fs.readJson(legacyConfigPath) as LegacyCypressConfigJson
// should never throw, unless there existing pluginsFile errors out,
// in which case they are attempting to migrate an already broken project.
await this.ctx.actions.migration.initialize(legacyConfig)
} catch (error) {
this.onLoadError(error)
}
}
get runModeExitEarly () {
return this._runModeExitEarly
}
@@ -658,19 +624,6 @@ export class ProjectLifecycleManager {
return this._eventRegistrar.executeNodeEvent(event, args)
}
private legacyPluginGuard () {
// test and warn for incompatible plugin
try {
const retriesPluginPath = path.dirname(resolve.sync('cypress-plugin-retries/package.json', {
basedir: this.projectRoot,
}))
this.ctx.onWarning(getError('INCOMPATIBLE_PLUGIN_RETRIES', path.relative(this.projectRoot, retriesPluginPath)))
} catch (e) {
// noop, incompatible plugin not installed
}
}
/**
* Find all information about the project we need to know to prompt different
* onboarding screens, suggestions in the onboarding wizard, etc.
@@ -679,7 +632,6 @@ export class ProjectLifecycleManager {
const configFile = this.ctx.modeOptions.configFile
const metaState: ProjectMetaState = {
...PROJECT_META_STATE,
hasLegacyCypressJson: this.ctx.migration.legacyConfigFileExists(),
hasCypressEnvFile: fs.existsSync(this._pathToFile('cypress.env.json')),
}
@@ -696,7 +648,6 @@ export class ProjectLifecycleManager {
projectRoot: this.projectRoot,
customConfigFile: configFile,
pkgJson,
isMigrating: metaState.hasLegacyCypressJson,
}) === 'ts'
} catch {
// No need to handle
@@ -704,23 +655,10 @@ export class ProjectLifecycleManager {
if (configFile) {
metaState.hasSpecifiedConfigViaCLI = this._pathToFile(configFile)
if (configFile.endsWith('.json')) {
metaState.needsCypressJsonMigration = true
const configFileNameAfterMigration = configFile.replace('.json', `.config.${metaState.isUsingTypeScript ? 'ts' : 'js'}`)
if (this.ctx.fs.existsSync(this._pathToFile(configFileNameAfterMigration))) {
if (this.ctx.fs.existsSync(this._pathToFile(configFile))) {
this.ctx.onError(getError('LEGACY_CONFIG_FILE', configFileNameAfterMigration, this.projectRoot, configFile))
} else {
this.ctx.onError(getError('MIGRATION_ALREADY_OCURRED', configFileNameAfterMigration, configFile))
}
}
} else {
this.setConfigFilePath(configFile)
if (fs.existsSync(this.configFilePath)) {
metaState.hasValidConfigFile = true
}
this.setConfigFilePath(configFile)
if (fs.existsSync(this.configFilePath)) {
metaState.hasValidConfigFile = true
}
this._projectMetaState = metaState
@@ -758,10 +696,6 @@ export class ProjectLifecycleManager {
configFilePathSet = true
}
if (metaState.hasLegacyCypressJson && !metaState.hasValidConfigFile) {
metaState.needsCypressJsonMigration = true
}
this._projectMetaState = metaState
return metaState
@@ -824,7 +758,7 @@ export class ProjectLifecycleManager {
return
}
if (testingType === 'e2e' && !this.ctx.migration.needsCypressJsonMigration()) {
if (testingType === 'e2e') {
// E2E doesn't have a wizard, so if we have a testing type on load we just create/update their cypress.config.js.
await this.ctx.actions.wizard.scaffoldTestingType()
} else if (testingType === 'component') {
@@ -864,17 +798,9 @@ export class ProjectLifecycleManager {
this.onLoadError(getError('CONFIG_FILE_NOT_FOUND', path.basename(this.metaState.hasSpecifiedConfigViaCLI), path.dirname(this.metaState.hasSpecifiedConfigViaCLI)))
}
if (this.metaState.hasLegacyCypressJson && !this.metaState.hasValidConfigFile && this.ctx.isRunMode) {
this.onLoadError(getError('CONFIG_FILE_MIGRATION_NEEDED', this.projectRoot))
}
if (this.metaState.allFoundConfigFiles.length > 1) {
this.onLoadError(getError('CONFIG_FILES_LANGUAGE_CONFLICT', this.projectRoot, this.metaState.allFoundConfigFiles))
}
if (this.metaState.hasValidConfigFile && this.metaState.hasLegacyCypressJson) {
this.onLoadError(getError('LEGACY_CONFIG_FILE', path.basename(this.configFilePath), this.projectRoot))
}
}
/**

View File

@@ -1,4 +1,4 @@
import { FoundBrowser, Editor, AllowedState, AllModeOptions, TestingType, BrowserStatus, PACKAGE_MANAGERS, AuthStateName, MIGRATION_STEPS, MigrationStep, StudioLifecycleManagerShape } from '@packages/types'
import type { FoundBrowser, Editor, AllowedState, AllModeOptions, TestingType, BrowserStatus, PACKAGE_MANAGERS, AuthStateName, StudioLifecycleManagerShape } from '@packages/types'
import { WizardBundler, CT_FRAMEWORKS, resolveComponentFrameworkDefinition, ErroredFramework } from '@packages/scaffold-config'
import type { NexusGenObjects } from '@packages/graphql/src/gen/nxs.gen'
// tslint:disable-next-line no-implicit-dependencies - electron dep needs to be defined
@@ -7,7 +7,7 @@ import type { ChildProcess } from 'child_process'
import type { SocketIONamespace, SocketIOServer } from '@packages/socket'
import type { Server } from 'http'
import type { ErrorWrapperSource } from '@packages/errors'
import type { EventCollectorSource, GitDataSource, LegacyCypressConfigJson } from '../sources'
import type { EventCollectorSource, GitDataSource } from '../sources'
import { machineId as getMachineId } from 'node-machine-id'
import type { CDPSocketServer } from '@packages/socket/lib/cdp-socket'
@@ -83,26 +83,6 @@ export interface WizardDataShape {
erroredFrameworks: ErroredFramework[]
}
export interface MigrationDataShape {
// TODO: have the model of migration here
step: MigrationStep
legacyConfigForMigration?: LegacyCypressConfigJson | null
filteredSteps: MigrationStep[]
flags: {
hasCustomIntegrationFolder: boolean
hasCustomIntegrationTestFiles: boolean
hasCustomComponentFolder: boolean
hasCustomComponentTestFiles: boolean
hasCustomSupportFile: boolean
hasComponentTesting: boolean
hasE2ESpec: boolean
hasPluginsFile: boolean
shouldAddCustomE2ESpecPattern: boolean
}
}
export interface ElectronShape {
app: App | null
browserWindow: BrowserWindow | null
@@ -150,7 +130,6 @@ export interface CoreDataShape {
currentTestingType: TestingType | null
diagnostics: Diagnostics
wizard: WizardDataShape
migration: MigrationDataShape
user: AuthenticatedUserShape | null
electron: ElectronShape
authState: AuthStateShape
@@ -215,22 +194,6 @@ export function makeCoreData (modeOptions: Partial<AllModeOptions> = {}): CoreDa
frameworks: CT_FRAMEWORKS.map((framework) => resolveComponentFrameworkDefinition(framework)),
erroredFrameworks: [],
},
migration: {
step: 'renameAuto',
legacyConfigForMigration: null,
filteredSteps: [...MIGRATION_STEPS],
flags: {
hasCustomIntegrationFolder: false,
hasCustomIntegrationTestFiles: false,
hasCustomComponentFolder: false,
hasCustomComponentTestFiles: false,
hasCustomSupportFile: false,
hasComponentTesting: true,
hasE2ESpec: true,
hasPluginsFile: true,
shouldAddCustomE2ESpecPattern: false,
},
},
activeBrowser: null,
user: null,
electron: {

View File

@@ -3,7 +3,6 @@
export * from './CypressEnv'
export * from './EventRegistrar'
export * from './LegacyPluginsIpc'
export * from './ProjectConfigIpc'
export * from './ProjectConfigManager'
export * from './ProjectLifecycleManager'

View File

@@ -1,299 +0,0 @@
import type { TestingType } from '@packages/types'
import type chokidar from 'chokidar'
import type { DataContext } from '..'
import {
createConfigString,
initComponentTestingMigration,
ComponentTestingMigrationStatus,
supportFilesForMigration,
getSpecs,
applyMigrationTransform,
shouldShowRenameSupport,
getIntegrationFolder,
isDefaultTestFiles,
getComponentTestFilesGlobs,
getComponentFolder,
} from './migration'
import _ from 'lodash'
import type { FilePart } from './migration/format'
import Debug from 'debug'
import path from 'path'
const debug = Debug('cypress:data-context:sources:MigrationDataSource')
export type LegacyCypressConfigJson = Partial<{
component: Omit<LegacyCypressConfigJson, 'component' | 'e2e'>
e2e: Omit<LegacyCypressConfigJson, 'component' | 'e2e'>
pluginsFile: string | false
supportFile: string | false
slowTestThreshold: number
componentFolder: string | false
integrationFolder: string
testFiles: string | string[]
ignoreTestFiles: string | string[]
env: { [key: string]: any }
[index: string]: any
}>
export interface MigrationFile {
testingType: TestingType
before: {
relative: string
parts: FilePart[]
}
after: {
relative: string
parts: FilePart[]
}
}
export class MigrationDataSource {
private componentTestingMigrationWatcher: chokidar.FSWatcher | null = null
componentTestingMigrationStatus?: ComponentTestingMigrationStatus
constructor (private ctx: DataContext) { }
get legacyConfig () {
if (!this.ctx.coreData.migration.legacyConfigForMigration) {
throw Error(`Expected _legacyConfig to be set. Did you forget to call MigrationDataSource#initialize?`)
}
return this.ctx.coreData.migration.legacyConfigForMigration
}
get legacyConfigProjectId () {
return this.legacyConfig.projectId || this.legacyConfig.e2e?.projectId
}
get shouldMigratePreExtension () {
return !this.legacyConfigProjectId
}
get legacyConfigFile () {
if (this.ctx.modeOptions.configFile && this.ctx.modeOptions.configFile.endsWith('.json')) {
return this.ctx.modeOptions.configFile
}
return 'cypress.json'
}
legacyConfigFileExists (): boolean {
// If we aren't in a current project we definitely don't have a legacy config file
if (!this.ctx.currentProject) {
return false
}
const configFilePath = path.isAbsolute(this.legacyConfigFile) ? this.legacyConfigFile : path.join(this.ctx.currentProject, this.legacyConfigFile)
const legacyConfigFileExists = this.ctx.fs.existsSync(configFilePath)
return Boolean(legacyConfigFileExists)
}
needsCypressJsonMigration (): boolean {
const legacyConfigFileExists = this.legacyConfigFileExists()
return this.ctx.lifecycleManager.metaState.needsCypressJsonMigration && Boolean(legacyConfigFileExists)
}
async getComponentTestingMigrationStatus () {
debug('getComponentTestingMigrationStatus: start')
if (!this.legacyConfig || !this.ctx.currentProject) {
throw Error('Need currentProject and config to continue')
}
const componentFolder = getComponentFolder(this.legacyConfig)
// no component folder, so no specs to migrate
// this should never happen since we never show the
// component specs migration step ("renameManual")
if (componentFolder === false) {
return null
}
debug('getComponentTestingMigrationStatus: componentFolder', componentFolder)
if (!this.componentTestingMigrationWatcher) {
debug('getComponentTestingMigrationStatus: initializing watcher')
const onFileMoved = async (status: ComponentTestingMigrationStatus) => {
this.componentTestingMigrationStatus = status
debug('getComponentTestingMigrationStatus: file moved %O', status)
if (status.completed) {
await this.componentTestingMigrationWatcher?.close()
this.componentTestingMigrationWatcher = null
}
// TODO(lachlan): is this the right place to use the emitter?
this.ctx.emitter.toLaunchpad()
}
const { status, watcher } = await initComponentTestingMigration(
this.ctx.currentProject,
componentFolder,
getComponentTestFilesGlobs(this.legacyConfig),
onFileMoved,
)
this.componentTestingMigrationStatus = status
this.componentTestingMigrationWatcher = watcher
debug('getComponentTestingMigrationStatus: watcher initialized. Status: %o', status)
}
if (!this.componentTestingMigrationStatus) {
throw Error(`Status should have been assigned by the watcher. Something is wrong`)
}
return this.componentTestingMigrationStatus
}
async supportFilesForMigrationGuide (): Promise<MigrationFile | null> {
if (!this.ctx.currentProject) {
throw Error('Need this.ctx.currentProject')
}
debug('supportFilesForMigrationGuide: config %O', this.legacyConfig)
if (!await shouldShowRenameSupport(this.ctx.currentProject, this.legacyConfig)) {
return null
}
if (!this.ctx.currentProject) {
throw Error(`Need this.ctx.projectRoot!`)
}
try {
const supportFiles = await supportFilesForMigration(this.ctx.currentProject)
debug('supportFilesForMigrationGuide: supportFiles %O', supportFiles)
return supportFiles
} catch (err) {
debug('supportFilesForMigrationGuide: err %O', err)
return null
}
}
async getSpecsForMigrationGuide (): Promise<MigrationFile[]> {
if (!this.ctx.currentProject) {
throw Error(`Need this.ctx.projectRoot!`)
}
const specs = await getSpecs(this.ctx.currentProject, this.legacyConfig)
const e2eMigrationOptions = {
// If the configFile has projectId, we do not want to change the preExtension
// so, we can keep the cloud history
shouldMigratePreExtension: this.shouldMigratePreExtension,
}
const canBeAutomaticallyMigrated: MigrationFile[] = specs.integration.map((s) => applyMigrationTransform(s, e2eMigrationOptions)).filter((spec) => spec.before.relative !== spec.after.relative)
const defaultComponentPattern = isDefaultTestFiles(this.legacyConfig, 'component')
// Can only migration component specs if they use the default testFiles pattern.
if (defaultComponentPattern) {
canBeAutomaticallyMigrated.push(...specs.component.map((s) => applyMigrationTransform(s)).filter((spec) => spec.before.relative !== spec.after.relative))
}
return this.checkAndUpdateDuplicatedSpecs(canBeAutomaticallyMigrated)
}
async createConfigString () {
if (!this.ctx.currentProject) {
throw Error('Need currentProject!')
}
const { isUsingTypeScript } = this.ctx.lifecycleManager.metaState
return createConfigString(this.legacyConfig, {
hasComponentTesting: this.ctx.coreData.migration.flags.hasComponentTesting,
hasE2ESpec: this.ctx.coreData.migration.flags.hasE2ESpec,
hasPluginsFile: this.ctx.coreData.migration.flags.hasPluginsFile,
projectRoot: this.ctx.currentProject,
isUsingTypeScript,
isProjectUsingESModules: this.ctx.lifecycleManager.metaState.isProjectUsingESModules,
shouldAddCustomE2ESpecPattern: this.ctx.coreData.migration.flags.shouldAddCustomE2ESpecPattern,
})
}
async integrationFolder () {
return getIntegrationFolder(this.legacyConfig)
}
async componentFolder () {
return getComponentFolder(this.legacyConfig)
}
async closeManualRenameWatcher () {
if (this.componentTestingMigrationWatcher) {
await this.componentTestingMigrationWatcher.close()
this.componentTestingMigrationWatcher = null
}
}
get configFileNameAfterMigration () {
return this.legacyConfigFile.replace('.json', `.config.${this.ctx.lifecycleManager.fileExtensionToUse}`)
}
private checkAndUpdateDuplicatedSpecs (specs: MigrationFile[]) {
const updatedSpecs: MigrationFile[] = []
const sortedSpecs = this.sortSpecsByExtension(specs)
sortedSpecs.forEach((spec) => {
const specExist = _.find(updatedSpecs, (x) => x.after.relative === spec.after.relative)
if (specExist) {
const beforeParts: FilePart[] = JSON.parse(JSON.stringify(spec.before.parts))
const preExtensionBefore = beforeParts.find((part) => part.group === 'preExtension')
if (preExtensionBefore) {
preExtensionBefore.highlight = false
}
const afterParts: FilePart[] = JSON.parse(JSON.stringify(spec.after.parts))
const fileNameAfter = afterParts.find((part) => part.group === 'fileName')
if (fileNameAfter && preExtensionBefore) {
const beforePreExtension = preExtensionBefore?.text?.replace('.', '')
fileNameAfter.text = `${fileNameAfter.text}${beforePreExtension}`
}
spec.before.parts = beforeParts
spec.after.parts = afterParts
spec.after.relative = afterParts.map((x) => x.text).join('')
}
updatedSpecs.push(spec)
})
return updatedSpecs
}
private sortSpecsByExtension (specs: MigrationFile[]) {
const sortedExtensions = ['.spec.', '.Spec.', '_spec.', '_Spec.', '-spec.', '-Spec.', '.test.', '.Test.', '_test.', '_Test.', '-test.', '-Test.']
return specs.sort(function (a, b) {
function getExtIndex (spec: string) {
let index = -1
// Sort the specs based on the extension, giving priority to .spec
sortedExtensions.some((c, i) => {
if (~spec.indexOf(c)) {
index = i
return true
}
return false
})
return index
}
return getExtIndex(a.before.relative) - getExtIndex(b.before.relative)
})
}
}

View File

@@ -21,7 +21,7 @@ import { toPosix } from '../util/file'
import type { FilePartsShape } from '@packages/graphql/src/schemaTypes/objectTypes/gql-FileParts'
import type { ProjectShape } from '../data'
import type { FindSpecs } from '../actions'
import { FileExtension, getDefaultSpecFileName } from './migration/utils'
import { FileExtension, getDefaultSpecFileName } from '../util/files'
type SpecPatterns = {
specPattern?: string[]

View File

@@ -10,7 +10,6 @@ export * from './FileDataSource'
export * from './GitDataSource'
export * from './GraphQLDataSource'
export * from './HtmlDataSource'
export * from './MigrationDataSource'
export * from './ProjectDataSource'
export * from './RelevantRunSpecsDataSource'
export * from './RelevantRunsDataSource'
@@ -18,4 +17,3 @@ export * from './RemoteRequestDataSource'
export * from './UtilDataSource'
export * from './VersionsDataSource'
export * from './WizardDataSource'
export * from './migration/'

View File

@@ -1,174 +0,0 @@
import globby from 'globby'
import type { TestingType } from '@packages/types'
import {
defaultTestFilesGlob,
FilePart,
formatMigrationFile,
getComponentFolder,
getComponentTestFilesGlobs,
getIntegrationFolder,
getIntegrationTestFilesGlobs,
isDefaultTestFiles,
legacyIntegrationFolder,
regexps,
} from '.'
import type { MigrationFile } from '../MigrationDataSource'
import type { LegacyCypressConfigJson } from '..'
export interface MigrationSpec {
relative: string
usesDefaultFolder: boolean
usesDefaultTestFiles: boolean
testingType: TestingType
}
interface GetSpecs {
component: MigrationSpec[]
integration: MigrationSpec[]
}
export type MigrationTransformOptions = {
shouldMigratePreExtension: boolean
}
export const defaultMigrationTransformOptions = {
shouldMigratePreExtension: true,
}
export function substitute (part: FilePart, options: MigrationTransformOptions = defaultMigrationTransformOptions): FilePart {
// nothing to substitute, just a regular
// part of the file
if (!('group' in part)) {
return part
}
// cypress/integration -> cypress/e2e
if (part.group === 'folder' && part.text === 'integration') {
return { ...part, text: 'e2e' }
}
// basic.spec.js -> basic.cy.js
if (part.group === 'preExtension' && options.shouldMigratePreExtension) {
return { ...part, text: '.cy.' }
}
// support/index.js -> support/e2e.js
if (part.group === 'supportFileName' && part.text === 'index') {
return { ...part, text: 'e2e' }
}
return part
}
export function applyMigrationTransform (
spec: MigrationSpec,
options: MigrationTransformOptions = defaultMigrationTransformOptions,
): MigrationFile {
let regexp: RegExp
if (spec.testingType === 'e2e' && spec.usesDefaultFolder && spec.usesDefaultTestFiles) {
// e2e, cypress/integration, **/* (default testFiles)
regexp = new RegExp(regexps.e2e.before.defaultFolderDefaultTestFiles)
} else if (spec.testingType === 'e2e' && !spec.usesDefaultFolder && spec.usesDefaultTestFiles) {
// e2e, custom-folder, **/* (default testFiles)
regexp = new RegExp(regexps.e2e.before.customFolderDefaultTestFiles)
} else if (spec.testingType === 'e2e' && spec.usesDefaultFolder && !spec.usesDefaultTestFiles) {
// e2e, cypress/integration , **/*.spec.ts (custom testFiles)
regexp = new RegExp(regexps.e2e.before.defaultFolderCustomTestFiles)
} else if (spec.testingType === 'component' && spec.usesDefaultFolder && spec.usesDefaultTestFiles) {
// component, cypress/component , (default testFiles)
regexp = new RegExp(regexps.component.before.defaultFolderDefaultTestFiles)
} else if (spec.testingType === 'component' && !spec.usesDefaultFolder && spec.usesDefaultTestFiles) {
// component, cypress/custom-component , (default testFiles)
regexp = new RegExp(regexps.component.before.customFolderDefaultTestFiles)
} else {
// custom folder AND test files pattern
// should be impossible, we should not call this function in the first place.
throw Error(`Cannot use applyMigrationTransform on a project with a custom folder and custom testFiles.`)
}
const partsBeforeMigration = formatMigrationFile(spec.relative, regexp, options)
const partsAfterMigration = partsBeforeMigration.map((part) => {
// avoid re-renaming files with the right preExtension
// it would make a myFile.cy.cy.js file
if (part.highlight
&& part.group === 'preExtension'
&& /\.cy\.([j|t]s[x]?|coffee)$/.test(spec.relative)) {
return part
}
return substitute(part, options)
})
return {
testingType: spec.testingType,
before: {
relative: spec.relative,
parts: partsBeforeMigration,
},
after: {
relative: partsAfterMigration.map((x) => x.text).join(''),
parts: partsAfterMigration,
},
}
}
export async function getSpecs (projectRoot: string, config: LegacyCypressConfigJson): Promise<GetSpecs> {
const integrationFolder = getIntegrationFolder(config)
const integrationTestFiles = getIntegrationTestFilesGlobs(config)
const componentFolder = getComponentFolder(config)
const componentTestFiles = getComponentTestFilesGlobs(config)
let integrationSpecs: MigrationSpec[] = []
let componentSpecs: MigrationSpec[] = []
const globs = integrationFolder
? integrationFolder === legacyIntegrationFolder
? [defaultTestFilesGlob].map((glob) => `${integrationFolder}/${glob}`)
: integrationTestFiles.map((glob) => `${integrationFolder}/${glob}`)
: []
let specs = integrationFolder
? (await globby(globs, { onlyFiles: true, cwd: projectRoot }))
: []
const fullyCustom = integrationFolder !== legacyIntegrationFolder && !isDefaultTestFiles(config, 'integration')
// we cannot do a migration if either integrationFolder is false,
// or if both the integrationFolder and testFiles are custom.
if (fullyCustom) {
integrationSpecs = []
} else {
integrationSpecs = specs.map((relative) => {
return {
relative,
usesDefaultFolder: integrationFolder === legacyIntegrationFolder,
usesDefaultTestFiles: isDefaultTestFiles(config, 'integration'),
testingType: 'e2e',
}
})
}
if (componentFolder === false || !isDefaultTestFiles(config, 'component')) {
componentSpecs = []
} else {
const globs = componentTestFiles.map((glob) => {
return `${componentFolder}/${glob}`
})
componentSpecs = (await globby(globs, { onlyFiles: true, cwd: projectRoot })).map((relative) => {
return {
relative,
usesDefaultFolder: componentFolder === 'cypress/component',
usesDefaultTestFiles: isDefaultTestFiles(config, 'component'),
testingType: 'component',
}
})
}
return {
component: componentSpecs,
integration: integrationSpecs,
}
}

View File

@@ -1,623 +0,0 @@
import chokidar from 'chokidar'
import fs from 'fs-extra'
import path from 'path'
import globby from 'globby'
import type { TestingType } from '@packages/types'
import { formatMigrationFile } from './format'
import { substitute } from './autoRename'
import { supportFileRegexps } from './regexps'
import type { MigrationFile } from '../MigrationDataSource'
import { toPosix } from '../../util'
import Debug from 'debug'
import dedent from 'dedent'
import { hasDefaultExport } from './parserUtils'
import { isDefaultSupportFile, LegacyCypressConfigJson, legacyIntegrationFolder } from '..'
import { parse } from '@babel/parser'
import generate from '@babel/generator'
import _ from 'lodash'
import { defineConfigAvailable, getBreakingKeys } from '@packages/config'
const debug = Debug('cypress:data-context:sources:migration:codegen')
type ConfigOptions = {
global: Record<string, unknown>
e2e: Record<string, unknown>
component: Record<string, unknown>
}
type ResolvedConfigOptions = Cypress.ResolvedConfigOptions & {
testFiles: string | string[]
ignoreTestFiles: string | string[]
}
export class NonStandardMigrationError extends Error {
constructor (fileType: 'support' | 'config') {
super()
this.message = `Failed to find default ${fileType}. Bailing automation migration.`
}
}
export interface CreateConfigOptions {
hasE2ESpec: boolean
hasPluginsFile: boolean
hasComponentTesting: boolean
projectRoot: string
isUsingTypeScript: boolean
isProjectUsingESModules: boolean
shouldAddCustomE2ESpecPattern: boolean
}
export async function createConfigString (cfg: LegacyCypressConfigJson, options: CreateConfigOptions) {
const newConfig = reduceConfig(cfg, options)
const relativePluginPath = await getPluginRelativePath(cfg, options.projectRoot)
debug('creating cypress.config from newConfig %o relativePluginPath %s options %o', newConfig, relativePluginPath, options)
return createCypressConfig(newConfig, relativePluginPath, options)
}
interface FileToBeMigratedManually {
relative: string
moved: boolean
}
export interface ComponentTestingMigrationStatus {
files: Map<string, FileToBeMigratedManually>
completed: boolean
}
export async function initComponentTestingMigration (
projectRoot: string,
componentFolder: string,
testFiles: string[],
onFileMoved: (status: ComponentTestingMigrationStatus) => void,
): Promise<{
status: ComponentTestingMigrationStatus
watcher: chokidar.FSWatcher | null
}> {
debug('initComponentTestingMigration %O', { projectRoot, componentFolder, testFiles })
const watchPaths = testFiles.map((glob) => {
return `${componentFolder}/${glob}`
})
const watcher = chokidar.watch(
watchPaths, {
cwd: projectRoot,
ignorePermissionErrors: true,
},
)
debug('watchPaths %o', watchPaths)
let filesToBeMoved: Map<string, FileToBeMigratedManually> = (await globby(watchPaths, {
cwd: projectRoot,
})).reduce<Map<string, FileToBeMigratedManually>>((acc, relative) => {
acc.set(relative, { relative, moved: false })
return acc
}, new Map())
debug('files to be moved manually %o', filesToBeMoved)
if (filesToBeMoved.size === 0) {
// this should not happen as the step should be hidden in this case
// but files can have been moved manually before clicking next
return {
status: {
files: filesToBeMoved,
completed: true,
},
watcher: null,
}
}
watcher.on('unlink', (unlinkedPath) => {
const posixUnlinkedPath = toPosix(unlinkedPath)
const file = filesToBeMoved.get(posixUnlinkedPath)
if (!file) {
throw Error(`Watcher incorrectly triggered ${posixUnlinkedPath}
while watching ${Array.from(filesToBeMoved.keys()).join(', ')}
projectRoot: ${projectRoot}`)
}
file.moved = true
const completed = Array.from(filesToBeMoved.values()).every((value) => value.moved === true)
onFileMoved({
files: filesToBeMoved,
completed,
})
})
return new Promise((resolve, reject) => {
watcher.on('ready', () => {
debug('watcher ready')
resolve({
status: {
files: filesToBeMoved,
completed: false,
},
watcher,
})
}).on('error', (err) => {
reject(err)
})
})
}
async function getPluginRelativePath (cfg: LegacyCypressConfigJson, projectRoot: string): Promise<string | undefined> {
return cfg.pluginsFile ? cfg.pluginsFile : await tryGetDefaultLegacyPluginsFile(projectRoot)
}
async function createCypressConfig (config: ConfigOptions, pluginPath: string | undefined, options: CreateConfigOptions): Promise<string> {
const globalString = Object.keys(config.global).length > 0 ? `${formatObjectForConfig(config.global)},` : ''
const componentString = options.hasComponentTesting ? createComponentTemplate(config.component) : ''
const e2eString = options.hasE2ESpec
? await createE2ETemplate(pluginPath, options, config.e2e)
: ''
if (defineConfigAvailable(options.projectRoot)) {
if (options.isUsingTypeScript || options.isProjectUsingESModules) {
return formatConfig(dedent`
import { defineConfig } from 'cypress'
export default defineConfig({
${globalString}
${e2eString}
${componentString}
})`)
}
return formatConfig(dedent`
const { defineConfig } = require('cypress')
module.exports = defineConfig({
${globalString}
${e2eString}
${componentString}
})`)
}
if (options.isUsingTypeScript || options.isProjectUsingESModules) {
return formatConfig(`export default {${globalString}${e2eString}${componentString}}`)
}
return formatConfig(`module.exports = {${globalString}${e2eString}${componentString}}`)
}
function formatObjectForConfig (obj: Record<string, unknown>) {
return JSON.stringify(obj, null, 2).replace(/^[{]|[}]$/g, '') // remove opening and closing {}
}
// Returns path of `pluginsFile` relative to projectRoot
// Considers cases of:
// 1. `pluginsFile` pointing to a directory containing an index file
// 2. `pluginsFile` pointing to a file
//
// Example:
// - projectRoot
// --- cypress
// ----- plugins
// -------- index.js
// Both { "pluginsFile": "cypress/plugins"} and { "pluginsFile": "cypress/plugins/index.js" } are valid.
//
// Will return `cypress/plugins/index.js` for both cases.
export async function getLegacyPluginsCustomFilePath (projectRoot: string, pluginPath: string): Promise<string> {
debug('looking for pluginPath %s in projectRoot %s', pluginPath, projectRoot)
const pluginLoc = path.join(projectRoot, pluginPath)
debug('fs.stats on %s', pluginLoc)
let stats: fs.Stats
try {
stats = await fs.stat(pluginLoc)
} catch (e) {
throw Error(`Looked for pluginsFile at ${pluginPath}, but it was not found.`)
}
if (stats.isFile()) {
debug('found pluginsFile %s', pluginLoc)
return pluginPath
}
if (stats.isDirectory()) {
// Although you are supposed to pass a file to `pluginsFile`, we also supported
// passing a directory containing an `index` file.
// If pluginsFile is a directory, see if there is an index.{js,ts} and grab that.
// {
// "pluginsFile": "plugins"
// }
// Where cypress/plugins contains an `index.{js,ts,coffee...}` but NOT `index.d.ts`.
const ls = await fs.readdir(pluginLoc)
const indexFile = ls.find((file) => file.startsWith('index.') && !file.endsWith('.d.ts'))
debug('pluginsFile was a directory containing %o, looks like we want %s', ls, indexFile)
if (indexFile) {
const pathToIndex = path.join(pluginPath, indexFile)
debug('found pluginsFile %s', pathToIndex)
return pathToIndex
}
}
debug('error, could not find path to pluginsFile!')
throw Error(`Could not find pluginsFile. Received projectRoot ${projectRoot} and pluginPath: ${pluginPath}`)
}
async function createE2ETemplate (pluginPath: string | undefined, createConfigOptions: CreateConfigOptions, options: Record<string, unknown>) {
if (createConfigOptions.shouldAddCustomE2ESpecPattern && !options.specPattern) {
options.specPattern = 'cypress/e2e/**/*.{js,jsx,ts,tsx}'
}
if (!createConfigOptions.hasPluginsFile || !pluginPath) {
return dedent`
e2e: {
setupNodeEvents(on, config) {},${formatObjectForConfig(options)}
},
`
}
let relPluginsPath: string
const startsWithDotSlash = new RegExp(/^.\//)
if (startsWithDotSlash.test(pluginPath)) {
relPluginsPath = `'${pluginPath}'`
} else {
relPluginsPath = `'./${pluginPath}'`
}
const legacyPluginFileLoc = await getLegacyPluginsCustomFilePath(createConfigOptions.projectRoot, pluginPath)
const pluginFile = await fs.readFile(path.join(createConfigOptions.projectRoot, legacyPluginFileLoc), 'utf8')
const requirePlugins = hasDefaultExport(pluginFile)
? `return require(${relPluginsPath}).default(on, config)`
: `return require(${relPluginsPath})(on, config)`
const setupNodeEvents = dedent`
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
${requirePlugins}
}`
return dedent`
e2e: {
${setupNodeEvents},${formatObjectForConfig(options)}
},`
}
function createComponentTemplate (options: Record<string, unknown>) {
return `component: {
setupNodeEvents(on, config) {},${formatObjectForConfig(options)}
},`
}
export interface RelativeSpec {
relative: string
}
/**
* Checks that at least one spec file exist for testing type
*
* NOTE: this is what we use to see if CT/E2E is set up
*/
export async function hasSpecFile (projectRoot: string, folder: string | false, glob: string | string[]): Promise<boolean> {
if (!folder) {
return false
}
return (await globby(glob, {
cwd: path.join(projectRoot, folder),
onlyFiles: true,
})).length > 0
}
export async function tryGetDefaultLegacyPluginsFile (projectRoot: string) {
const files = await globby('cypress/plugins/index.*', { cwd: projectRoot, ignore: ['cypress/plugins/index.d.ts'] })
return files[0]
}
export async function tryGetDefaultLegacySupportFile (projectRoot: string) {
const files = await globby('cypress/support/index.*', { cwd: projectRoot, ignore: ['cypress/support/index.d.ts'] })
debug('tryGetDefaultLegacySupportFile: files %O', files)
return files[0]
}
export async function getDefaultLegacySupportFile (projectRoot: string) {
const defaultSupportFile = await tryGetDefaultLegacySupportFile(projectRoot)
if (!defaultSupportFile) {
throw new NonStandardMigrationError('support')
}
return defaultSupportFile
}
export async function supportFilesForMigration (projectRoot: string): Promise<MigrationFile> {
debug('Checking for support files in %s', projectRoot)
const defaultOldSupportFile = await getDefaultLegacySupportFile(projectRoot)
const defaultNewSupportFile = renameSupportFilePath(defaultOldSupportFile)
const afterParts = formatMigrationFile(
defaultOldSupportFile,
new RegExp(supportFileRegexps.e2e.beforeRegexp),
).map((part) => substitute(part))
return {
testingType: 'e2e',
before: {
relative: defaultOldSupportFile,
parts: formatMigrationFile(defaultOldSupportFile, new RegExp(supportFileRegexps.e2e.beforeRegexp)),
},
after: {
relative: defaultNewSupportFile,
parts: afterParts,
},
}
}
export interface SpecToMove {
from: string
to: string
}
export async function moveSpecFiles (projectRoot: string, specs: SpecToMove[]) {
await Promise.all(specs.map((spec) => {
const from = path.join(projectRoot, spec.from)
const to = path.join(projectRoot, spec.to)
if (from === to) {
return
}
return fs.move(from, to)
}))
}
export async function cleanUpIntegrationFolder (projectRoot: string) {
const integrationPath = path.join(projectRoot, 'cypress', 'integration')
const e2ePath = path.join(projectRoot, 'cypress', 'e2e')
try {
await fs.copy(integrationPath, e2ePath, { recursive: true })
await fs.remove(integrationPath)
} catch (e: any) {
// only throw if the folder exists
if (e.code !== 'ENOENT') {
throw e
}
}
}
export function renameSupportFilePath (relative: string) {
const res = new RegExp(supportFileRegexps.e2e.beforeRegexp).exec(relative)
if (!res?.groups?.supportFileName) {
throw new NonStandardMigrationError('support')
}
return relative.slice(0, res.index) + relative.slice(res.index).replace(res.groups.supportFileName, 'e2e')
}
export function reduceConfig (cfg: LegacyCypressConfigJson, options: CreateConfigOptions): ConfigOptions {
return Object.entries(cfg).reduce((acc, [key, val]) => {
switch (key) {
case 'pluginsFile':
case '$schema':
return acc
case 'e2e':
case 'component': {
const value = val as ResolvedConfigOptions
if (!value) {
return acc
}
const { testFiles, ignoreTestFiles, ...rest } = value
// don't include if it's the default! No need.
const specPattern = getSpecPattern(cfg, key, options.shouldAddCustomE2ESpecPattern)
const ext = '**/*.cy.{js,jsx,ts,tsx}'
const isDefaultE2E = key === 'e2e' && specPattern === `cypress/e2e/${ext}`
const isDefaultCT = key === 'component' && specPattern === ext
const breakingKeys = getBreakingKeys()
const restWithoutBreakingKeys = _.omit(rest, breakingKeys)
const existingWithoutBreakingKeys = _.omit(acc[key], breakingKeys)
if (isDefaultE2E || isDefaultCT) {
return {
...acc, [key]: {
...restWithoutBreakingKeys,
...existingWithoutBreakingKeys,
},
}
}
return {
...acc, [key]: {
...restWithoutBreakingKeys,
...existingWithoutBreakingKeys,
specPattern,
},
}
}
case 'integrationFolder':
// If the integration folder is set, but the value is the same as the default legacy one
// we do not want to update the config value, we keep using the new default.
if (val === legacyIntegrationFolder) {
return acc
}
return {
...acc,
e2e: { ...acc.e2e, specPattern: getSpecPattern(cfg, 'e2e', options.shouldAddCustomE2ESpecPattern) },
}
case 'componentFolder':
return {
...acc,
component: { ...acc.component, specPattern: getSpecPattern(cfg, 'component') },
}
case 'testFiles':
return {
...acc,
e2e: { ...acc.e2e, specPattern: getSpecPattern(cfg, 'e2e', options.shouldAddCustomE2ESpecPattern) },
component: { ...acc.component, specPattern: getSpecPattern(cfg, 'component') },
}
case 'ignoreTestFiles':
return {
...acc,
e2e: { ...acc.e2e, excludeSpecPattern: val },
component: { ...acc.component, excludeSpecPattern: val },
}
case 'supportFile':
// If the supportFile is set, but is the same value as the default one; where
// we migrate it, we do not want to put the legacy value in the migrated config.
// It can be .ts or .js
if (isDefaultSupportFile(val)) {
return acc
}
return {
...acc,
e2e: { ...acc.e2e, supportFile: val },
}
case 'baseUrl':
return {
...acc,
e2e: { ...acc.e2e, [key]: val },
}
case 'slowTestThreshold':
return {
...acc,
component: { ...acc.component, [key]: val },
e2e: { ...acc.e2e, [key]: val },
}
default:
return { ...acc, global: { ...acc.global, [key]: val } }
}
}, { global: {}, e2e: {}, component: {} })
}
function propOrArrayProp<T> (val: T[]): T | T[] {
if (val[0] && val.length === 1) {
return val[0]
}
return val
}
export function getSpecPattern (cfg: LegacyCypressConfigJson, testingType: TestingType, shouldAddCustomE2ESpecPattern: boolean = false) {
let _specPattern = cfg[testingType]?.testFiles ?? cfg.testFiles ?? (testingType === 'e2e' && shouldAddCustomE2ESpecPattern ? '**/*.{js,jsx,ts,tsx}' : '**/*.cy.{js,jsx,ts,tsx}')
const specPattern = _.castArray(_specPattern)
const customComponentFolder = cfg.component?.componentFolder ?? cfg.componentFolder ?? null
if (testingType === 'component' && customComponentFolder) {
return propOrArrayProp(specPattern.map((pattern) => `${customComponentFolder}/${pattern}`))
}
if (testingType === 'e2e') {
const customIntegrationFolder = cfg.e2e?.integrationFolder ?? cfg.integrationFolder ?? null
if (customIntegrationFolder && customIntegrationFolder !== legacyIntegrationFolder) {
return propOrArrayProp(specPattern.map((pattern) => `${customIntegrationFolder}/${pattern}`))
}
return propOrArrayProp(specPattern.map((pattern) => `cypress/e2e/${pattern}`))
}
return propOrArrayProp(specPattern)
}
function formatWithBundledBabel (config: string) {
const ast = parse(config)
// @ts-ignore - transitive babel types have a minor conflict - readonly vs non readonly.
let { code } = generate(ast, {}, config)
// By default babel generates imports like this:
// const {
// defineConfig
// } = require('cypress');
// So we replace them with a one-liner, since we know this will never
// be more than one import.
//
// Babel also adds empty lines, for example:
//
// export default defineConfig({
// component: {
// },
// <===== empty line
// e2e: {
//
// }
// })
// Which we don't want, so we change those to single carriage returns.
const replacers = [
{
from: dedent`
const {
defineConfig
} = require('cypress');
`,
to: dedent`
const { defineConfig } = require('cypress');
`,
},
{
from: dedent`
import {
defineConfig
} from 'cypress';
`,
to: dedent`
import { defineConfig } from 'cypress';
`,
},
{
from: `,\n\n`,
to: `,\n`,
},
]
for (const rep of replacers) {
if (code.includes(rep.from)) {
code = code.replaceAll(rep.from, rep.to)
}
}
return code
}
export function formatConfig (config: string): string {
try {
const prettier = require('prettier') as typeof import('prettier')
return prettier.format(config, {
semi: false,
singleQuote: true,
endOfLine: 'lf',
parser: 'babel',
})
} catch (e) {
// If they do not have prettier
// We do a basic format using babel, which we
// bundle as part of the binary.
// We don't ship a fully fledged formatter like
// prettier, since it's massively bloats the bundle.
return formatWithBundledBabel(config)
}
}

View File

@@ -1,62 +0,0 @@
import dedent from 'dedent'
import { MigrationTransformOptions, defaultMigrationTransformOptions } from './autoRename'
export type FilePart = {
text: string
group?: 'folder' | 'preExtension' | 'supportFileName' | 'fileName'
highlight: boolean
}
export function formatMigrationFile (file: string, regexp: RegExp, options: MigrationTransformOptions = defaultMigrationTransformOptions): FilePart[] {
const match = regexp.exec(file)
if (!match?.groups) {
throw new Error(dedent`
Expected groups main,ext or file in ${file} using ${regexp} when matching ${file}
Perhaps this isn't a spec file, or it is an unexpected format?`)
}
const { folder, fileName, preExtension, extension, supportFileName } = match.groups
if (supportFileName && extension) {
return [{
text: `${file.slice(0, match.index)}cypress/support/`, // user/cypress/support/index.js -> user/cypress/support/
highlight: false,
}, {
text: supportFileName, // user/cypress/support/index.js -> index
highlight: true,
group: 'supportFileName',
},
{
text: extension, // user/cypress/support/index.js -> .js
highlight: false,
}]
}
return [{
text: file.slice(0, match.index), // user/cypress/integration/file.spec.tsx -> user/
highlight: false,
},
{
text: folder ? 'cypress/' : '', // empty when using a custom integration folder or in component
highlight: false,
},
{
text: folder || '', // user/cypress/integration/file.spec.tsx -> integration
highlight: true,
group: 'folder',
},
{
text: (folder ? '/' : '') + fileName, // user/cypress/integration/file.spec.tsx -> /file
highlight: false,
group: 'fileName',
},
{
text: preExtension || '', // user/cypress/integration/file.spec.tsx -> .spec.
highlight: options.shouldMigratePreExtension,
group: 'preExtension',
}, {
text: extension || '', // user/cypress/integration/file.spec.tsx -> tsx
highlight: false,
}].filter((f) => f.text.length) as FilePart[]
}

View File

@@ -1,11 +0,0 @@
/* eslint-disable padding-line-between-statements */
// created by autobarrel, do not modify directly
export * from './autoRename'
export * from './codegen'
export * from './format'
export * from './legacyOptions'
export * from './parserUtils'
export * from './regexps'
export * from './shouldShowSteps'
export * from './utils'

View File

@@ -1,296 +0,0 @@
interface ResolvedConfigOption {
name: string
defaultValue?: any
isFolder?: boolean
isExperimental?: boolean
/**
* Can be mutated with Cypress.config() or test-specific configuration overrides
*/
canUpdateDuringTestTime?: boolean
}
interface RuntimeConfigOption {
name: string
defaultValue: any
isInternal?: boolean
/**
* Can be mutated with Cypress.config() or test-specific configuration overrides
*/
canUpdateDuringTestTime?: boolean
}
export const legacyIntegrationFolder = 'cypress/integration'
// NOTE:
// If you add/remove/change a config value, make sure to update the following
// - cli/types/index.d.ts (including allowed config options on TestOptions)
// - cypress.schema.json
//
// Add options in alphabetical order for better readability
// TODO - add boolean attribute to indicate read-only / static vs mutable options
// that can be updated during test executions
const resolvedOptions: Array<ResolvedConfigOption> = [
{
name: 'animationDistanceThreshold',
defaultValue: 5,
canUpdateDuringTestTime: true,
}, {
name: 'baseUrl',
defaultValue: null,
canUpdateDuringTestTime: true,
}, {
name: 'blockHosts',
defaultValue: null,
canUpdateDuringTestTime: true,
}, {
name: 'chromeWebSecurity',
defaultValue: true,
canUpdateDuringTestTime: false,
}, {
name: 'clientCertificates',
defaultValue: [],
canUpdateDuringTestTime: false,
}, {
name: 'component',
// runner-ct overrides
defaultValue: {},
canUpdateDuringTestTime: false,
}, {
name: 'componentFolder',
defaultValue: 'cypress/component',
isFolder: true,
canUpdateDuringTestTime: false,
}, {
name: 'defaultCommandTimeout',
defaultValue: 4000,
canUpdateDuringTestTime: true,
}, {
name: 'downloadsFolder',
defaultValue: 'cypress/downloads',
isFolder: true,
canUpdateDuringTestTime: false,
}, {
name: 'e2e',
// e2e runner overrides
defaultValue: {},
canUpdateDuringTestTime: false,
}, {
name: 'env',
defaultValue: {},
canUpdateDuringTestTime: true,
}, {
name: 'execTimeout',
defaultValue: 60000,
canUpdateDuringTestTime: true,
}, {
name: 'exit',
defaultValue: true,
canUpdateDuringTestTime: false,
}, {
name: 'experimentalInteractiveRunEvents',
defaultValue: false,
isExperimental: true,
canUpdateDuringTestTime: false,
}, {
name: 'experimentalSourceRewriting',
defaultValue: false,
isExperimental: true,
canUpdateDuringTestTime: false,
}, {
name: 'experimentalStudio',
defaultValue: false,
isExperimental: true,
canUpdateDuringTestTime: false,
}, {
name: 'fileServerFolder',
defaultValue: '',
isFolder: true,
canUpdateDuringTestTime: false,
}, {
name: 'fixturesFolder',
defaultValue: 'cypress/fixtures',
isFolder: true,
canUpdateDuringTestTime: false,
}, {
name: 'ignoreTestFiles',
defaultValue: '*.hot-update.js',
canUpdateDuringTestTime: true,
}, {
name: 'includeShadowDom',
defaultValue: false,
canUpdateDuringTestTime: true,
}, {
name: 'integrationFolder',
defaultValue: legacyIntegrationFolder,
isFolder: true,
canUpdateDuringTestTime: false,
}, {
name: 'keystrokeDelay',
defaultValue: 0,
canUpdateDuringTestTime: true,
}, {
name: 'modifyObstructiveCode',
defaultValue: true,
canUpdateDuringTestTime: false,
}, {
name: 'nodeVersion',
canUpdateDuringTestTime: false,
}, {
name: 'numTestsKeptInMemory',
defaultValue: 50,
canUpdateDuringTestTime: true,
}, {
name: 'pageLoadTimeout',
defaultValue: 60000,
canUpdateDuringTestTime: true,
}, {
name: 'pluginsFile',
defaultValue: 'cypress/plugins',
isFolder: true,
canUpdateDuringTestTime: false,
}, {
name: 'port',
defaultValue: null,
canUpdateDuringTestTime: true,
}, {
name: 'projectId',
defaultValue: null,
canUpdateDuringTestTime: true,
}, {
name: 'redirectionLimit',
defaultValue: 20,
canUpdateDuringTestTime: true,
}, {
name: 'reporter',
defaultValue: 'spec',
canUpdateDuringTestTime: true,
}, {
name: 'reporterOptions',
defaultValue: null,
canUpdateDuringTestTime: true,
}, {
name: 'requestTimeout',
defaultValue: 5000,
canUpdateDuringTestTime: true,
}, {
name: 'resolvedNodePath',
defaultValue: null,
canUpdateDuringTestTime: false,
}, {
name: 'resolvedNodeVersion',
defaultValue: null,
canUpdateDuringTestTime: false,
}, {
name: 'responseTimeout',
defaultValue: 30000,
canUpdateDuringTestTime: true,
}, {
name: 'retries',
defaultValue: {
runMode: 0,
openMode: 0,
},
canUpdateDuringTestTime: true,
}, {
name: 'screenshotOnRunFailure',
defaultValue: true,
canUpdateDuringTestTime: true,
}, {
name: 'screenshotsFolder',
defaultValue: 'cypress/screenshots',
isFolder: true,
canUpdateDuringTestTime: false,
}, {
name: 'slowTestThreshold',
defaultValue: (options: Record<string, any> = {}) => options.testingType === 'component' ? 250 : 10000,
canUpdateDuringTestTime: true,
}, {
name: 'scrollBehavior',
defaultValue: 'top',
canUpdateDuringTestTime: true,
}, {
name: 'supportFile',
defaultValue: 'cypress/support',
isFolder: true,
canUpdateDuringTestTime: false,
}, {
name: 'supportFolder',
defaultValue: false,
isFolder: true,
canUpdateDuringTestTime: false,
}, {
name: 'taskTimeout',
defaultValue: 60000,
canUpdateDuringTestTime: true,
}, {
name: 'testFiles',
defaultValue: '**/*.*',
canUpdateDuringTestTime: false,
}, {
name: 'trashAssetsBeforeRuns',
defaultValue: true,
canUpdateDuringTestTime: false,
}, {
name: 'userAgent',
defaultValue: null,
canUpdateDuringTestTime: false,
}, {
name: 'video',
defaultValue: false,
canUpdateDuringTestTime: false,
}, {
name: 'videoCompression',
defaultValue: 32,
canUpdateDuringTestTime: false,
}, {
name: 'videosFolder',
defaultValue: 'cypress/videos',
isFolder: true,
canUpdateDuringTestTime: false,
}, {
name: 'videoUploadOnPasses',
defaultValue: true,
canUpdateDuringTestTime: false,
}, {
name: 'viewportHeight',
defaultValue: 660,
canUpdateDuringTestTime: true,
}, {
name: 'viewportWidth',
defaultValue: 1000,
canUpdateDuringTestTime: true,
}, {
name: 'waitForAnimations',
defaultValue: true,
canUpdateDuringTestTime: true,
}, {
name: 'watchForFileChanges',
defaultValue: true,
canUpdateDuringTestTime: false,
},
]
const runtimeOptions: Array<RuntimeConfigOption> = [
{
name: 'browsers',
defaultValue: [],
canUpdateDuringTestTime: false,
}, {
name: 'hosts',
defaultValue: null,
canUpdateDuringTestTime: false,
}, {
name: 'isInteractive',
defaultValue: true,
canUpdateDuringTestTime: false,
}, {
name: 'modifyObstructiveCode',
defaultValue: true,
canUpdateDuringTestTime: false,
},
]
export const legacyOptions: Array<ResolvedConfigOption|RuntimeConfigOption> = [
...resolvedOptions,
...runtimeOptions,
]

View File

@@ -1,47 +0,0 @@
import { parse, ParserOptions } from '@babel/parser'
import { visit } from 'recast'
import type * as bt from '@babel/types'
const babelParserOptions: ParserOptions = {
sourceType: 'module',
strictMode: false,
tokens: true,
plugins: [
'decorators-legacy',
'doExpressions',
'objectRestSpread',
'classProperties',
'classPrivateProperties',
'classPrivateMethods',
'exportDefaultFrom',
'exportNamespaceFrom',
'asyncGenerators',
'functionBind',
'functionSent',
'dynamicImport',
'numericSeparator',
'optionalChaining',
'importMeta',
'bigInt',
'optionalCatchBinding',
'throwExpressions',
'nullishCoalescingOperator',
'typescript',
],
}
export function hasDefaultExport (src: string): boolean {
const ast = parse(src, babelParserOptions) as bt.File
let hasDefault = false
visit(ast, {
visitExportDefaultDeclaration () {
hasDefault = true
return false
},
})
return hasDefault
}

View File

@@ -1,36 +0,0 @@
/**
* This partial regular expression is used to extract
* the extension from a spec file name:
*
* matches
* - file.spec.tsx -> ext=".spec."
* - file_Spec.jsx -> ext="_Spec."
* - file-spec.js -> ext="-spec."
* - spec.jsx -> ext="."
*
* The final objective being to be able to replace "ext" with ".cy."
*/
const specExtRe = '(?:[._-]?(?:[s|S]pec|[T|t]est))?[.])(?<extension>(?:[j|t]s[x]?|coffee|cjsx)'
export const regexps = {
e2e: {
before: {
defaultFolderDefaultTestFiles: `cypress\/(?<folder>integration)\/(?<fileName>.+?)(?<preExtension>${specExtRe})$`,
defaultFolderCustomTestFiles: `cypress\/(?<folder>integration)\/(?<fileName>.+)$`,
customFolderDefaultTestFiles: `(?<fileName>.+?)(?<preExtension>${specExtRe})$`,
},
},
component: {
before: {
defaultFolderDefaultTestFiles: `(?<fileName>cypress\/component\/.+?)(?<preExtension>${specExtRe})`,
customFolderDefaultTestFiles: `(?<fileName>.+?)(?<preExtension>${specExtRe})`,
},
},
} as const
export const supportFileRegexps = {
e2e: {
beforeRegexp: 'cypress[\\\\/]support[\\\\/](?<supportFileName>index)(?<extension>\.(?:[j|t]sx?|coffee))',
afterRegexp: 'cypress[\\\\/]support[\\\\/](?<supportFileName>e2e)(?<extension>\.(?:[j|t]sx?|coffee))',
},
} as const

View File

@@ -1,194 +0,0 @@
import globby from 'globby'
import path from 'path'
import { MIGRATION_STEPS } from '@packages/types'
import { applyMigrationTransform, getSpecs, isDefaultSupportFile, legacyIntegrationFolder, tryGetDefaultLegacySupportFile } from '.'
import type { LegacyCypressConfigJson } from '..'
export const defaultTestFilesGlob = '**/*.{js,ts,jsx,tsx,coffee,cjsx}'
function getTestFilesGlobs (config: LegacyCypressConfigJson, type: 'component' | 'integration'): string[] {
// super awkward how we call it integration tests, but the key to override
// the config is `e2e`
const k = type === 'component' ? 'component' : 'e2e'
const glob = config[k]?.testFiles ?? config.testFiles
if (glob) {
return ([] as string[]).concat(glob)
}
return [defaultTestFilesGlob]
}
export function getIntegrationTestFilesGlobs (config: LegacyCypressConfigJson): string[] {
return getTestFilesGlobs(config, 'integration')
}
export function getComponentTestFilesGlobs (config: LegacyCypressConfigJson): string[] {
return getTestFilesGlobs(config, 'component')
}
export function isDefaultTestFiles (config: LegacyCypressConfigJson, type: 'component' | 'integration') {
const testFiles = type === 'component'
? getComponentTestFilesGlobs(config)
: getIntegrationTestFilesGlobs(config)
return testFiles.length === 1 && testFiles[0] === defaultTestFilesGlob
}
export function getPluginsFile (config: LegacyCypressConfigJson) {
if (config.e2e?.pluginsFile === false || config.pluginsFile === false) {
return false
}
return config.e2e?.pluginsFile ?? config.pluginsFile ?? 'cypress/plugins/index.js'
}
export function getIntegrationFolder (config: LegacyCypressConfigJson) {
return config.e2e?.integrationFolder ?? config.integrationFolder ?? legacyIntegrationFolder
}
export function getComponentFolder (config: LegacyCypressConfigJson): false | string {
if (config.component?.componentFolder === false || config.componentFolder === false) {
return false
}
return config.component?.componentFolder ?? config.componentFolder ?? 'cypress/component'
}
async function hasSpecFiles (projectRoot: string, dir: string, testFilesGlob: string[]): Promise<boolean> {
const f = await globby(testFilesGlob, { cwd: path.join(projectRoot, dir) })
return f.length > 0
}
export async function shouldShowAutoRenameStep (projectRoot: string, config: LegacyCypressConfigJson) {
const specsToAutoMigrate = await getSpecs(projectRoot, config)
const e2eMigrationOptions = {
// If the configFile has projectId, we do not want to change the preExtension
// so, we can keep the cloud history
shouldMigratePreExtension: !config.projectId && !config.e2e?.projectId,
}
const integrationCleaned = specsToAutoMigrate.integration.filter((spec) => {
const transformed = applyMigrationTransform(spec, e2eMigrationOptions)
return transformed.before.relative !== transformed.after.relative
})
const componentCleaned = specsToAutoMigrate.component.filter((spec) => {
const transformed = applyMigrationTransform(spec)
return transformed.before.relative !== transformed.after.relative
})
// if we have at least one spec to auto migrate in either Ct or E2E, we return true.
return integrationCleaned.length > 0 || componentCleaned.length > 0
}
async function anyComponentSpecsExist (projectRoot: string, config: LegacyCypressConfigJson) {
const componentFolder = getComponentFolder(config)
if (componentFolder === false) {
return false
}
const componentTestFiles = getComponentTestFilesGlobs(config)
return hasSpecFiles(projectRoot, componentFolder, componentTestFiles)
}
async function anyIntegrationSpecsExist (projectRoot: string, config: LegacyCypressConfigJson) {
const integrationFolder = getIntegrationFolder(config)
const integrationTestFiles = getIntegrationTestFilesGlobs(config)
return hasSpecFiles(projectRoot, integrationFolder, integrationTestFiles)
}
// we only show rename support file if they are using the default
// if they have anything set in their config, we will not try to rename it.
// Also, if there are no **no** integration specs, we are doing a CT only migration,
// in which case we don't migrate the supportFile - they'll make a new support/component.js
// when they set CT up.
export async function shouldShowRenameSupport (projectRoot: string, config: LegacyCypressConfigJson) {
if (!await anyIntegrationSpecsExist(projectRoot, config)) {
return false
}
let supportFile = config.e2e?.supportFile ?? config.supportFile
if (supportFile === undefined) {
const foundDefaultSupportFile = await tryGetDefaultLegacySupportFile(projectRoot)
if (foundDefaultSupportFile) {
supportFile = foundDefaultSupportFile
}
}
// if the support file is set to false, we don't show the rename step
// if the support file does not exist (value is undefined), we don't show the rename step
if (!supportFile) {
return false
}
// if the support file is custom, we don't show the rename step
// only if the support file matches the default do we show the rename step
return isDefaultSupportFile(supportFile)
}
// if they have component testing configured using the defaults, they will need to
// rename/move their specs.
async function shouldShowRenameManual (projectRoot: string, config: LegacyCypressConfigJson) {
const componentFolder = getComponentFolder(config)
const usingAllDefaults = componentFolder === 'cypress/component' && isDefaultTestFiles(config, 'component')
if (componentFolder === false || !usingAllDefaults) {
return false
}
return anyComponentSpecsExist(projectRoot, config)
}
// All projects must move from cypress.json to cypress.config.js!
export function shouldShowConfigFileStep (config: LegacyCypressConfigJson) {
return true
}
export type Step = typeof MIGRATION_STEPS[number]
export async function getStepsForMigration (
projectRoot: string,
config: LegacyCypressConfigJson,
configFileExists: boolean,
): Promise<Step[]> {
const steps: Step[] = []
for (const step of MIGRATION_STEPS) {
if (step === 'renameAuto' && await shouldShowAutoRenameStep(projectRoot, config)) {
steps.push(step)
}
if (step === 'renameManual' && await shouldShowRenameManual(projectRoot, config)) {
steps.push(step)
}
if (step === 'renameSupport' && await shouldShowRenameSupport(projectRoot, config)) {
steps.push(step)
}
if (step === 'configFile' && configFileExists) {
steps.push(step)
}
// if we are showing rename manual, this implies
// component testing is configured.
if (step === 'setupComponent' && await anyComponentSpecsExist(projectRoot, config)) {
steps.push(step)
}
}
return steps
}

View File

@@ -3,7 +3,7 @@ import type { TestingType, FoundSpec } from '@packages/types'
import Debug from 'debug'
import _ from 'lodash'
import path from 'path'
import { getPathFromSpecPattern, getLongestCommonPrefixFromPaths } from '../ProjectDataSource'
import { getPathFromSpecPattern, getLongestCommonPrefixFromPaths } from '../sources/ProjectDataSource'
export const isDefaultSupportFile = (supportFile: string) => {
if (_.isNil(supportFile) || !_.isBoolean(supportFile) && supportFile.match(/(^|\.+\/)cypress\/support($|\/index($|\.(ts|js|coffee)$))/)) {
@@ -19,7 +19,7 @@ export async function getDefaultSpecFileName (
{ currentProject, testingType, fileExtensionToUse, specPattern, specs = [], name }:
{ currentProject: string | null, testingType: TestingType | null, fileExtensionToUse: FileExtension, specPattern: string[], specs?: FoundSpec[], name?: string },
): Promise<string> {
const debug = Debug('cypress:data-context:sources:migration:utils')
const debug = Debug('cypress:data-context:util:files')
const defaultFilename = `${name ? name : testingType === 'e2e' ? 'spec' : 'ComponentName'}.cy.${fileExtensionToUse}`
const defaultPathname = path.join('cypress', testingType ?? 'e2e', defaultFilename)

View File

@@ -5,6 +5,7 @@ export * from './DocumentNodeBuilder'
export * from './autoBindDebug'
export * from './config-file-updater'
export * from './file'
export * from './files'
export * from './hasTypescript'
export * from './pluginHandlers'
export * from './testCounts'

View File

@@ -22,11 +22,8 @@ export const urqlCacheKeys: Partial<UrqlCacheKeys> = {
keys: {
DevState: (data) => data.__typename,
Wizard: (data) => data.__typename,
Migration: (data) => data.__typename,
CloudRunCommitInfo: () => null,
GitInfo: () => null,
MigrationFile: () => null,
MigrationFilePart: () => null,
CodeFrame: () => null,
ProjectPreferences: (data) => data.__typename,
VersionData: () => null,

View File

@@ -1,103 +0,0 @@
import chai, { expect } from 'chai'
import chaiAsPromised from 'chai-as-promised'
import { getConfigWithDefaults, getDiff } from '../../../src/actions/MigrationActions'
import fs from 'fs-extra'
import Fixtures from '@tooling/system-tests'
import { createTestDataContext, scaffoldMigrationProject } from '../helper'
import path from 'path'
chai.use(chaiAsPromised)
describe('MigrationActions', () => {
context('getConfigWithDefaults', () => {
it('returns a config with defaults without touching the original', () => {
const config = {
foo: 'bar',
}
expect(getConfigWithDefaults(config)).to.have.property('env')
expect(getConfigWithDefaults(config)).to.have.property('browsers')
expect(config).not.to.have.property('env')
expect(config).not.to.have.property('browsers')
})
})
context('getDiff', () => {
it('returns all the updated values', () => {
const oldConfig = {
foo: 'bar',
other: 'config',
removed: 'value',
updated: 'oldValue',
}
const newConfig = {
foo: 'hello',
other: 'config',
updated: 'newValue',
}
const diff = getDiff(oldConfig, newConfig)
expect(diff).to.have.property('foo', 'hello')
expect(diff).to.have.property('updated', 'newValue')
expect(diff).not.to.have.property('removed')
})
})
describe('#initialize', () => {
let currentProject: string
beforeEach(async () => {
Fixtures.clearFixtureNodeModules('migration')
currentProject = await scaffoldMigrationProject('migration')
})
// simulate having a specific version of cypress installed
// in a project's local node_modules
function mockLocallyInstalledCypress (projectRoot: string, version: string) {
const mockPkgJson = {
version,
main: 'index.js',
}
const mockCypressDir = path.join(projectRoot, 'node_modules', 'cypress')
fs.mkdirSync(mockCypressDir, { recursive: true })
fs.createFileSync(path.join(mockCypressDir, 'index.js'))
fs.writeJsonSync(path.join(mockCypressDir, 'package.json'), mockPkgJson)
}
it('errors when local cypress version is <10', async () => {
mockLocallyInstalledCypress(currentProject, '9.5.0')
const ctx = createTestDataContext()
ctx.update((coreData) => {
coreData.currentProject = currentProject
coreData.currentTestingType = 'e2e'
coreData.app.isGlobalMode = true
})
const currentVersion = (await ctx.versions.versionData()).current.version
return (
expect(ctx.actions.migration.initialize({})).to.eventually.be.rejectedWith(
`You are running Cypress version ${currentVersion} in global mode, but you are attempting to migrate a project where Cypress version 9.5.0 is installed`,
)
)
})
it('does not error when local cypress version is 10', () => {
mockLocallyInstalledCypress(currentProject, '10.0.0')
const ctx = createTestDataContext()
ctx.update((coreData) => {
coreData.currentProject = currentProject
coreData.currentTestingType = 'e2e'
})
return (
expect(ctx.actions.migration.initialize({})).to.eventually.not.be.rejected
)
})
})
})

View File

@@ -1,577 +0,0 @@
import {
getSpecs,
applyMigrationTransform,
MigrationSpec,
} from '../../../../src/sources/migration/autoRename'
import { expect } from 'chai'
import path from 'path'
import fs from 'fs-extra'
import { MigrationFile } from '../../../../src/sources'
import { scaffoldMigrationProject } from '../../helper'
describe('getSpecs', () => {
it('handles custom folders', async () => {
// CASE 1: E2E + CT, custom folders, default test files
// We want to rename specs, but keep current folders.
const cwd = await scaffoldMigrationProject('migration-e2e-component-default-test-files')
const json = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getSpecs(cwd, json)
expect(actual.integration).to.eql([
{
relative: 'cypress/custom-integration/foo.spec.ts',
usesDefaultFolder: false,
usesDefaultTestFiles: true,
testingType: 'e2e',
},
])
expect(actual.component).to.eql([
{
relative: 'cypress/custom-component/button.spec.js',
usesDefaultFolder: false,
usesDefaultTestFiles: true,
testingType: 'component',
},
])
})
it('handles default folder and custom testFiles', async () => {
// CASE 1: E2E + CT, custom folders, default test files
// We want to rename specs, but keep current folders.
const cwd = await scaffoldMigrationProject('migration')
const json = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getSpecs(cwd, json)
expect(actual.integration).to.eql([
{
'relative': 'cypress/integration/app_spec.js',
'testingType': 'e2e',
'usesDefaultFolder': true,
'usesDefaultTestFiles': false,
},
{
relative: 'cypress/integration/bar.spec.js',
usesDefaultFolder: true,
usesDefaultTestFiles: false,
testingType: 'e2e',
},
{
'relative': 'cypress/integration/blog-post-spec.ts',
'testingType': 'e2e',
'usesDefaultFolder': true,
'usesDefaultTestFiles': false,
},
{
'relative': 'cypress/integration/company.js',
'testingType': 'e2e',
'usesDefaultFolder': true,
'usesDefaultTestFiles': false,
},
{
'relative': 'cypress/integration/homeSpec.js',
'testingType': 'e2e',
'usesDefaultFolder': true,
'usesDefaultTestFiles': false,
},
{
'relative': 'cypress/integration/sign-up.js',
'testingType': 'e2e',
'usesDefaultFolder': true,
'usesDefaultTestFiles': false,
},
{
'relative': 'cypress/integration/spectacleBrowser.ts',
'testingType': 'e2e',
'usesDefaultFolder': true,
'usesDefaultTestFiles': false,
},
{
'relative': 'cypress/integration/someDir/someFile.js',
'testingType': 'e2e',
'usesDefaultFolder': true,
'usesDefaultTestFiles': false,
},
])
// expect(actual.component).to.eql([
// {
// relative: 'src/Radio.spec.js',
// usesDefaultFolder: false,
// usesDefaultTestFiles: false,
// testingType: 'component',
// },
// ])
})
it('handles default folders', async () => {
// CASE 1: E2E + CT, custom folders, default test files
// We want to rename specs, but keep current folders.
const cwd = await scaffoldMigrationProject('migration-e2e-component-default-everything')
const json = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getSpecs(cwd, json)
expect(actual.integration).to.eql([
{
relative: 'cypress/integration/foo.spec.ts',
usesDefaultFolder: true,
usesDefaultTestFiles: true,
testingType: 'e2e',
},
{
relative: 'cypress/integration/spec.ts',
usesDefaultFolder: true,
usesDefaultTestFiles: true,
testingType: 'e2e',
},
])
expect(actual.component).to.eql([
{
relative: 'cypress/component/button.spec.js',
usesDefaultFolder: true,
usesDefaultTestFiles: true,
testingType: 'component',
},
])
})
})
describe('applyMigrationTransform', () => {
describe('e2e spec', () => {
it('handles default folders and extensions', async () => {
const input: MigrationSpec = {
relative: 'cypress/integration/button.spec.js',
usesDefaultFolder: true,
usesDefaultTestFiles: true,
testingType: 'e2e',
}
const expected: MigrationFile = {
testingType: 'e2e',
before: {
relative: 'cypress/integration/button.spec.js',
parts: [
{
'highlight': false,
'text': 'cypress/',
},
{
group: 'folder',
'highlight': true,
'text': 'integration',
},
{
'highlight': false,
'text': '/button',
'group': 'fileName',
},
{
'highlight': true,
group: 'preExtension',
'text': '.spec.',
},
{
'highlight': false,
'text': 'js',
},
],
},
after: {
relative: 'cypress/e2e/button.cy.js',
parts: [
{
'highlight': false,
'text': 'cypress/',
},
{
'highlight': true,
group: 'folder',
'text': 'e2e',
},
{
'highlight': false,
'text': '/button',
'group': 'fileName',
},
{
'highlight': true,
group: 'preExtension',
'text': '.cy.',
},
{
'highlight': false,
'text': 'js',
},
],
},
}
const result = applyMigrationTransform(input)
expect(result.before).to.eql(expected.before)
expect(result.after).to.eql(expected.after)
})
it('handles custom folder, default extension', async () => {
const input: MigrationSpec = {
relative: 'custom-folder/button.spec.js',
usesDefaultFolder: false,
usesDefaultTestFiles: true,
testingType: 'e2e',
}
const expected: MigrationFile = {
testingType: 'e2e',
before: {
relative: 'custom-folder/button.spec.js',
parts: [
{
'highlight': false,
'text': 'custom-folder/button',
'group': 'fileName',
},
{
'highlight': true,
group: 'preExtension',
'text': '.spec.',
},
{
'highlight': false,
'text': 'js',
},
],
},
after: {
relative: 'custom-folder/button.cy.js',
parts: [
{
'highlight': false,
'text': 'custom-folder/button',
'group': 'fileName',
},
{
'highlight': true,
group: 'preExtension',
'text': '.cy.',
},
{
'highlight': false,
'text': 'js',
},
],
},
}
const result = applyMigrationTransform(input)
expect(result.before).to.eql(expected.before)
expect(result.after).to.eql(expected.after)
})
it('handles default folder, custom extension', async () => {
const input: MigrationSpec = {
relative: 'cypress/integration/foo.bar',
usesDefaultFolder: true,
usesDefaultTestFiles: false,
testingType: 'e2e',
}
const expected: MigrationFile = {
testingType: 'e2e',
before: {
relative: 'cypress/integration/foo.bar',
parts: [
{
'highlight': false,
'text': 'cypress/',
},
{
'highlight': true,
group: 'folder',
'text': 'integration',
},
{
'highlight': false,
'text': '/foo.bar',
'group': 'fileName',
},
],
},
after: {
relative: 'cypress/e2e/foo.bar',
parts: [
{
'highlight': false,
'text': 'cypress/',
},
{
'highlight': true,
group: 'folder',
'text': 'e2e',
},
{
'highlight': false,
'text': '/foo.bar',
'group': 'fileName',
},
],
},
}
const result = applyMigrationTransform(input)
expect(result.before).to.eql(expected.before)
expect(result.after).to.eql(expected.after)
})
it('handles a spec named spec', () => {
const input: MigrationSpec = {
relative: 'cypress/integration/spec.js',
usesDefaultFolder: true,
usesDefaultTestFiles: true,
testingType: 'e2e',
}
const expected: MigrationFile = {
testingType: 'e2e',
before: {
relative: 'cypress/integration/spec.js',
parts: [
{
'highlight': false,
'text': 'cypress/',
},
{
'highlight': true,
group: 'folder',
'text': 'integration',
},
{
'highlight': false,
'text': '/spec',
'group': 'fileName',
},
{
'highlight': true,
group: 'preExtension',
'text': '.',
},
{
'highlight': false,
'text': 'js',
},
],
},
after: {
relative: 'cypress/e2e/spec.cy.js',
parts: [
{
'highlight': false,
'text': 'cypress/',
},
{
'highlight': true,
group: 'folder',
'text': 'e2e',
},
{
'highlight': false,
'text': '/spec',
'group': 'fileName',
},
{
'highlight': true,
group: 'preExtension',
'text': '.cy.',
},
{
'highlight': false,
'text': 'js',
},
],
},
}
const result = applyMigrationTransform(input)
expect(result.before).to.eql(expected.before)
expect(result.after).to.eql(expected.after)
})
it('handles .test files', () => {
const result = applyMigrationTransform(
{
relative: 'cypress/tests/api-bankaccounts.test.js',
usesDefaultFolder: false,
usesDefaultTestFiles: true,
testingType: 'e2e',
},
)
const expected: MigrationFile = {
testingType: 'e2e',
before: {
relative: 'cypress/tests/api-bankaccounts.test.js',
parts: [
{
'highlight': false,
'text': 'cypress/tests/api-bankaccounts',
'group': 'fileName',
},
{
'highlight': true,
group: 'preExtension',
'text': '.test.',
},
{
'highlight': false,
'text': 'js',
},
],
},
after: {
relative: 'cypress/tests/api-bankaccounts.cy.js',
parts: [
{
'highlight': false,
'text': 'cypress/tests/api-bankaccounts',
'group': 'fileName',
},
{
'highlight': true,
group: 'preExtension',
'text': '.cy.',
},
{
'highlight': false,
'text': 'js',
},
],
},
}
expect(result.before).to.eql(expected.before)
expect(result.after).to.eql(expected.after)
})
})
describe('component spec', () => {
it('handles default folders and extensions', async () => {
const input: MigrationSpec = {
relative: 'cypress/component/button.spec.tsx',
usesDefaultFolder: true,
usesDefaultTestFiles: true,
testingType: 'component',
}
const expected: MigrationFile = {
testingType: 'component',
before: {
relative: 'cypress/component/button.spec.tsx',
parts: [
{
'highlight': false,
'text': 'cypress/component/button',
'group': 'fileName',
},
{
'highlight': true,
group: 'preExtension',
'text': '.spec.',
},
{
'highlight': false,
'text': 'tsx',
},
],
},
after: {
relative: 'cypress/component/button.cy.tsx',
parts: [
{
'highlight': false,
'text': 'cypress/component/button',
'group': 'fileName',
},
{
'highlight': true,
group: 'preExtension',
'text': '.cy.',
},
{
'highlight': false,
'text': 'tsx',
},
],
},
}
const result = applyMigrationTransform(input)
expect(result.before).to.eql(expected.before)
expect(result.after).to.eql(expected.after)
})
})
describe('component with custom folder, default testFiles', () => {
it('handles custom folders and default extensions', async () => {
const input: MigrationSpec = {
relative: 'cypress/custom-component/button.spec.js',
usesDefaultFolder: false,
usesDefaultTestFiles: true,
testingType: 'component',
}
const expected: MigrationFile = {
'testingType': 'component',
'before': {
'relative': 'cypress/custom-component/button.spec.js',
'parts': [
{
'text': 'cypress/custom-component/button',
'highlight': false,
'group': 'fileName',
},
{
'text': '.spec.',
'highlight': true,
'group': 'preExtension',
},
{
'text': 'js',
'highlight': false,
},
],
},
'after': {
'relative': 'cypress/custom-component/button.cy.js',
'parts': [
{
'text': 'cypress/custom-component/button',
'highlight': false,
'group': 'fileName',
},
{
'text': '.cy.',
'highlight': true,
'group': 'preExtension',
},
{
'text': 'js',
'highlight': false,
},
],
},
}
const actual = applyMigrationTransform(input)
expect(actual.before).to.eql(expected.before)
expect(actual.after).to.eql(expected.after)
})
})
})

View File

@@ -1,561 +0,0 @@
import snapshot from 'snap-shot-it'
import path from 'path'
import fs from 'fs-extra'
import {
createConfigString,
initComponentTestingMigration,
ComponentTestingMigrationStatus,
NonStandardMigrationError,
supportFilesForMigration,
reduceConfig,
renameSupportFilePath,
} from '../../../../src/sources/migration'
import { expect } from 'chai'
import { MigrationFile } from '../../../../src/sources'
import { scaffoldMigrationProject, getSystemTestProject } from '../../helper'
const projectRoot = getSystemTestProject('migration-e2e-defaults')
describe('cypress.config.js generation', () => {
it('generates correct config for component testing migration with custom testFiles glob', async () => {
const config = {
component: {
testFiles: '**/*.spec.cy.{js,ts,jsx,tsx}',
componentFolder: '.',
},
}
const generatedConfig = await createConfigString(config, {
hasE2ESpec: false,
hasComponentTesting: true,
hasPluginsFile: false,
projectRoot,
isUsingTypeScript: false,
shouldAddCustomE2ESpecPattern: false,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
it('generates correct config for component testing migration with custom testFiles array of glob', async () => {
const config = {
e2e: {
testFiles: ['**/*.spec.js', '**/*.test.js'],
},
}
const generatedConfig = await createConfigString(config, {
hasE2ESpec: true,
hasComponentTesting: false,
hasPluginsFile: false,
projectRoot,
isUsingTypeScript: false,
shouldAddCustomE2ESpecPattern: true,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
it('should create a string when passed only a global option', async () => {
const config: Partial<Cypress.Config> = {
viewportWidth: 300,
}
const generatedConfig = await createConfigString(config, {
hasE2ESpec: true,
hasComponentTesting: true,
hasPluginsFile: true,
projectRoot,
isUsingTypeScript: false,
shouldAddCustomE2ESpecPattern: false,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
it('should create a string when passed only a e2e options', async () => {
const config: Partial<Cypress.Config> = {
e2e: {
baseUrl: 'localhost:3000',
},
}
const generatedConfig = await createConfigString(config, {
hasE2ESpec: true,
hasComponentTesting: true,
hasPluginsFile: true,
projectRoot,
isUsingTypeScript: false,
shouldAddCustomE2ESpecPattern: false,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
it('should create a string when passed only a component options', async () => {
const generatedConfig = await createConfigString({
component: {
retries: 2,
},
}, {
hasE2ESpec: true,
hasComponentTesting: true,
hasPluginsFile: true,
projectRoot,
isUsingTypeScript: false,
shouldAddCustomE2ESpecPattern: false,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
it('should create only a component entry when no e2e specs are detected', async () => {
const generatedConfig = await createConfigString({}, {
hasE2ESpec: false,
hasComponentTesting: true,
hasPluginsFile: true,
projectRoot,
isUsingTypeScript: false,
shouldAddCustomE2ESpecPattern: false,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
it('should create only an e2e entry when no component specs are detected', async () => {
const generatedConfig = await createConfigString({}, {
hasE2ESpec: true,
hasComponentTesting: false,
hasPluginsFile: true,
projectRoot,
isUsingTypeScript: false,
shouldAddCustomE2ESpecPattern: false,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
it('should create a string for a config with global, component, and e2e options', async () => {
const config = {
viewportWidth: 300,
baseUrl: 'localhost:300',
slowTestThreshold: 500,
e2e: {
retries: 2,
},
component: {
retries: 1,
},
}
const generatedConfig = await createConfigString(config, {
hasE2ESpec: true,
hasComponentTesting: true,
hasPluginsFile: true,
projectRoot,
isUsingTypeScript: false,
shouldAddCustomE2ESpecPattern: false,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
it('should create a string when passed an empty object', async () => {
const config = {}
const generatedConfig = await createConfigString(config, {
hasE2ESpec: true,
hasComponentTesting: true,
hasPluginsFile: true,
projectRoot,
isUsingTypeScript: false,
shouldAddCustomE2ESpecPattern: false,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
it('should create a string when passed an empty object for an ECMA Script project', async () => {
const config = {}
const generatedConfig = await createConfigString(config, {
hasE2ESpec: true,
hasComponentTesting: true,
hasPluginsFile: true,
projectRoot,
isUsingTypeScript: false,
isProjectUsingESModules: true,
shouldAddCustomE2ESpecPattern: false,
})
snapshot(generatedConfig)
})
it('should exclude fields that are no longer valid', async () => {
const config = {
'$schema': 'http://someschema.com',
pluginsFile: './cypress/plugins/index.js',
componentFolder: 'path/to/component/folder',
}
const generatedConfig = await createConfigString(config, {
hasE2ESpec: true,
hasComponentTesting: true,
hasPluginsFile: true,
projectRoot,
isUsingTypeScript: false,
shouldAddCustomE2ESpecPattern: false,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
it('should handle export default in plugins file', async () => {
const projectRoot = getSystemTestProject('migration-e2e-export-default')
const config = fs.readJsonSync(path.join(projectRoot, 'cypress.json'))
const generatedConfig = await createConfigString(config, {
hasE2ESpec: true,
hasComponentTesting: true,
hasPluginsFile: true,
projectRoot,
isUsingTypeScript: true,
shouldAddCustomE2ESpecPattern: false,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
it('should maintain both root level and nested non-breaking options during migration', async () => {
const projectRoot = getSystemTestProject('migration-e2e-component-default-everything')
const config = await fs.readJson(path.join(projectRoot, 'cypress.json'))
const generatedConfig = await createConfigString(config, {
hasE2ESpec: true,
hasComponentTesting: true,
hasPluginsFile: true,
projectRoot,
isUsingTypeScript: true,
shouldAddCustomE2ESpecPattern: false,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
it('should add custom specPattern if project has projectId', async () => {
const projectRoot = getSystemTestProject('migration-e2e-defaults-with-projectId')
const config = await fs.readJson(path.join(projectRoot, 'cypress.json'))
const generatedConfig = await createConfigString(config, {
hasE2ESpec: true,
hasComponentTesting: true,
hasPluginsFile: true,
projectRoot,
isUsingTypeScript: true,
shouldAddCustomE2ESpecPattern: true,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
it('should not add custom specPattern if project has projectId and integrationFolder', async () => {
const projectRoot = getSystemTestProject('migration-e2e-defaults-with-projectId')
const config = await fs.readJson(path.join(projectRoot, 'cypress.json'))
config['integrationFolder'] = 'cypress/custom/e2e'
const generatedConfig = await createConfigString(config, {
hasE2ESpec: true,
hasComponentTesting: true,
hasPluginsFile: true,
projectRoot,
isUsingTypeScript: true,
shouldAddCustomE2ESpecPattern: true,
isProjectUsingESModules: false,
})
snapshot(generatedConfig)
})
})
describe('supportFilesForMigrationGuide', () => {
it('finds and represents correct supportFile migration guide', async () => {
const cwd = await scaffoldMigrationProject('migration')
const actual = await supportFilesForMigration(cwd)
const expected: MigrationFile = {
testingType: 'e2e',
before: {
relative: 'cypress/support/index.js',
parts: [
{
'text': 'cypress/support/',
'highlight': false,
},
{
'text': 'index',
'highlight': true,
group: 'supportFileName',
},
{
'text': '.js',
'highlight': false,
},
],
},
after: {
relative: 'cypress/support/e2e.js',
parts: [
{
'text': 'cypress/support/',
'highlight': false,
},
{
'text': 'e2e',
'highlight': true,
group: 'supportFileName',
},
{
'text': '.js',
'highlight': false,
},
],
},
}
// expect(actual.before).to.eql(expected.before)
expect(actual.after).to.eql(expected.after)
})
})
describe('renameSupportFilePath', () => {
it('renames and keeps correct js extension', () => {
const p = 'cypress/support/index.js'
const actual = renameSupportFilePath(p)
expect(actual).to.eq('cypress/support/e2e.js')
})
it('renames and keeps correct tsx extension', () => {
const p = 'cypress/support/index.tsx'
const actual = renameSupportFilePath(p)
expect(actual).to.eq('cypress/support/e2e.tsx')
})
it('errors on non standard path', () => {
const p = 'cypress/support/something-else.tsx'
expect(() => renameSupportFilePath(p)).to.throw(NonStandardMigrationError)
})
})
describe('initComponentTestingMigration', () => {
it('calls callback with status each time file is removed', async () => {
const cwd = await scaffoldMigrationProject('migration-component-testing-customized')
const delay = () => new Promise((res) => setTimeout(res, 250))
let updatedStatus: ComponentTestingMigrationStatus
const onFileMoved = (_status: ComponentTestingMigrationStatus) => {
updatedStatus = _status
}
const { status, watcher } = await initComponentTestingMigration(
cwd,
'src',
['**/*.{js,tsx}'],
onFileMoved,
)
expect(status.completed).to.be.false
expect(status.files).to.eql(new Map([
['src/button.spec.js', { moved: false,
relative: 'src/button.spec.js',
}],
['src/input-spec.tsx', {
moved: false,
relative: 'src/input-spec.tsx',
}],
]))
fs.moveSync(
path.join(cwd, 'src', 'input-spec.tsx'),
path.join(cwd, 'src', 'input.cy.tsx'),
)
// give watcher time to trigger
await delay()
expect(updatedStatus).to.eql({
files: new Map([
['src/button.spec.js', { moved: false, relative: 'src/button.spec.js' }],
['src/input-spec.tsx', { moved: true, relative: 'src/input-spec.tsx' }],
]),
completed: false,
})
fs.moveSync(
path.join(cwd, 'src', 'button.spec.js'),
path.join(cwd, 'src', 'button.cy.js'),
)
// give watcher time to trigger
await delay()
expect(updatedStatus).to.eql({
files: new Map([
['src/button.spec.js', { moved: true, relative: 'src/button.spec.js' }],
['src/input-spec.tsx', { moved: true, relative: 'src/input-spec.tsx' }],
]),
completed: true,
})
await watcher.close()
})
})
describe('reduceConfig', () => {
const options = {
hasComponentTesting: false,
hasE2ESpec: false,
hasPluginsFile: false,
projectRoot: '',
isUsingTypeScript: false,
isProjectUsingESModules: false,
shouldAddCustomE2ESpecPattern: false,
}
it('should move the testFiles field to e2e and component', () => {
const config = { testFiles: '**/**.cy.js' }
const newConfig = reduceConfig(config, options)
expect(newConfig.e2e.specPattern).to.eq('cypress/e2e/**/**.cy.js')
expect(newConfig.component.specPattern).to.eq('**/**.cy.js')
})
it('should update integration folder for e2e when is set to default', () => {
const config = { testFiles: '*.spec.js', integrationFolder: 'cypress/integration' }
const newConfig = reduceConfig(config, options)
expect(newConfig.e2e.specPattern).to.eq(`cypress/e2e/${config.testFiles}`)
})
it('should combine componentFolder and integrationFolder with testFiles field in component', () => {
const config = { testFiles: '**/**.cy.js', componentFolder: 'src', integrationFolder: 'cypress/src' }
const newConfig = reduceConfig(config, options)
expect(newConfig.component.specPattern).to.eq('src/**/**.cy.js')
expect(newConfig.e2e.specPattern).to.eq(`${config.integrationFolder}/${config.testFiles}`)
})
it('should combine nested componentFolder and integrationFolder with testFiles field in component', () => {
const config = {
testFiles: '**/**.cy.js',
component: {
componentFolder: 'src',
},
e2e: {
integrationFolder: 'cypress/src',
},
}
const newConfig = reduceConfig(config, options)
expect(newConfig.component.componentFolder).to.not.exist
expect(newConfig.component.specPattern).to.eq('src/**/**.cy.js')
expect(newConfig.e2e.specPattern).to.eq(`${config.e2e.integrationFolder}/${config.testFiles}`)
})
it('should add custom integrationFolder to default testFiles if testFiles is not present', () => {
const config = { integrationFolder: 'cypress/custom-integration' }
const newConfig = reduceConfig(config, options)
expect(newConfig.e2e.specPattern).to.eq(`${config.integrationFolder}/**/*.cy.{js,jsx,ts,tsx}`)
})
it('should add custom integrationFolder to default testFiles if testFiles is not present and shouldAddCustomE2ESpecPattern is true', () => {
const config = { integrationFolder: 'cypress/custom-integration' }
const newConfig = reduceConfig(config, { ...options, shouldAddCustomE2ESpecPattern: true })
expect(newConfig.e2e.specPattern).to.eq(`${config.integrationFolder}/**/*.{js,jsx,ts,tsx}`)
})
it('should combine testFiles with highest specificity', () => {
const config = {
testFiles: '**/**.cy.js',
componentFolder: 'lower/specificity',
integrationFolder: 'lower/specificity',
component: {
componentFolder: 'higher/specificity',
},
e2e: {
integrationFolder: 'higher/specificity',
},
}
const newConfig = reduceConfig(config, options)
expect(newConfig.component.specPattern).to.eq(`higher/specificity/**/**.cy.js`)
expect(newConfig.e2e.specPattern).to.eq(`${config.e2e.integrationFolder}/${config.testFiles}`)
})
it('should exclude integrationFolder and componentFolder', () => {
const config = {
componentFolder: 'src',
integrationFolder: 'cypress/integration',
}
const newConfig = reduceConfig(config, options)
// @ts-ignore field not on ConfigOptions type
expect(newConfig.global.componentFolder).to.not.exist
// @ts-ignore field not on ConfigOptions type
expect(newConfig.global.integrationFolder).to.not.exist
})
it('should rename ignoreTestFiles to excludeSpecPattern', () => {
const config = { ignoreTestFiles: 'path/to/**/*.js' }
const newConfig = reduceConfig(config, options)
expect(newConfig.e2e.excludeSpecPattern).to.eq(config.ignoreTestFiles)
expect(newConfig.component.excludeSpecPattern).to.eq(config.ignoreTestFiles)
})
it('should nest supportFile under component and e2e', () => {
const config = { supportFile: 'cypress/support/mySupportFile.js' }
const newConfig = reduceConfig(config, options)
expect(newConfig.e2e.supportFile).to.eq(config.supportFile)
})
it('should not add supportFile if it is the default one', () => {
expect(reduceConfig({ supportFile: null }, options).e2e.supportFile).to.not.exist
expect(reduceConfig({ supportFile: undefined }, options).e2e.supportFile).to.not.exist
expect(reduceConfig({ supportFile: 'cypress/support' }, options).e2e.supportFile).to.not.exist
expect(reduceConfig({ supportFile: 'cypress/support/index' }, options).e2e.supportFile).to.not.exist
expect(reduceConfig({ supportFile: 'cypress/support/index.js' }, options).e2e.supportFile).to.not.exist
expect(reduceConfig({ supportFile: './cypress/support/index.js' }, options).e2e.supportFile).to.not.exist
expect(reduceConfig({ supportFile: '../cypress/support/index.js' }, options).e2e.supportFile).to.not.exist
})
it('should exclude the pluginsFile', () => {
const config = { pluginsFile: 'cypress/plugins/index.js' }
const newConfig = reduceConfig(config, options)
// @ts-ignore field not on ConfigOptions type
expect(newConfig.global.pluginsFile).to.not.exist
})
})

View File

@@ -1,63 +0,0 @@
import { expect } from 'chai'
import {
formatMigrationFile,
} from '../../../../src/sources/migration/format'
import { regexps, supportFileRegexps } from '../../../../src/sources/migration/regexps'
describe('formatMigrationFile', () => {
describe('e2e - defaultFolderDefaultTestFiles', () => {
it('breaks pre-migration spec into parts', () => {
const spec = 'cypress/integration/app.spec.js'
const re = new RegExp(regexps.e2e.before.defaultFolderDefaultTestFiles)
const actual = formatMigrationFile(spec, re, { shouldMigratePreExtension: true })
expect(actual).to.eql([
{ text: 'cypress/', highlight: false },
{ text: 'integration', highlight: true, group: 'folder' },
{ text: '/app', highlight: false, group: 'fileName' },
{ text: '.spec.', highlight: true, group: 'preExtension' },
{ text: 'js', highlight: false },
])
})
it('do not highlight the preExtension when migratePreExtension is false', () => {
const spec = 'cypress/integration/app.spec.js'
const re = new RegExp(regexps.e2e.before.defaultFolderDefaultTestFiles)
const actual = formatMigrationFile(spec, re, { shouldMigratePreExtension: false })
expect(actual).to.eql([
{ text: 'cypress/', highlight: false },
{ text: 'integration', highlight: true, group: 'folder' },
{ text: '/app', highlight: false, group: 'fileName' },
{ text: '.spec.', highlight: false, group: 'preExtension' },
{ text: 'js', highlight: false },
])
})
})
;['js', 'ts'].forEach((ext) => {
it(`handles e2e support pre file migration [${ext}]`, () => {
const file = `cypress/support/index.${ext}`
const re = new RegExp(supportFileRegexps.e2e.beforeRegexp)
const actual = formatMigrationFile(file, re, { shouldMigratePreExtension: true })
expect(actual).to.eql([
{ text: 'cypress/support/', highlight: false },
{ text: 'index', highlight: true, group: 'supportFileName' },
{ text: `.${ext}`, highlight: false },
])
})
it(`handles e2e support post file migration [${ext}]`, () => {
const file = `cypress/support/e2e.${ext}`
const re = new RegExp(supportFileRegexps.e2e.afterRegexp)
const actual = formatMigrationFile(file, re, { shouldMigratePreExtension: true })
expect(actual).to.eql([
{ text: 'cypress/support/', highlight: false },
{ text: 'e2e', highlight: true, group: 'supportFileName' },
{ text: `.${ext}`, highlight: false },
])
})
})
})

View File

@@ -1,71 +0,0 @@
import { expect } from 'chai'
import fs from 'fs-extra'
import path from 'path'
import { processConfigViaLegacyPlugins } from '../../../../src/actions'
import { getSystemTestProject } from '../../helper'
describe('processConfigViaLegacyPlugins', () => {
it('executes legacy plugins and returns modified config', async () => {
const projectRoot = getSystemTestProject('migration-e2e-plugins-modify-config')
const result = await processConfigViaLegacyPlugins(projectRoot, {})
expect(result).to.eql({
'component': {
'testFiles': '**/*.spec.ts',
},
'e2e': {
'testFiles': '**/*.js',
},
'integrationFolder': 'tests/e2e',
'retries': {
'openMode': 0,
'runMode': 1,
},
'testFiles': '**/*.spec.js',
})
})
it('executes legacy plugins and returns without change if pluginsFile returns nothing', async () => {
const projectRoot = getSystemTestProject('migration-e2e-defaults')
const configFile = fs.readJsonSync(path.join(projectRoot, 'cypress.json'))
const result = await processConfigViaLegacyPlugins(projectRoot, configFile)
expect(result).to.eql(configFile)
})
it('works with cypress/plugins/index.ts and export default', async () => {
const projectRoot = getSystemTestProject('migration-e2e-export-default')
const result = await processConfigViaLegacyPlugins(projectRoot, {
retries: 10,
viewportWidth: 8888,
})
expect(result).to.eql({
retries: 10,
viewportWidth: 1111, // mutated in plugins file
})
})
it('catches error', (done) => {
const projectRoot = getSystemTestProject('migration-e2e-legacy-plugins-throws-error')
processConfigViaLegacyPlugins(projectRoot, {})
.catch((e) => {
expect(e.originalError.message).to.eq('Uh oh, there was an error!')
done()
})
})
it('handles pluginsFile: false', async () => {
const projectRoot = getSystemTestProject('launchpad')
const result = await processConfigViaLegacyPlugins(projectRoot, {
retries: 10,
viewportWidth: 8888,
})
expect(result).to.eql({
retries: 10,
viewportWidth: 8888,
})
})
})

View File

@@ -1,129 +0,0 @@
import { scaffoldMigrationProject } from '../../helper'
import path from 'path'
import fs from 'fs-extra'
import {
getStepsForMigration,
shouldShowAutoRenameStep,
Step,
} from '../../../../src/sources/migration/shouldShowSteps'
import { expect } from 'chai'
describe('shouldShowAutoRenameStep', () => {
it('true when testFiles is custom, but default integration folder', async () => {
const cwd = await scaffoldMigrationProject('migration')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await shouldShowAutoRenameStep(cwd, config)
expect(actual).to.be.true
})
it('true when testFiles is custom, but default integration folder', async () => {
const cwd = await scaffoldMigrationProject('migration-e2e-component-default-test-files')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await shouldShowAutoRenameStep(cwd, config)
expect(actual).to.be.true
})
it('false when integrationFolder and testFiles are custom', async () => {
const cwd = await scaffoldMigrationProject('migration-e2e-fully-custom')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await shouldShowAutoRenameStep(cwd, config)
expect(actual).to.be.false
})
it('true when integrationFolder custom and testFiles default', async () => {
const cwd = await scaffoldMigrationProject('migration-e2e-custom-integration')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await shouldShowAutoRenameStep(cwd, config)
expect(actual).to.be.true
})
it('true when integrationFolder default and testFiles custom', async () => {
const cwd = await scaffoldMigrationProject('migration-e2e-custom-test-files')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await shouldShowAutoRenameStep(cwd, config)
expect(actual).to.be.true
})
it('true when integrationFolder and testFiles default and spec exists', async () => {
const cwd = await scaffoldMigrationProject('migration-e2e-defaults')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await shouldShowAutoRenameStep(cwd, config)
expect(actual).to.be.true
})
it('false when integrationFolder and testFiles default by no spec to migrate', async () => {
const cwd = await scaffoldMigrationProject('migration-e2e-defaults-no-specs')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await shouldShowAutoRenameStep(cwd, config)
expect(actual).to.be.false
})
})
describe('getStepsForMigration', () => {
it('only returns configFile step for highly custom project', async () => {
const cwd = await scaffoldMigrationProject('migration-e2e-fully-custom')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getStepsForMigration(cwd, config, true)
const expected: Step[] = ['configFile']
expect(actual).to.eql(expected)
})
it('returns all e2e steps for project with all defaults', async () => {
const cwd = await scaffoldMigrationProject('migration-e2e-defaults')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getStepsForMigration(cwd, config, true)
const expected: Step[] = ['renameAuto', 'renameSupport', 'configFile']
expect(actual).to.eql(expected)
})
it('returns all e2e steps for project with all defaults + custom testFiles', async () => {
const cwd = await scaffoldMigrationProject('migration-e2e-custom-test-files')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getStepsForMigration(cwd, config, true)
const expected: Step[] = ['renameAuto', 'renameSupport', 'configFile']
expect(actual).to.eql(expected)
})
it('returns all steps for default integrationFolder, custom testFiles', async () => {
const cwd = await scaffoldMigrationProject('migration')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getStepsForMigration(cwd, config, true)
const expected: Step[] = ['renameAuto', 'renameSupport', 'configFile', 'setupComponent']
expect(actual).to.eql(expected)
})
it('returns all steps except supportFile for default CT project', async () => {
const cwd = await scaffoldMigrationProject('migration-component-testing-defaults')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getStepsForMigration(cwd, config, true)
const expected: Step[] = ['renameAuto', 'renameManual', 'configFile', 'setupComponent']
expect(actual).to.eql(expected)
})
it('returns component steps for component testing project (no e2e)', async () => {
const cwd = await scaffoldMigrationProject('migration-component-testing-customized')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getStepsForMigration(cwd, config, true)
const expected: Step[] = ['configFile', 'setupComponent']
expect(actual).to.eql(expected)
})
})

View File

@@ -777,17 +777,6 @@ describe('network stubbing', { retries: 15 }, function () {
})
})
it('errors on matchUrlAgainstPath usage', function (done) {
testFail((err) => {
expect(err.message).to.include('`matchUrlAgainstPath` was removed in Cypress 7.0.0')
done()
})
// @ts-ignore
cy.intercept({ matchUrlAgainstPath: true })
})
it('errors on unknown prop', function (done) {
testFail((err) => {
expect(err.message).to.include('An unknown \`RouteMatcher\` property was passed: `wrong`')
@@ -1023,7 +1012,7 @@ describe('network stubbing', { retries: 15 }, function () {
context('cors preflight', function () {
// a different domain from the page own domain
// NOTE: this domain is redirected back to the local host test server
// using "hosts" setting in the "cypress.json" file
// using "hosts" setting in the "cypress.config.js" file
let corsUrl = 'http://diff.foobar.com:3501/no-cors'
beforeEach(() => {

View File

@@ -158,11 +158,6 @@ function validateRouteMatcherOptions (routeMatcher: RouteMatcherOptions): { isVa
}
}
// @ts-ignore
if (routeMatcher.matchUrlAgainstPath) {
return err(`\`matchUrlAgainstPath\` was removed in Cypress 7.0.0 and should be removed from your tests. Your tests will run the same. For more information, visit https://on.cypress.io/migration-guide`)
}
for (const prop in routeMatcher) {
if (!allRouteMatcherFields.includes(prop)) {
return err(`An unknown \`RouteMatcher\` property was passed: \`${String(prop)}\`\n\nValid \`RouteMatcher\` properties are: ${allRouteMatcherFields.join(', ')}`)

View File

@@ -60,9 +60,5 @@
<span style="color:#e05561"><span style="color:#4f5666"> - <span style="color:#e05561"><span style="color:#4ec4ff">edge<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4f5666"> - <span style="color:#e05561"><span style="color:#4ec4ff">edge:beta<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4f5666"> - <span style="color:#e05561"><span style="color:#4ec4ff">edge:canary<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4f5666"> - <span style="color:#e05561"><span style="color:#4ec4ff">edge:dev<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Note: In Cypress version 4.0.0, Canary must be launched as <span style="color:#de73ff">chrome:canary<span style="color:#e05561">, not <span style="color:#de73ff">canary<span style="color:#e05561">.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">See https://on.cypress.io/migration-guide for more information on breaking changes in 4.0.0.<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
<span style="color:#e05561"><span style="color:#4f5666"> - <span style="color:#e05561"><span style="color:#4ec4ff">edge:dev<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -36,7 +36,7 @@
</head>
<body><pre><span style="color:#e05561">You passed the <span style="color:#de73ff">--record<span style="color:#e05561"> flag but this project has not been setup to record.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">This project is missing the <span style="color:#e5e510">projectId<span style="color:#e05561"> inside of: <span style="color:#4ec4ff">/path/to/cypress.json<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">This project is missing the <span style="color:#e5e510">projectId<span style="color:#e05561"> inside of: <span style="color:#4ec4ff">/path/to/cypress.config.js<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">We cannot uniquely identify this project without this id.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">

View File

@@ -36,7 +36,7 @@
</head>
<body><pre><span style="color:#e05561">We could not find a Cypress Cloud project with the projectId: <span style="color:#e5e510">project-id-123<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">This <span style="color:#de73ff">projectId<span style="color:#e05561"> came from your <span style="color:#4ec4ff">/path/to/cypress.json<span style="color:#e05561"> file or an environment variable.<span style="color:#e6e6e6">
<span style="color:#e05561">This <span style="color:#de73ff">projectId<span style="color:#e05561"> came from your <span style="color:#4ec4ff">/path/to/cypress.config.js<span style="color:#e05561"> file or an environment variable.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Please log into Cypress Cloud and find your project.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">

View File

@@ -1,48 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">componentFolder<span style="color:#e05561"> configuration option is now invalid when set on the config object in Cypress version 10.0.0.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">It is now renamed to <span style="color:#e5e510">specPattern<span style="color:#e05561"> and configured separately as a component testing property: <span style="color:#de73ff">component.specPattern<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">{<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> component: {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> specPattern: &#39;...&#39;,<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> },<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,49 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">To run component tests, Cypress needs you to configure the <span style="color:#e5e510">dev-server:start<span style="color:#e05561"> event.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Please update this file: <span style="color:#4ec4ff">/path/to/plugins/file.js<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">module.exports = (on, config) =&gt; {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> on(&#39;dev-server:start&#39;, () =&gt; {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> <span style="color:#4f5666">// start dev server here<span style="color:#4ec4ff"><span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> return startDevServer(...)<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> }<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/component-testing<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -34,9 +34,9 @@
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">specPattern<span style="color:#e05561"> configuration option is now invalid when set from the root of the config object in Cypress version 10.0.0.<span style="color:#e6e6e6">
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">specPattern<span style="color:#e05561"> configuration option is invalid when set from the root of the config object.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">It is now configured separately as a testing type property: <span style="color:#de73ff">e2e.specPattern<span style="color:#e05561"> and <span style="color:#de73ff">component.specPattern<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Set it within a testing type property: <span style="color:#de73ff">e2e.specPattern<span style="color:#e05561"> and <span style="color:#de73ff">component.specPattern<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">{<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> e2e: {<span style="color:#e05561"><span style="color:#e6e6e6">
@@ -45,7 +45,5 @@
<span style="color:#e05561"><span style="color:#4ec4ff"> component: {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> specPattern: &#39;...&#39;,<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> },<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -34,15 +34,13 @@
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">indexHtmlFile<span style="color:#e05561"> configuration option is now invalid when set from the root of the config object in Cypress version 10.0.0.<span style="color:#e6e6e6">
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">indexHtmlFile<span style="color:#e05561"> configuration option is invalid when set from the root of the config object.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">It is now configured separately as a testing type property: <span style="color:#de73ff">component.indexHtmlFile<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Set it within a testing type property: <span style="color:#de73ff">component.indexHtmlFile<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">{<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> component: {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> indexHtmlFile: &#39;...&#39;,<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> }<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -34,15 +34,13 @@
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">baseUrl<span style="color:#e05561"> configuration option is now invalid when set from the root of the config object in Cypress version 10.0.0.<span style="color:#e6e6e6">
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">baseUrl<span style="color:#e05561"> configuration option is invalid when set from the root of the config object.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">It is now configured separately as a testing type property: <span style="color:#de73ff">e2e.baseUrl<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Set it within a testing type property: <span style="color:#de73ff">e2e.baseUrl<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">{<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> e2e: {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> baseUrl: &#39;...&#39;,<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> }<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -42,7 +42,5 @@
<span style="color:#e05561"><span style="color:#4ec4ff"> e2e: {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> baseUrl: &#39;...&#39;,<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> }<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -42,7 +42,5 @@
<span style="color:#e05561"><span style="color:#4ec4ff"> e2e: {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> indexHtmlFile: &#39;...&#39;,<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> }<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,45 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">There is a <span style="color:#e5e510">cypress.json<span style="color:#e05561"> file at the path: <span style="color:#4ec4ff">/path/to/projectRoot<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Cypress version 10.0.0 no longer supports <span style="color:#e5e510">cypress.json<span style="color:#e05561">.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Please run <span style="color:#4ec4ff">cypress open<span style="color:#e05561"> to launch the migration tool to migrate to <span style="color:#de73ff">cypress.config.{js,ts,mjs,cjs}<span style="color:#e05561">.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -36,5 +36,5 @@
</head>
<body><pre><span style="color:#e05561">Could not find a Cypress configuration file.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">We looked but did not find a <span style="color:#e5e510">cypress.json<span style="color:#e05561"> file in this folder: <span style="color:#4ec4ff">/path/to/project/root<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span>
<span style="color:#e05561">We looked but did not find a <span style="color:#e5e510">cypress.config.js<span style="color:#e05561"> file in this folder: <span style="color:#4ec4ff">/path/to/project/root<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -34,7 +34,7 @@
</style>
</head>
<body><pre><span style="color:#e05561">Your <span style="color:#e5e510">configFile<span style="color:#e05561"> at <span style="color:#4ec4ff">cypress.json<span style="color:#e05561"> set an invalid value:<span style="color:#e6e6e6">
<body><pre><span style="color:#e05561">Your <span style="color:#e5e510">configFile<span style="color:#e05561"> at <span style="color:#4ec4ff">cypress.config.js<span style="color:#e05561"> set an invalid value:<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Expected <span style="color:#e5e510">defaultCommandTimeout<span style="color:#e05561"> to be a number.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">

View File

@@ -34,7 +34,7 @@
</style>
</head>
<body><pre><span style="color:#e05561">Your <span style="color:#e5e510">configFile<span style="color:#e05561"> at <span style="color:#4ec4ff">cypress.json<span style="color:#e05561"> set an invalid value:<span style="color:#e6e6e6">
<body><pre><span style="color:#e05561">Your <span style="color:#e5e510">configFile<span style="color:#e05561"> at <span style="color:#4ec4ff">cypress.config.js<span style="color:#e05561"> set an invalid value:<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Expected <span style="color:#e5e510">defaultCommandTimeout<span style="color:#e05561"> to be a number.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">

View File

@@ -34,7 +34,7 @@
</style>
</head>
<body><pre><span style="color:#e05561">Your <span style="color:#e5e510">configFile<span style="color:#e05561"> at <span style="color:#4ec4ff">cypress.json<span style="color:#e05561"> set an invalid value:<span style="color:#e6e6e6">
<body><pre><span style="color:#e05561">Your <span style="color:#e5e510">configFile<span style="color:#e05561"> at <span style="color:#4ec4ff">cypress.config.js<span style="color:#e05561"> set an invalid value:<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Expected <span style="color:#e5e510">defaultCommandTimeout<span style="color:#e05561"> to be a number.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">

View File

@@ -34,7 +34,7 @@
</style>
</head>
<body><pre><span style="color:#e05561">Your <span style="color:#e5e510">configFile<span style="color:#e05561"> at <span style="color:#4ec4ff">cypress.json<span style="color:#e05561"> set an invalid value:<span style="color:#e6e6e6">
<body><pre><span style="color:#e05561">Your <span style="color:#e5e510">configFile<span style="color:#e05561"> at <span style="color:#4ec4ff">cypress.config.js<span style="color:#e05561"> set an invalid value:<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">The error occurred while validating the <span style="color:#de73ff">browsers<span style="color:#e05561"> list.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">

View File

@@ -34,7 +34,7 @@
</style>
</head>
<body><pre><span style="color:#e05561">Your <span style="color:#e5e510">configFile<span style="color:#e05561"> at <span style="color:#4ec4ff">cypress.json<span style="color:#e05561"> set an invalid value:<span style="color:#e6e6e6">
<body><pre><span style="color:#e05561">Your <span style="color:#e5e510">configFile<span style="color:#e05561"> at <span style="color:#4ec4ff">cypress.config.js<span style="color:#e05561"> set an invalid value:<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Expected <span style="color:#e5e510">defaultCommandTimeout<span style="color:#e05561"> to be a number.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">

View File

@@ -34,7 +34,7 @@
</style>
</head>
<body><pre><span style="color:#e05561">Your <span style="color:#e5e510">configFile<span style="color:#e05561"> as <span style="color:#4ec4ff">cypress.json<span style="color:#e05561"> set an invalid value:<span style="color:#e6e6e6">
<body><pre><span style="color:#e05561">Your <span style="color:#e5e510">configFile<span style="color:#e05561"> as <span style="color:#4ec4ff">cypress.config.js<span style="color:#e05561"> set an invalid value:<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e5e510">`something` was not right<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,46 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">experimentalComponentTesting<span style="color:#e05561"> configuration option was removed in Cypress version 7.0.0.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Please remove this flag from: <span style="color:#4ec4ff">/path/to/cypress.config.js<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Component Testing is now a supported testing type. You can run your component tests with:<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"> <span style="color:#4f5666">$<span style="color:#e05561"> <span style="color:#4ec4ff">cypress open --component<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,40 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">experimentalNetworkStubbing<span style="color:#e05561"> configuration option was removed in Cypress version 6.0.0.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">It is no longer necessary for using <span style="color:#de73ff">cy.intercept()<span style="color:#e05561">. You can safely remove this option from your config.<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,40 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">experimentalRunEvents<span style="color:#e05561"> configuration option was removed in Cypress version 6.7.0. It is no longer necessary when listening to run events in the plugins file.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">You can safely remove this option from your config.<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,42 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">experimentalGetCookiesSameSite<span style="color:#e05561"> configuration option was removed in Cypress version 5.0.0.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Returning the <span style="color:#de73ff">sameSite<span style="color:#e05561"> property is now the default behavior of the <span style="color:#de73ff">cy.cookie<span style="color:#e05561"> commands.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">You can safely remove this option from your config.<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,42 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">experimentalSessionSupport<span style="color:#e05561"> configuration option was removed in Cypress version 9.6.0.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">You can safely remove this option from your config.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/session<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,40 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">experimentalShadowDomSupport<span style="color:#e05561"> configuration option was removed in Cypress version 5.2.0. It is no longer necessary when utilizing the <span style="color:#de73ff">includeShadowDom<span style="color:#e05561"> option.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">You can safely remove this option from your config.<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,44 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">We&#39;re ending the experimental phase of Cypress Studio in Cypress version 10.0.0.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">If you don&#39;t think you can live without Studio or you&#39;d like to learn about how to work around its removal, please join the discussion here: http://on.cypress.io/studio-removal<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Your feedback will help us factor in product decisions that may see Studio return in a future release.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">You can safely remove the <span style="color:#e5e510">experimentalStudio<span style="color:#e05561"> configuration option from your config.<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,42 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">firefoxGcInterval<span style="color:#e05561"> configuration option was removed in Cypress version 8.0.0. It was introduced to work around a bug in Firefox 79 and below.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Since Cypress no longer supports Firefox 85 and below in Cypress Cypress version 8.0.0, this option was removed.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">You can safely remove this option from your config.<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,45 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">We&#39;ve detected that the incompatible plugin <span style="color:#e5e510">cypress-plugin-retries<span style="color:#e05561"> is installed at: <span style="color:#4ec4ff">./path/to/cypress-plugin-retries<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Test retries is now natively supported in Cypress version 5.0.0.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Remove the plugin from your dependencies to silence this warning.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/test-retries<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,48 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">integrationFolder<span style="color:#e05561"> configuration option is now invalid when set on the config object in Cypress version 10.0.0.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">It is now renamed to <span style="color:#e5e510">specPattern<span style="color:#e05561"> and configured separately as a end to end testing property: <span style="color:#de73ff">e2e.specPattern<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">{<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> e2e: {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> specPattern: &#39;...&#39;,<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> },<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,45 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">Your <span style="color:#e5e510">cypress/plugins/index.js<span style="color:#e05561"> file threw an error. <span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Please ensure your pluginsFile is valid and relaunch the migration tool to migrate to Cypress version 10.0.0.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#c062de"><span style="color:#e6e6e6">
<span style="color:#c062de">Error: fail whale<span style="color:#e6e6e6">
<span style="color:#c062de"> at makeErr (cypress/packages/errors/test/unit/visualSnapshotErrors_spec.ts)<span style="color:#e6e6e6">
<span style="color:#c062de"> at LEGACY_CONFIG_ERROR_DURING_MIGRATION (cypress/packages/errors/test/unit/visualSnapshotErrors_spec.ts)<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,43 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">There is both a <span style="color:#e5e510">cypress.json<span style="color:#e05561"> and a <span style="color:#e5e510">cypress.json<span style="color:#e05561"> file at the location below:<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">/path/to/projectRoot<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Cypress no longer supports cypress.json, please remove it from your project.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,45 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">You are attempting to use Cypress with an older config file: <span style="color:#e5e510">custom.json<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">When you upgraded to Cypress v10.0 the config file was updated and moved to a new location: <span style="color:#e5e510">custom.config.js<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">You may need to update any CLI scripts to ensure that they are referring the new version. This would typically look something like:<span style="color:#e6e6e6">
<span style="color:#e05561">&quot;<span style="color:#e5e510">cypress open --config-file=custom.config.js<span style="color:#e05561">&quot;<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,43 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">You are running Cypress 10+ in global mode and attempting to open or migrate a project where an install of <span style="color:#4ec4ff">cypress<span style="color:#e05561"> cannot be found.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Ensure that <span style="color:#4ec4ff">cypress@10<span style="color:#e05561"> or greater is installed in the project you are attempting to open or migrate.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,43 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">You are running Cypress version 10.0.0 in global mode, but you are attempting to migrate a project where Cypress version 9.6.0 is installed.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Ensure the project you are migrating has Cypress version Cypress version 10.0.0 installed.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -34,5 +34,5 @@
</style>
</head>
<body><pre><span style="color:#e05561">Can&#39;t find <span style="color:#e5e510">projectId<span style="color:#e05561"> in the config file: <span style="color:#4ec4ff">/path/to/project/cypress.json<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span>
<body><pre><span style="color:#e05561">Can&#39;t find <span style="color:#e5e510">projectId<span style="color:#e05561"> in the config file: <span style="color:#4ec4ff">/path/to/project/cypress.config.js<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,51 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">pluginsFile<span style="color:#e05561"> configuration option you have supplied has been replaced with <span style="color:#de73ff">setupNodeEvents<span style="color:#e05561">.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">This new option is not a one-to-one correlation and it must be configured separately as a testing type property: <span style="color:#de73ff">e2e.setupNodeEvents<span style="color:#e05561"> and <span style="color:#de73ff">component.setupNodeEvents<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">{<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> e2e: {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> setupNodeEvents()<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> },<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> component: {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> setupNodeEvents()<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> },<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,52 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">Your <span style="color:#de73ff">configFile<span style="color:#e05561"> is invalid: <span style="color:#4ec4ff">/path/to/project/cypress.config.js<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Binding to the <span style="color:#de73ff">on(&#39;dev-server:start&#39;)<span style="color:#e05561"> event is no longer necessary.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Please update your code to use the <span style="color:#e5e510">component.devServer()<span style="color:#e05561"> function.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">{<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> component: {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> devServer (cypressDevServerConfig, devServerConfig) {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> <span style="color:#4f5666">// start dev server here<span style="color:#4ec4ff"><span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> }<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">Learn more: https://on.cypress.io/dev-server<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -1,51 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">The <span style="color:#e5e510">testFiles<span style="color:#e05561"> configuration option is now invalid when set on the config object in Cypress version 10.0.0.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">It is now renamed to <span style="color:#e5e510">specPattern<span style="color:#e05561"> and configured separately as a testing type property: <span style="color:#de73ff">e2e.specPattern<span style="color:#e05561"> or <span style="color:#de73ff">component.specPattern<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">{<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> e2e: {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> specPattern: &#39;...&#39;,<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> },<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> component: {<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> specPattern: &#39;...&#39;,<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff"> },<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#4ec4ff">}<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>

View File

@@ -7,7 +7,7 @@ import path from 'path'
import stripAnsi from 'strip-ansi'
import type { BreakingErrResult, TestingType } from '@packages/types'
import { humanTime, logError, parseResolvedPattern, pluralize } from './errorUtils'
import { errPartial, errTemplate, fmt, theme, PartialErr } from './errTemplate'
import { errPartial, errTemplate, fmt, theme } from './errTemplate'
import { stackWithoutMessage } from './stackUtils'
import type { ClonedError, ConfigValidationFailureInfo, CypressError, ErrTemplateResult, ErrorLike } from './errorTypes'
import { normalizeNetworkErrorMessage } from './normalizeNetworkErrorMessage'
@@ -105,16 +105,6 @@ export const AllCypressErrors = {
${fmt.listItems(options)}`
},
BROWSER_NOT_FOUND_BY_NAME: (browser: string, foundBrowsersStr: string[]) => {
let canarySuffix: PartialErr | null = null
if (browser === 'canary') {
canarySuffix = errPartial`\
${fmt.off('\n\n')}
Note: In ${fmt.cypressVersion(`4.0.0`)}, Canary must be launched as ${fmt.highlightSecondary(`chrome:canary`)}, not ${fmt.highlightSecondary(`canary`)}.
See https://on.cypress.io/migration-guide for more information on breaking changes in 4.0.0.`
}
return errTemplate`\
Can't run because you've entered an invalid browser name.
@@ -126,7 +116,7 @@ export const AllCypressErrors = {
You can also use a custom browser: https://on.cypress.io/customize-browsers
Available browsers found on your system are:
${fmt.listItems(foundBrowsersStr)}${canarySuffix}`
${fmt.listItems(foundBrowsersStr)}`
},
BROWSER_NOT_FOUND_BY_PATH: (arg1: string, arg2: string) => {
return errTemplate`\
@@ -895,7 +885,7 @@ export const AllCypressErrors = {
Fix the error in your code and re-run your tests.`
},
// happens when there is an error in configuration file like "cypress.json"
// happens when there is an error in configuration file like "cypress.config.js"
// TODO: make this relative path, not absolute
CONFIG_VALIDATION_MSG_ERROR: (fileType: 'configFile' | null, fileName: string | null, validationMsg: string) => {
if (!fileType) {
@@ -1216,42 +1206,12 @@ export const AllCypressErrors = {
If you don't require screenshots or videos to be stored you can safely ignore this warning.`
},
EXPERIMENTAL_SAMESITE_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`experimentalGetCookiesSameSite`)} configuration option was removed in ${fmt.cypressVersion(`5.0.0`)}.
Returning the ${fmt.highlightSecondary(`sameSite`)} property is now the default behavior of the ${fmt.highlightSecondary(`cy.cookie`)} commands.
You can safely remove this option from your config.`
},
EXPERIMENTAL_JIT_COMPILE_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`experimentalJustInTimeCompile`)} configuration option was removed in ${fmt.cypressVersion(`14.0.0`)}.
A new ${fmt.highlightSecondary(`justInTimeCompile`)} configuration option is available and is now ${fmt.highlightSecondary(`true`)} by default.
You can safely remove this option from your config.`
},
// TODO: verify configFile is absolute path
// TODO: make this relative path, not absolute
EXPERIMENTAL_COMPONENT_TESTING_REMOVED: (arg1: {configFile: string}) => {
return errTemplate`\
The ${fmt.highlight('experimentalComponentTesting')} configuration option was removed in ${fmt.cypressVersion(`7.0.0`)}.
Please remove this flag from: ${fmt.path(arg1.configFile)}
Component Testing is now a supported testing type. You can run your component tests with:
${fmt.terminal(`cypress open --component`)}
https://on.cypress.io/migration-guide`
},
EXPERIMENTAL_SESSION_SUPPORT_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`experimentalSessionSupport`)} configuration option was removed in ${fmt.cypressVersion(`9.6.0`)}.
You can safely remove this option from your config.
https://on.cypress.io/session`
},
EXPERIMENTAL_SESSION_AND_ORIGIN_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`experimentalSessionAndOrigin`)} configuration option was removed in ${fmt.cypressVersion(`12.0.0`)}.
@@ -1261,34 +1221,6 @@ export const AllCypressErrors = {
https://on.cypress.io/session
https://on.cypress.io/origin`
},
EXPERIMENTAL_SHADOW_DOM_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`experimentalShadowDomSupport`)} configuration option was removed in ${fmt.cypressVersion(`5.2.0`)}. It is no longer necessary when utilizing the ${fmt.highlightSecondary(`includeShadowDom`)} option.
You can safely remove this option from your config.`
},
EXPERIMENTAL_NETWORK_STUBBING_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`experimentalNetworkStubbing`)} configuration option was removed in ${fmt.cypressVersion(`6.0.0`)}.
It is no longer necessary for using ${fmt.highlightSecondary(`cy.intercept()`)}. You can safely remove this option from your config.`
},
EXPERIMENTAL_RUN_EVENTS_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`experimentalRunEvents`)} configuration option was removed in ${fmt.cypressVersion(`6.7.0`)}. It is no longer necessary when listening to run events in the plugins file.
You can safely remove this option from your config.`
},
EXPERIMENTAL_STUDIO_REMOVED: () => {
return errTemplate`\
We're ending the experimental phase of Cypress Studio in ${fmt.cypressVersion(`10.0.0`)}.
If you don't think you can live without Studio or you'd like to learn about how to work around its removal, please join the discussion here: http://on.cypress.io/studio-removal
Your feedback will help us factor in product decisions that may see Studio return in a future release.
You can safely remove the ${fmt.highlight(`experimentalStudio`)} configuration option from your config.`
},
EXPERIMENTAL_SINGLE_TAB_RUN_MODE: () => {
return errTemplate`\
The ${fmt.highlight(`experimentalSingleTabRunMode`)} experiment is currently only supported for Component Testing.
@@ -1356,26 +1288,6 @@ export const AllCypressErrors = {
Read the documentation for the injectDocumentDomain configuration option: https://on.cypress.io/inject-document-domain-configuration
`
},
FIREFOX_GC_INTERVAL_REMOVED: () => {
return errTemplate`\
The ${fmt.highlight(`firefoxGcInterval`)} configuration option was removed in ${fmt.cypressVersion(`8.0.0`)}. It was introduced to work around a bug in Firefox 79 and below.
Since Cypress no longer supports Firefox 85 and below in Cypress ${fmt.cypressVersion(`8.0.0`)}, this option was removed.
You can safely remove this option from your config.`
},
// TODO: make this relative path, not absolute
INCOMPATIBLE_PLUGIN_RETRIES: (arg1: string) => {
return errTemplate`\
We've detected that the incompatible plugin ${fmt.highlight(`cypress-plugin-retries`)} is installed at: ${fmt.path(arg1)}
Test retries is now natively supported in ${fmt.cypressVersion(`5.0.0`)}.
Remove the plugin from your dependencies to silence this warning.
https://on.cypress.io/test-retries
`
},
INVALID_CONFIG_OPTION: (arg1: string[]) => {
const phrase = arg1.length > 1 ? 'options are' : 'option is'
@@ -1395,31 +1307,11 @@ export const AllCypressErrors = {
${fmt.stackTrace(arg2)}`
},
CONFIG_FILE_INVALID_DEV_START_EVENT: (pluginsFilePath: string) => {
const code = errPartial`
module.exports = (on, config) => {
on('dev-server:start', () => {
${fmt.comment('// start dev server here')}
return startDevServer(...)
}
}`
return errTemplate`\
To run component tests, Cypress needs you to configure the ${fmt.highlight(`dev-server:start`)} event.
Please update this file: ${fmt.path(pluginsFilePath)}
${fmt.code(code)}
https://on.cypress.io/component-testing`
},
UNSUPPORTED_BROWSER_VERSION: (errorMsg: string) => {
return errTemplate`${fmt.off(errorMsg)}`
},
// V10 Added:
MULTIPLE_SUPPORT_FILES_FOUND: (arg1: string, arg2: string[]) => {
return errTemplate`\
There were multiple support files found matching your ${fmt.highlightSecondary(`supportFile`)} pattern.
@@ -1433,80 +1325,6 @@ export const AllCypressErrors = {
Please remove or combine the support files into a single file.`
},
CONFIG_FILE_MIGRATION_NEEDED: (projectRoot: string) => {
return errTemplate`
There is a ${fmt.highlight(`cypress.json`)} file at the path: ${fmt.path(projectRoot)}
${fmt.cypressVersion('10.0.0')} no longer supports ${fmt.highlight(`cypress.json`)}.
Please run ${fmt.highlightTertiary('cypress open')} to launch the migration tool to migrate to ${fmt.highlightSecondary('cypress.config.{js,ts,mjs,cjs}')}.
https://on.cypress.io/migration-guide
`
},
LEGACY_CONFIG_ERROR_DURING_MIGRATION: (file: string, error: Error) => {
return errTemplate`
Your ${fmt.highlight(file)} file threw an error. ${fmt.stackTrace(error)}
Please ensure your pluginsFile is valid and relaunch the migration tool to migrate to ${fmt.cypressVersion('10.0.0')}.
`
},
LEGACY_CONFIG_FILE: (baseFileName: string, projectRoot: string, legacyConfigFile: string = 'cypress.json') => {
return errTemplate`
There is both a ${fmt.highlight(baseFileName)} and a ${fmt.highlight(legacyConfigFile)} file at the location below:
${fmt.path(projectRoot)}
Cypress no longer supports ${fmt.off(legacyConfigFile)}, please remove it from your project.
`
},
SETUP_NODE_EVENTS_DO_NOT_SUPPORT_DEV_SERVER: (configFilePath: string) => {
const code = errPartial`
{
component: {
devServer (cypressDevServerConfig, devServerConfig) {
${fmt.comment(`// start dev server here`)
}
}
}`
return errTemplate`\
Your ${fmt.highlightSecondary(`configFile`)} is invalid: ${fmt.path(configFilePath)}
Binding to the ${fmt.highlightSecondary(`on('dev-server:start')`)} event is no longer necessary.
Please update your code to use the ${fmt.highlight(`component.devServer()`)} function.
${fmt.code(code)}
Learn more: https://on.cypress.io/dev-server
`
},
PLUGINS_FILE_CONFIG_OPTION_REMOVED: (_errShape: BreakingErrResult) => {
const code = errPartial`
{
e2e: {
setupNodeEvents()
},
component: {
setupNodeEvents()
},
}`
return errTemplate`\
The ${fmt.highlight('pluginsFile')} configuration option you have supplied has been replaced with ${fmt.highlightSecondary('setupNodeEvents')}.
This new option is not a one-to-one correlation and it must be configured separately as a testing type property: ${fmt.highlightSecondary('e2e.setupNodeEvents')} and ${fmt.highlightSecondary('component.setupNodeEvents')}
${fmt.code(code)}
https://on.cypress.io/migration-guide`
},
VIDEO_UPLOAD_ON_PASSES_REMOVED: (_errShape: BreakingErrResult) => {
return errTemplate`\
The ${fmt.highlight(`videoUploadOnPasses`)} configuration option was removed in ${fmt.cypressVersion(`13.0.0`)}.
@@ -1528,13 +1346,11 @@ export const AllCypressErrors = {
}`
return errTemplate`\
The ${fmt.highlight(errShape.name)} configuration option is now invalid when set from the root of the config object in ${fmt.cypressVersion(`10.0.0`)}.
The ${fmt.highlight(errShape.name)} configuration option is invalid when set from the root of the config object.
It is now configured separately as a testing type property: ${fmt.highlightSecondary(`e2e.${errShape.name}`)} and ${fmt.highlightSecondary(`component.${errShape.name}`)}
Set it within a testing type property: ${fmt.highlightSecondary(`e2e.${errShape.name}`)} and ${fmt.highlightSecondary(`component.${errShape.name}`)}
${fmt.code(code)}
https://on.cypress.io/migration-guide`
${fmt.code(code)}`
},
CONFIG_FILE_INVALID_ROOT_CONFIG_E2E: (errShape: BreakingErrResult) => {
@@ -1546,13 +1362,11 @@ export const AllCypressErrors = {
}`
return errTemplate`\
The ${fmt.highlight(errShape.name)} configuration option is now invalid when set from the root of the config object in ${fmt.cypressVersion(`10.0.0`)}.
The ${fmt.highlight(errShape.name)} configuration option is invalid when set from the root of the config object.
It is now configured separately as a testing type property: ${fmt.highlightSecondary(`e2e.${errShape.name}`)}
Set it within a testing type property: ${fmt.highlightSecondary(`e2e.${errShape.name}`)}
${fmt.code(code)}
https://on.cypress.io/migration-guide`
${fmt.code(code)}`
},
CONFIG_FILE_INVALID_ROOT_CONFIG_COMPONENT: (errShape: BreakingErrResult) => {
@@ -1564,13 +1378,11 @@ export const AllCypressErrors = {
}`
return errTemplate`\
The ${fmt.highlight(errShape.name)} configuration option is now invalid when set from the root of the config object in ${fmt.cypressVersion(`10.0.0`)}.
The ${fmt.highlight(errShape.name)} configuration option is invalid when set from the root of the config object.
It is now configured separately as a testing type property: ${fmt.highlightSecondary(`component.${errShape.name}`)}
Set it within a testing type property: ${fmt.highlightSecondary(`component.${errShape.name}`)}
${fmt.code(code)}
https://on.cypress.io/migration-guide`
${fmt.code(code)}`
},
// TODO: add path to config file
@@ -1587,9 +1399,7 @@ export const AllCypressErrors = {
Please remove this option or add this as an e2e testing type property: ${fmt.highlightSecondary(`e2e.${errShape.name}`)}
${fmt.code(code)}
https://on.cypress.io/migration-guide`
${fmt.code(code)}`
},
CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_E2E: (errShape: BreakingErrResult) => {
@@ -1605,9 +1415,7 @@ export const AllCypressErrors = {
Please remove this option or add this as a component testing type property: ${fmt.highlightSecondary(`component.${errShape.name}`)}
${fmt.code(code)}
https://on.cypress.io/migration-guide`
${fmt.code(code)}`
},
CONFIG_FILE_DEV_SERVER_IS_NOT_VALID: (configFilePath: string, setupNodeEvents: any) => {
@@ -1681,120 +1489,6 @@ export const AllCypressErrors = {
`
},
MIGRATION_ALREADY_OCURRED: (configFile: string, legacyConfigFile: string) => {
return errTemplate`
You are attempting to use Cypress with an older config file: ${fmt.highlight(legacyConfigFile)}
When you upgraded to Cypress v10.0 the config file was updated and moved to a new location: ${fmt.highlight(configFile)}
You may need to update any CLI scripts to ensure that they are referring the new version. This would typically look something like:
"${fmt.highlight(`cypress open --config-file=${configFile}`)}"
https://on.cypress.io/migration-guide
`
},
TEST_FILES_RENAMED: (errShape: BreakingErrResult, err?: Error) => {
const stackTrace = err ? fmt.stackTrace(err) : null
const newName = errShape.newName || '<unknown>'
const testingTypedHelpMessage = errShape.testingType
? errPartial`${fmt.highlightSecondary(`${errShape.testingType}.${newName}`)}`
: errPartial`${fmt.highlightSecondary(`e2e.${newName}`)} or ${fmt.highlightSecondary(`component.${newName}`)}`
const code = errShape.testingType
? errPartial`
{
${fmt.off(errShape.testingType)}: {
specPattern: '...',
},
}`
: errPartial`
{
e2e: {
specPattern: '...',
},
component: {
specPattern: '...',
},
}`
return errTemplate`\
The ${fmt.highlight(errShape.name)} configuration option is now invalid when set on the config object in ${fmt.cypressVersion(`10.0.0`)}.
It is now renamed to ${fmt.highlight(newName)} and configured separately as a testing type property: ${testingTypedHelpMessage}
${fmt.code(code)}
https://on.cypress.io/migration-guide
${stackTrace}
`
},
COMPONENT_FOLDER_REMOVED: (errShape: BreakingErrResult, err?: Error) => {
const stackTrace = err ? fmt.stackTrace(err) : null
const code = errPartial`
{
component: {
specPattern: '...',
},
}`
return errTemplate`\
The ${fmt.highlight(errShape.name)} configuration option is now invalid when set on the config object in ${fmt.cypressVersion(`10.0.0`)}.
It is now renamed to ${fmt.highlight('specPattern')} and configured separately as a component testing property: ${fmt.highlightSecondary('component.specPattern')}
${fmt.code(code)}
https://on.cypress.io/migration-guide
${stackTrace}
`
},
INTEGRATION_FOLDER_REMOVED: (errShape: BreakingErrResult, err?: Error) => {
const stackTrace = err ? fmt.stackTrace(err) : null
const code = errPartial`
{
e2e: {
specPattern: '...',
},
}`
return errTemplate`\
The ${fmt.highlight(errShape.name)} configuration option is now invalid when set on the config object in ${fmt.cypressVersion(`10.0.0`)}.
It is now renamed to ${fmt.highlight('specPattern')} and configured separately as a end to end testing property: ${fmt.highlightSecondary('e2e.specPattern')}
${fmt.code(code)}
https://on.cypress.io/migration-guide
${stackTrace}
`
},
MIGRATION_MISMATCHED_CYPRESS_VERSIONS: (version: string, currentVersion: string) => {
return errTemplate`
You are running ${fmt.cypressVersion(currentVersion)} in global mode, but you are attempting to migrate a project where ${fmt.cypressVersion(version)} is installed.
Ensure the project you are migrating has Cypress version ${fmt.cypressVersion(currentVersion)} installed.
https://on.cypress.io/migration-guide
`
},
MIGRATION_CYPRESS_NOT_FOUND: () => {
return errTemplate`
You are running Cypress 10+ in global mode and attempting to open or migrate a project where an install of ${fmt.code('cypress')} cannot be found.
Ensure that ${fmt.code('cypress@10')} or greater is installed in the project you are attempting to open or migrate.
https://on.cypress.io/migration-guide
`
},
DEV_SERVER_CONFIG_FILE_NOT_FOUND: (devServer: 'vite' | 'webpack', root: string, searchedFor: string[]) => {
const devServerConfigFile = `${devServer}Config`

View File

@@ -49,13 +49,13 @@ describe('lib/errors', () => {
})
it('logs err.message', () => {
const err = errors.getError('NO_PROJECT_ID', '/path/to/project/cypress.json')
const err = errors.getError('NO_PROJECT_ID', '/path/to/project/cypress.config.js')
const ret = errors.log(err)
expect(ret).to.be.undefined
expect(console.log).to.be.calledWithMatch('/path/to/project/cypress.json')
expect(console.log).to.be.calledWithMatch('/path/to/project/cypress.config.js')
})
it('logs err.details', () => {

View File

@@ -304,13 +304,6 @@ describe('visual error templates', () => {
// testVisualErrors('CANNOT_RECORD_NO_PROJECT_ID', {
testVisualErrors(errorType, {
LEGACY_CONFIG_ERROR_DURING_MIGRATION: () => {
const err = makeErr()
return {
default: ['cypress/plugins/index.js', err],
}
},
CANNOT_TRASH_ASSETS: () => {
const err = makeErr()
@@ -598,7 +591,7 @@ describe('visual error templates', () => {
},
CANNOT_RECORD_NO_PROJECT_ID: () => {
return {
default: ['/path/to/cypress.json'],
default: ['/path/to/cypress.config.js'],
}
},
PROJECT_ID_AND_KEY_BUT_MISSING_RECORD_OPTION: () => {
@@ -739,12 +732,12 @@ describe('visual error templates', () => {
},
CLOUD_PROJECT_NOT_FOUND: () => {
return {
default: ['project-id-123', '/path/to/cypress.json'],
default: ['project-id-123', '/path/to/cypress.config.js'],
}
},
NO_PROJECT_ID: () => {
return {
default: ['/path/to/project/cypress.json'],
default: ['/path/to/project/cypress.config.js'],
}
},
NO_PROJECT_FOUND_AT_PROJECT_ROOT: () => {
@@ -864,28 +857,28 @@ describe('visual error templates', () => {
},
CONFIG_VALIDATION_ERROR: () => {
return {
default: ['configFile', 'cypress.json', {
default: ['configFile', 'cypress.config.js', {
key: 'defaultCommandTimeout',
type: 'a number',
value: false,
}],
list: ['configFile', 'cypress.json', {
list: ['configFile', 'cypress.config.js', {
key: 'displayName',
type: 'a non-empty string',
value: { name: 'chrome', version: '1.2.3', displayName: null },
list: 'browsers',
}],
invalidString: ['configFile', 'cypress.json', {
invalidString: ['configFile', 'cypress.config.js', {
key: 'defaultCommandTimeout',
type: 'a number',
value: '1234',
}],
invalidObject: ['configFile', 'cypress.json', {
invalidObject: ['configFile', 'cypress.config.js', {
key: 'defaultCommandTimeout',
type: 'a number',
value: { foo: 'bar' },
}],
invalidArray: ['configFile', 'cypress.json', {
invalidArray: ['configFile', 'cypress.config.js', {
key: 'defaultCommandTimeout',
type: 'a number',
value: [1, 2, 3],
@@ -899,7 +892,7 @@ describe('visual error templates', () => {
},
CONFIG_VALIDATION_MSG_ERROR: () => {
return {
default: ['configFile', 'cypress.json', '`something` was not right'],
default: ['configFile', 'cypress.config.js', '`something` was not right'],
noFileType: [null, null, '`something` was not right'],
}
},
@@ -958,7 +951,7 @@ describe('visual error templates', () => {
},
CONFIG_FILE_NOT_FOUND: () => {
return {
default: ['cypress.json', '/path/to/project/root'],
default: ['cypress.config.js', '/path/to/project/root'],
}
},
INVOKED_BINARY_OUTSIDE_NPM_MODULE: () => {
@@ -1113,81 +1106,16 @@ describe('visual error templates', () => {
default: ['/path/to/folder'],
}
},
EXPERIMENTAL_SAMESITE_REMOVED: () => {
return {
default: [],
}
},
EXPERIMENTAL_JIT_COMPILE_REMOVED: () => {
return {
default: [],
}
},
EXPERIMENTAL_COMPONENT_TESTING_REMOVED: () => {
return {
default: [{ configFile: '/path/to/cypress.config.js' }],
}
},
EXPERIMENTAL_SESSION_SUPPORT_REMOVED: () => {
return {
default: [],
}
},
EXPERIMENTAL_SESSION_AND_ORIGIN_REMOVED: () => {
return {
default: [],
}
},
EXPERIMENTAL_SHADOW_DOM_REMOVED: () => {
return {
default: [],
}
},
EXPERIMENTAL_NETWORK_STUBBING_REMOVED: () => {
return {
default: [],
}
},
EXPERIMENTAL_RUN_EVENTS_REMOVED: () => {
return {
default: [],
}
},
EXPERIMENTAL_STUDIO_REMOVED: () => {
return {
default: [],
}
},
FIREFOX_GC_INTERVAL_REMOVED: () => {
return {
default: [],
}
},
INCOMPATIBLE_PLUGIN_RETRIES: () => {
return {
default: ['./path/to/cypress-plugin-retries'],
}
},
CONFIG_FILE_MIGRATION_NEEDED: () => {
return {
default: ['/path/to/projectRoot'],
}
},
LEGACY_CONFIG_FILE: () => {
return {
default: ['cypress.json', '/path/to/projectRoot'],
}
},
SETUP_NODE_EVENTS_DO_NOT_SUPPORT_DEV_SERVER: () => {
return {
default: ['/path/to/project/cypress.config.js'],
}
},
CONFIG_FILE_INVALID_DEV_START_EVENT: () => {
return {
default: ['/path/to/plugins/file.js'],
}
},
CONFIG_FILE_DEV_SERVER_INVALID_RETURN: () => {
return {
default: [{}],
@@ -1214,11 +1142,6 @@ describe('visual error templates', () => {
default: ['spec.{ts,js}', ['support.ts', 'support.js']],
}
},
PLUGINS_FILE_CONFIG_OPTION_REMOVED: () => {
return {
default: [{ name: 'pluginsFile', configFile: '/path/to/cypress.config.js.ts' }],
}
},
VIDEO_UPLOAD_ON_PASSES_REMOVED: () => {
return {
default: [{ name: 'videoUploadOnPasses', configFile: '/path/to/cypress.config.js.ts' }],
@@ -1274,36 +1197,6 @@ describe('visual error templates', () => {
default: [makeErr()],
}
},
MIGRATION_ALREADY_OCURRED: () => {
return {
default: ['custom.config.js', 'custom.json'],
}
},
TEST_FILES_RENAMED: () => {
return {
default: [{ name: 'testFiles', newName: 'specPattern', configFile: '/path/to/cypress.config.js.ts' }],
}
},
COMPONENT_FOLDER_REMOVED: () => {
return {
default: [{ name: 'componentFolder', configFile: '/path/to/cypress.config.js.ts' }],
}
},
INTEGRATION_FOLDER_REMOVED: () => {
return {
default: [{ name: 'integrationFolder', configFile: '/path/to/cypress.config.js.ts' }],
}
},
MIGRATION_MISMATCHED_CYPRESS_VERSIONS: () => {
return {
default: ['9.6.0', '10.0.0'],
}
},
MIGRATION_CYPRESS_NOT_FOUND: () => {
return {
default: [],
}
},
DEV_SERVER_CONFIG_FILE_NOT_FOUND: () => {
return {
default: ['vite', '/dev/project', ['vite.config.js', 'vite.config.ts']],

View File

@@ -31,7 +31,6 @@ export interface ClientTestContext {
allBundlers: WizardBundler[]
warnings: []
}
migration: {}
user: AuthenticatedUserShape | null
cloudTypes: typeof cloudTypes
__mockPartial: any
@@ -104,7 +103,6 @@ export function makeClientTestContext (): ClientTestContext {
},
],
},
migration: {},
__mockPartial: {},
}
}

View File

@@ -1,158 +0,0 @@
import { MIGRATION_STEPS } from '@packages/types'
import type { Migration } from '../generated/test-graphql-types.gen'
import type { MaybeResolver } from './clientTestUtils'
let _id = 0
const id = () => {
_id++
return _id.toString()
}
export const stubMigration: MaybeResolver<Migration> = {
__typename: 'Migration',
filteredSteps: MIGRATION_STEPS.map((name, index) => {
return {
id: (index + 1).toString(),
index: index + 1,
isCompleted: false,
isCurrentStep: name === 'renameAuto',
__typename: 'MigrationStep',
name,
}
}),
specFiles: [
{
__typename: 'MigrationFile',
testingType: 'e2e',
before: {
__typename: 'MigrationFileData',
id: id(),
relative: 'cypress/integration/app.spec.js',
parts: [
{ id: id(), __typename: 'MigrationFilePart', text: 'cypress/', highlight: false },
{ id: id(), __typename: 'MigrationFilePart', text: 'integration', highlight: true },
{ id: id(), __typename: 'MigrationFilePart', text: '/app', highlight: false },
{ id: id(), __typename: 'MigrationFilePart', text: '.spec.', highlight: true },
{ id: id(), __typename: 'MigrationFilePart', text: 'js', highlight: false },
],
},
after: {
__typename: 'MigrationFileData',
id: id(),
relative: 'cypress/integration/app.spec.js',
parts: [
{ id: id(), __typename: 'MigrationFilePart', text: 'cypress/', highlight: false },
{ id: id(), __typename: 'MigrationFilePart', text: 'integration', highlight: true },
{ id: id(), __typename: 'MigrationFilePart', text: '/app', highlight: false },
{ id: id(), __typename: 'MigrationFilePart', text: '.cy.', highlight: true },
{ id: id(), __typename: 'MigrationFilePart', text: 'js', highlight: false },
],
},
},
],
manualFiles: {
id: id(),
__typename: 'ManualMigration',
completed: false,
files: [
{
id: id(),
__typename: 'ManualMigrationFile',
moved: false,
relative: 'cypress/component/button-spec.js',
},
{
id: id(),
__typename: 'ManualMigrationFile',
moved: true,
relative: 'cypress/component/hello.spec.tsx',
},
],
},
configBeforeCode: `{
"baseUrl": "http://localhost:1234/",
"retries": 2
}`,
configAfterCode: `const { defineConfig } = require('cypress')
module.exports = defineConfig({
retries: 2,
e2e: {
// End-to-end config overrides go here
baseUrl: "http://localhost:1234/"
setupNodeEvents (on, config) {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these directly
return require('cypress/plugins/index.js')(on, config) }
}
},
})`,
integrationFolder: 'cypress/integration',
componentFolder: 'cypress/component',
supportFiles:
{
__typename: 'MigrationFile',
testingType: 'e2e',
before: {
id: id(),
relative: 'cypress/support/index.js',
__typename: 'MigrationFileData',
parts: [
{
id: id(),
__typename: 'MigrationFilePart',
text: 'cypress/support/',
highlight: false,
},
{
id: id(),
__typename: 'MigrationFilePart',
text: 'index',
highlight: true,
},
{
id: id(),
__typename: 'MigrationFilePart',
text: '.js',
highlight: false,
},
],
},
after: {
id: id(),
relative: 'cypress/support/e2e.js',
__typename: 'MigrationFileData',
parts: [
{
id: id(),
__typename: 'MigrationFilePart',
text: 'cypress/support/',
highlight: false,
},
{
id: id(),
__typename: 'MigrationFilePart',
text: 'e2e',
highlight: true,
},
{
id: id(),
__typename: 'MigrationFilePart',
text: '.js',
highlight: false,
},
],
},
},
hasComponentTesting: true,
hasCustomComponentFolder: false,
hasCustomComponentTestFiles: false,
hasCustomIntegrationFolder: false,
hasCustomIntegrationTestFiles: false,
configFileNameAfter: 'cypress.config.js',
configFileNameBefore: 'cypress.json',
shouldMigratePreExtension: true,
}

View File

@@ -13,9 +13,6 @@ export const stubQuery: MaybeResolver<Query> = {
wizard (source, args, ctx) {
return ctx.wizard
},
migration (source, args, ctx) {
return ctx.migration
},
currentProject (source, args, ctx) {
return ctx.currentProject
},

View File

@@ -4,7 +4,6 @@ import { stubMutation } from './stubgql-Mutation'
import { stubQuery } from './stubgql-Query'
import { stubGlobalProject, stubProject } from './stubgql-Project'
import { CloudOrganizationStubs, CloudProjectStubs, CloudRecordKeyStubs, CloudRunStubs, CloudUserStubs } from '@packages/graphql/test/stubCloudTypes'
import { stubMigration } from './stubgql-Migration'
import type { CodegenTypeMap } from '../generated/test-graphql-types.gen'
import { StubErrorWrapper } from './stubgql-ErrorWrapper'
@@ -15,7 +14,6 @@ export const GQLStubRegistry = {
ProjectLike: stubProject,
GlobalProject: stubGlobalProject,
CurrentProject: stubProject,
Migration: stubMigration,
Mutation: stubMutation,
Query: stubQuery,
CloudOrganization: CloudOrganizationStubs.cyOrg,

View File

@@ -221,10 +221,6 @@ mutation GlobalPageHeader_clearCurrentProject {
currentProject {
id
}
# This ensures the cache is updated with null after clearing project
migration {
configFileNameBefore
}
}
}
`

View File

@@ -909,97 +909,6 @@
"link": "install Cypress"
}
},
"migration": {
"before": "Before",
"after": "After",
"heresWhy": "here's why:",
"renameAuto": {
"title": "We recommend automatically renaming your specs in this step",
"changeButton": "change",
"changedSpecFolder": "We've changed the default spec folder from:",
"changedSpecExt": "We've changed the default spec file extension from:",
"changedSpecPatternExplain": "We've changed the default spec file extension to {0} in order to avoid conflicts with any existing testing frameworks.",
"optedOutMessage": "You've opted not to rename your spec file extension. You may need to change your specPattern later so we can still find your spec files.",
"folderRenameMessage": "You've opted not to rename your spec file extension, we'll only rename the folder",
"modal": {
"title": "Change the existing spec file extension",
"warning": "We recommend using the default extension to avoid inconsistencies, framework conflicts, and confusion with your team.",
"line1": "Cypress now supports the ability to create new spec files from within the UI for both E2E and component specs.",
"line2": "All new spec files created within Cypress will use the default pattern of: ",
"line3": "We want to rename your existing specs so that they have a consistent filename pattern for both E2E and component testing.",
"line4": "All documentation and example code will be using: ",
"line5": "We've changed the placement of component specs to be next to their source files (e.g. src/Button.jsx and src/Button.cy.jsx)",
"line6": "The new default pattern of {0} prevents targeting conflicts with other testing frameworks. (e.g. Jest)",
"label": "Choose from the following filename patterns:",
"option1": "{0} (recommended)",
"option2": "Don't rename anything — keep what I have.",
"option3": "Rename folder only.",
"optOutAdditional": "I may need to change my {0} later if I don't use the recommended filename extension.",
"buttonSave": "Save changes",
"buttonCancel": "Cancel"
}
},
"renameManual": {
"title": "We need you to move your component specs manually",
"componentFolderRemoved": "We've removed the {0} options from the Cypress config.",
"addedSpecPattern": "We've added a new {0} option in the Cypress config that tells us where to find your component specs.",
"cannotAuto": "We can't automatically migrate your existing component spec files. We recommend that you move the following component spec files next to your source component files (e.g. {0})",
"ifSkipNote": "If you skip this step, Cypress will still be able to find them, but any new specs that you create will automatically be created next to your component files."
},
"renameSupport": {
"title": "We'll automatically rename your existing E2E support file in this step",
"serveDifferentTypes": "We now serve different support files for E2E and Component Testing.",
"changedSupportFile": "We've renamed the E2E support file from:"
},
"configFile": {
"title": "We need to migrate to the new Cypress configuration file",
"changedDefault": "We've changed the default Cypress config file from:",
"customOptions": "We've set a custom {specPattern} option based on your {options}.",
"willConvert": "We'll automatically create a new {jsFile} file and seed it with your options from your existing {jsonFile}."
},
"setupComponent": {
"title": "You need to reconfigure Cypress for component testing",
"line1": "We've detected that you are currently using the experimental version of component testing.",
"line2": "Your existing configuration is no longer compatible with new component testing configuration options.",
"line3": "In a previous step, we renamed your component specs, but can't automatically migrate your existing component testing configuration.",
"line4": "In the next screen, you'll be able to reconfigure component testing in a new guided configuration wizard."
},
"wizard": {
"title": "Migrating to Cypress {version}",
"description": "Your project requires updates to work with this version.",
"typicalMigrationLabel": "Typical migration:",
"typicalMigrationTime": "5-10 minutes",
"step1": {
"title": "Migrate existing specs",
"description": "In this step, we'll automatically rename and/or move your existing spec files as needed.",
"button": "Rename these specs for me",
"buttonSkip": "Skip renaming specs",
"buttonRenameFolder": "Rename the folder for me"
},
"step2": {
"title": "Move your existing component specs",
"description": "In this step, you'll manually move your existing component specs to their new default location.",
"buttonWait": "Waiting for you to move your component specs...",
"buttonDone": "Continue to next step",
"button": "I'll do this later"
},
"step3": {
"title": "Rename the Cypress support file",
"description": "In this step, we'll automatically rename your existing support file.",
"button": "Rename the support file for me"
},
"step4": {
"title": "Migrate to the new Cypress configuration file",
"description": "In this step, we'll automatically migrate your existing Cypress configuration to the new Cypress configuration file.",
"button": "Migrate the configuration for me"
},
"step5": {
"title": "Reconfigure component testing",
"description": "In this step, we'll explain how you will reconfigure Cypress for component testing.",
"button": "Finish migration and continue"
}
}
},
"majorVersionWelcome": {
"title": "What's New in Cypress",
"actionContinue": "Continue",

View File

@@ -1041,9 +1041,6 @@ type CurrentProject implements Node & ProjectLike {
"""Whether the project has Typescript"""
isUsingTypeScript: Boolean
"""Whether the project needs to be migrated before proceeding"""
needsLegacyConfigMigration: Boolean
packageManager: PackageManagerEnum!
"""Cached preferences for this project"""
@@ -1161,18 +1158,15 @@ enum ErrorTypeEnum {
CLOUD_STALE_RUN
CLOUD_UNKNOWN_CREATE_RUN_WARNING
CLOUD_UNKNOWN_INVALID_REQUEST
COMPONENT_FOLDER_REMOVED
COMPONENT_TESTING_MISMATCHED_DEPENDENCIES
CONFIG_FILES_LANGUAGE_CONFLICT
CONFIG_FILE_DEV_SERVER_INVALID_RETURN
CONFIG_FILE_DEV_SERVER_IS_NOT_VALID
CONFIG_FILE_INVALID_DEV_START_EVENT
CONFIG_FILE_INVALID_ROOT_CONFIG
CONFIG_FILE_INVALID_ROOT_CONFIG_COMPONENT
CONFIG_FILE_INVALID_ROOT_CONFIG_E2E
CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_COMPONENT
CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_E2E
CONFIG_FILE_MIGRATION_NEEDED
CONFIG_FILE_NOT_FOUND
CONFIG_FILE_REQUIRE_ERROR
CONFIG_FILE_SETUP_NODE_EVENTS_ERROR
@@ -1185,46 +1179,31 @@ enum ErrorTypeEnum {
DUPLICATE_TASK_KEY
ERROR_READING_FILE
ERROR_WRITING_FILE
EXPERIMENTAL_COMPONENT_TESTING_REMOVED
EXPERIMENTAL_JIT_COMPILE_REMOVED
EXPERIMENTAL_NETWORK_STUBBING_REMOVED
EXPERIMENTAL_ORIGIN_DEPENDENCIES_E2E_ONLY
EXPERIMENTAL_RUN_ALL_SPECS_E2E_ONLY
EXPERIMENTAL_RUN_EVENTS_REMOVED
EXPERIMENTAL_SAMESITE_REMOVED
EXPERIMENTAL_SESSION_AND_ORIGIN_REMOVED
EXPERIMENTAL_SESSION_SUPPORT_REMOVED
EXPERIMENTAL_SHADOW_DOM_REMOVED
EXPERIMENTAL_SINGLE_TAB_RUN_MODE
EXPERIMENTAL_SKIP_DOMAIN_INJECTION_REMOVED
EXPERIMENTAL_STUDIO_E2E_ONLY
EXPERIMENTAL_STUDIO_REMOVED
EXTENSION_NOT_LOADED
FIREFOX_CDP_FAILED_TO_CONNECT
FIREFOX_COULD_NOT_CONNECT
FIREFOX_GC_INTERVAL_REMOVED
FIREFOX_GECKODRIVER_FAILURE
FIXTURE_NOT_FOUND
FOLDER_NOT_WRITABLE
FREE_PLAN_EXCEEDS_MONTHLY_TESTS
FREE_PLAN_IN_GRACE_PERIOD_EXCEEDS_MONTHLY_TESTS
FREE_PLAN_IN_GRACE_PERIOD_PARALLEL_FEATURE
INCOMPATIBLE_PLUGIN_RETRIES
INCORRECT_CI_BUILD_ID_USAGE
INDETERMINATE_CI_BUILD_ID
INJECT_DOCUMENT_DOMAIN_DEPRECATION
INJECT_DOCUMENT_DOMAIN_E2E_ONLY
INTEGRATION_FOLDER_REMOVED
INVALID_CONFIG_OPTION
INVALID_CYPRESS_INTERNAL_ENV
INVALID_REPORTER_NAME
INVOKED_BINARY_OUTSIDE_NPM_MODULE
JIT_COMPONENT_TESTING
LEGACY_CONFIG_ERROR_DURING_MIGRATION
LEGACY_CONFIG_FILE
MIGRATION_ALREADY_OCURRED
MIGRATION_CYPRESS_NOT_FOUND
MIGRATION_MISMATCHED_CYPRESS_VERSIONS
MULTIPLE_SUPPORT_FILES_FOUND
NO_DEFAULT_CONFIG_FILE_FOUND
NO_PROJECT_FOUND_AT_PROJECT_ROOT
@@ -1233,7 +1212,6 @@ enum ErrorTypeEnum {
PARALLEL_FEATURE_NOT_AVAILABLE_IN_PLAN
PLAN_EXCEEDS_MONTHLY_TESTS
PLAN_IN_GRACE_PERIOD_RUN_GROUPING_FEATURE_USED
PLUGINS_FILE_CONFIG_OPTION_REMOVED
PLUGINS_RUN_EVENT_ERROR
PORT_IN_USE_LONG
PORT_IN_USE_SHORT
@@ -1246,14 +1224,12 @@ enum ErrorTypeEnum {
RENAMED_CONFIG_OPTION
RENDERER_CRASHED
RUN_GROUPING_FEATURE_NOT_AVAILABLE_IN_PLAN
SETUP_NODE_EVENTS_DO_NOT_SUPPORT_DEV_SERVER
SETUP_NODE_EVENTS_INVALID_EVENT_NAME_ERROR
SETUP_NODE_EVENTS_IS_NOT_FUNCTION
SUPPORT_FILE_NOT_FOUND
TESTING_TYPE_NOT_CONFIGURED
TESTS_DID_NOT_START_FAILED
TESTS_DID_NOT_START_RETRYING
TEST_FILES_RENAMED
UNEXPECTED_BEFORE_BROWSER_LAUNCH_PROPERTIES
UNEXPECTED_INTERNAL_ERROR
UNEXPECTED_MUTATION_ERROR
@@ -1481,149 +1457,6 @@ input LocalTestCountsInput {
totalTests: Int!
}
type ManualMigration implements Node {
"""is the manual migration completed (all files are moved)"""
completed: Boolean!
"""files needing manual migration"""
files: [ManualMigrationFile!]!
"""Relay style Node ID field for the ManualMigration field"""
id: ID!
}
type ManualMigrationFile implements Node {
"""Relay style Node ID field for the ManualMigrationFile field"""
id: ID!
"""has the file been moved since opening the migration helper"""
moved: Boolean!
"""name of file to migrate"""
relative: String!
}
"""Contains all data related to the 9.X to 10.0 migration UI"""
type Migration {
"""the component folder path used to store components tests"""
componentFolder: String!
"""contents of the cypress.json file after conversion"""
configAfterCode: String!
"""contents of the cypress.json file before conversion"""
configBeforeCode: String!
"""the name of the config file after the migration"""
configFileNameAfter: String!
"""the name of the config file to be migrated"""
configFileNameBefore: String!
"""Steps filtered with the current context"""
filteredSteps: [MigrationStep!]!
"""whether component testing is set up in the migrated config or not"""
hasComponentTesting: Boolean!
"""whether the component folder is custom or not"""
hasCustomComponentFolder: Boolean!
"""whether the testFiles member is custom or not in component testing"""
hasCustomComponentTestFiles: Boolean!
"""whether the integration folder is custom or not"""
hasCustomIntegrationFolder: Boolean!
"""whether the testFiles member is custom or not in integration"""
hasCustomIntegrationTestFiles: Boolean!
"""the integration folder path used to store e2e tests"""
integrationFolder: String!
"""Whether the project has Typescript"""
isUsingTypeScript: Boolean
"""List of files needing manual conversion"""
manualFiles: ManualMigration
"""whether the pre extension info should be displayed"""
shouldMigratePreExtension: Boolean
"""All spec files after conversion"""
specFiles: [MigrationFile!]!
"""Support files needing automated rename"""
supportFiles: MigrationFile
}
type MigrationFile {
after: MigrationFileData!
before: MigrationFileData!
testingType: TestingTypeEnum!
}
type MigrationFileData implements Node {
"""Relay style Node ID field for the MigrationFileData field"""
id: ID!
parts: [MigrationFilePart!]!
relative: String!
}
type MigrationFilePart implements Node {
"""is this part a folder or extension that needs migration"""
group: String
"""should highlight in migration UI"""
highlight: Boolean!
"""Relay style Node ID field for the MigrationFilePart field"""
id: ID!
"""part of filename"""
text: String!
}
type MigrationRegexp {
"""regexp to use to rename existing specs in component"""
afterComponent: String!
"""regexp to use to rename existing specs in e2e"""
afterE2E: String!
"""regexp to identify existing specs in component"""
beforeComponent: String!
"""regexp to identify existing specs in e2e"""
beforeE2E: String!
}
"""Contains all data related to the 9.X to 10.0 migration UI"""
type MigrationStep implements Node {
"""Relay style Node ID field for the MigrationStep field"""
id: ID!
"""Index of the step in the list"""
index: Int!
"""Has the current step been completed"""
isCompleted: Boolean!
"""This is the current step"""
isCurrentStep: Boolean!
"""Identifier of the step"""
name: MigrationStepEnum!
}
enum MigrationStepEnum {
configFile
renameAuto
renameManual
renameSupport
setupComponent
}
type Mutation {
"""Internal use only, clears the cloud cache"""
_clearCloudCache: Boolean
@@ -1675,9 +1508,6 @@ type Mutation {
dismissWarning(id: ID!): Query
e2eExamples: [ScaffoldedFile!]!
"""user has finished migration component specs - move to next step"""
finishedRenamingComponentSpecs: Query
"""Sets focus to the active browser window"""
focusActiveBrowserWindow: Boolean!
@@ -1721,34 +1551,6 @@ type Mutation {
"""Check if a give spec file will match the project spec pattern"""
matchesSpecPattern(specFile: String!): Boolean!
"""While migrating to 10+ skip manual rename step"""
migrateCloseManualRenameWatcher: Boolean
"""Merges the component testing config in cypress.config.{js,ts}"""
migrateComponentTesting: Query
"""Transforms cypress.json file into cypress.config.js file"""
migrateConfigFile: Query
"""While migrating to 10+ renames files to match the new .cy pattern"""
migrateRenameSpecs(
"""specs to move - current name"""
after: [String!]
"""specs to move - current name"""
before: [String!]
skip: Boolean
): Query
"""When the user decides to skip specs rename"""
migrateRenameSpecsFolder: Query
"""While migrating to 10+ launch renaming of support file"""
migrateRenameSupport: Query
"""While migrating to 10+ skip manual rename step"""
migrateSkipManualRename: Query
"""
Allow the relevant run for debugging marked as next to be considered the current relevant run
"""
@@ -2054,9 +1856,6 @@ type Query {
Unique node machine identifier for this instance - may be nil if unable to resolve
"""
machineId: String
"""Metadata about the migration, null if we aren't showing it"""
migration: Migration
node(id: ID!): Node
"""Defines the suggested polling intervals for various schema resources"""

View File

@@ -112,13 +112,6 @@ export const CurrentProject = objectType({
},
})
t.boolean('needsLegacyConfigMigration', {
description: 'Whether the project needs to be migrated before proceeding',
resolve (source, args, ctx) {
return ctx.migration.needsCypressJsonMigration()
},
})
t.boolean('hasValidConfigFile', {
description: 'Whether the project has a valid config file',
resolve (source, args, ctx) {

View File

@@ -1,290 +0,0 @@
import { enumType, objectType } from 'nexus'
import { TestingTypeEnum } from '..'
import { MIGRATION_STEPS } from '@packages/types'
export const MigrationStepEnum = enumType({
name: 'MigrationStepEnum',
members: MIGRATION_STEPS,
})
export const MigrationStep = objectType({
name: 'MigrationStep',
node: 'name',
description: 'Contains all data related to the 9.X to 10.0 migration UI',
definition (t) {
t.nonNull.field('name', {
type: MigrationStepEnum,
description: 'Identifier of the step',
})
t.nonNull.boolean('isCurrentStep', {
description: 'This is the current step',
resolve: (source, args, ctx) => {
return ctx.coreData.migration.step === source.name
},
})
t.nonNull.boolean('isCompleted', {
description: 'Has the current step been completed',
resolve: (source, args, ctx) => {
const indexOfObservedStep = ctx.coreData.migration.filteredSteps.indexOf(source.name)
const indexOfCurrentStep = ctx.coreData.migration.filteredSteps.indexOf(ctx.coreData.migration.step)
return indexOfObservedStep < indexOfCurrentStep
},
})
t.nonNull.int('index', {
description: 'Index of the step in the list',
resolve: (source, args, ctx) => {
return ctx.coreData.migration.filteredSteps.indexOf(source.name) + 1
},
})
},
})
export const MigrationFilePart = objectType({
name: 'MigrationFilePart',
node: (obj) => obj.text,
definition (t) {
t.nonNull.string('text', {
description: 'part of filename',
})
t.nonNull.boolean('highlight', {
description: 'should highlight in migration UI',
})
t.string('group', {
description: 'is this part a folder or extension that needs migration',
})
},
})
export const ManualMigrationFile = objectType({
name: 'ManualMigrationFile',
node: 'relative',
definition (t) {
t.nonNull.boolean('moved', {
description: 'has the file been moved since opening the migration helper',
})
t.nonNull.string('relative', {
description: 'name of file to migrate',
})
},
})
export const ManualMigration = objectType({
name: 'ManualMigration',
node: ({ files }) => files.map((f) => f.relative).join(),
definition (t) {
t.nonNull.list.nonNull.field('files', {
type: ManualMigrationFile,
description: 'files needing manual migration',
})
t.nonNull.boolean('completed', {
description: 'is the manual migration completed (all files are moved)',
})
},
})
export const MigrationFileData = objectType({
name: 'MigrationFileData',
node: (obj) => obj.parts.map((file) => file.text).join(''),
definition (t) {
t.nonNull.string('relative')
t.nonNull.list.nonNull.field('parts', {
type: MigrationFilePart,
})
},
})
export const MigrationFile = objectType({
name: 'MigrationFile',
definition (t) {
t.nonNull.field('testingType', {
type: TestingTypeEnum,
})
t.nonNull.field('before', {
type: MigrationFileData,
})
t.nonNull.field('after', {
type: MigrationFileData,
})
},
})
export const MigrationRegexp = objectType({
name: 'MigrationRegexp',
definition (t) {
t.nonNull.string('beforeE2E', {
description: 'regexp to identify existing specs in e2e',
})
t.nonNull.string('afterE2E', {
description: 'regexp to use to rename existing specs in e2e',
})
t.nonNull.string('beforeComponent', {
description: 'regexp to identify existing specs in component',
})
t.nonNull.string('afterComponent', {
description: 'regexp to use to rename existing specs in component',
})
},
})
export const Migration = objectType({
name: 'Migration',
description: 'Contains all data related to the 9.X to 10.0 migration UI',
definition (t) {
t.nonNull.list.nonNull.field('filteredSteps', {
type: MigrationStep,
description: 'Steps filtered with the current context',
resolve: (source, args, ctx) => {
return ctx.coreData.migration.filteredSteps.map((name) => {
return {
name,
}
})
},
})
t.nonNull.list.nonNull.field('specFiles', {
description: 'All spec files after conversion',
type: MigrationFile,
resolve: async (source, args, ctx) => {
const result = await ctx.migration.getSpecsForMigrationGuide()
return result
},
})
t.field('manualFiles', {
description: 'List of files needing manual conversion',
type: ManualMigration,
resolve: async (source, args, ctx) => {
// avoid starting the watcher when not on this step
if (ctx.coreData.migration.step !== 'renameManual') {
return null
}
const status = await ctx.migration.getComponentTestingMigrationStatus()
if (!status) {
return null
}
return {
completed: status.completed,
// we sort it to make sure the endpoint always returns the
// specs in the same order, so things don't jump around.
files: [...status.files.values()]
.sort((x, y) => y.relative.length - x.relative.length),
}
},
})
t.field('supportFiles', {
description: 'Support files needing automated rename',
type: MigrationFile,
resolve: (source, args, ctx) => {
return ctx.migration.supportFilesForMigrationGuide()
},
})
t.nonNull.string('configFileNameBefore', {
description: 'the name of the config file to be migrated',
resolve: (source, args, ctx) => {
return ctx.migration.legacyConfigFile
},
})
t.nonNull.string('configFileNameAfter', {
description: 'the name of the config file after the migration',
resolve: (source, args, ctx) => {
return ctx.migration.configFileNameAfterMigration
},
})
t.nonNull.string('configBeforeCode', {
description: 'contents of the cypress.json file before conversion',
resolve: (source, args, ctx) => {
return JSON.stringify(ctx.coreData.migration.legacyConfigForMigration, null, 2)
},
})
t.nonNull.string('configAfterCode', {
description: 'contents of the cypress.json file after conversion',
resolve: (source, args, ctx) => {
return ctx.migration.createConfigString()
},
})
t.nonNull.string('integrationFolder', {
description: 'the integration folder path used to store e2e tests',
resolve: async (source, args, ctx) => (await ctx.migration.integrationFolder()).toString(),
})
t.nonNull.string('componentFolder', {
description: 'the component folder path used to store components tests',
resolve: async (source, args, ctx) => (await ctx.migration.componentFolder()).toString(),
})
t.nonNull.boolean('hasCustomIntegrationFolder', {
description: 'whether the integration folder is custom or not',
resolve: (source, args, ctx) => {
return ctx.coreData.migration.flags.hasCustomIntegrationFolder
}
,
})
t.nonNull.boolean('hasCustomIntegrationTestFiles', {
description: 'whether the testFiles member is custom or not in integration',
resolve: (source, args, ctx) => {
return ctx.coreData.migration.flags.hasCustomIntegrationTestFiles
},
})
t.nonNull.boolean('hasCustomComponentFolder', {
description: 'whether the component folder is custom or not',
resolve: (source, args, ctx) => {
return ctx.coreData.migration.flags.hasCustomComponentFolder
},
})
t.nonNull.boolean('hasCustomComponentTestFiles', {
description: 'whether the testFiles member is custom or not in component testing',
resolve: (source, args, ctx) => {
return ctx.coreData.migration.flags.hasCustomComponentTestFiles
},
})
t.nonNull.boolean('hasComponentTesting', {
description: 'whether component testing is set up in the migrated config or not',
resolve: (source, args, ctx) => {
return ctx.coreData.migration.flags.hasComponentTesting
},
})
t.boolean('isUsingTypeScript', {
description: 'Whether the project has Typescript',
resolve (source, args, ctx) {
return ctx.lifecycleManager.metaState.isUsingTypeScript
},
})
t.boolean('shouldMigratePreExtension', {
description: 'whether the pre extension info should be displayed',
resolve: (source, args, ctx) => {
return ctx.migration.shouldMigratePreExtension
},
})
},
})

View File

@@ -489,102 +489,6 @@ export const mutation = mutationType({
},
})
t.field('migrateRenameSpecs', {
description: 'While migrating to 10+ renames files to match the new .cy pattern',
type: Query,
args: {
skip: booleanArg(),
before: list(nonNull(stringArg({
description: 'specs to move - current name',
}))),
after: list(nonNull(stringArg({
description: 'specs to move - current name',
}))),
},
resolve: async (_, { skip, before, after }, ctx) => {
if (!skip && before && after) {
await ctx.actions.migration.renameSpecFiles(before, after)
}
await ctx.actions.migration.nextStep()
return {}
},
})
t.field('migrateRenameSpecsFolder', {
description: 'When the user decides to skip specs rename',
type: Query,
resolve: async (_, args, ctx) => {
await ctx.actions.migration.renameSpecsFolder()
await ctx.actions.migration.nextStep()
return {}
},
})
t.field('migrateSkipManualRename', {
description: 'While migrating to 10+ skip manual rename step',
type: Query,
resolve: async (_, args, ctx) => {
await ctx.actions.migration.nextStep()
return {}
},
})
t.field('migrateCloseManualRenameWatcher', {
description: 'While migrating to 10+ skip manual rename step',
type: 'Boolean',
resolve: async (_, args, ctx) => {
await ctx.actions.migration.closeManualRenameWatcher()
return true
},
})
t.field('finishedRenamingComponentSpecs', {
description: 'user has finished migration component specs - move to next step',
type: Query,
resolve: async (_, args, ctx) => {
await ctx.actions.migration.nextStep()
return {}
},
})
t.field('migrateRenameSupport', {
description: 'While migrating to 10+ launch renaming of support file',
type: Query,
resolve: async (_, args, ctx) => {
await ctx.actions.migration.renameSupportFile()
await ctx.actions.migration.nextStep()
return {}
},
})
t.field('migrateConfigFile', {
description: 'Transforms cypress.json file into cypress.config.js file',
type: Query,
resolve: async (_, args, ctx) => {
await ctx.actions.migration.createConfigFile()
await ctx.actions.migration.nextStep()
return {}
},
})
t.field('migrateComponentTesting', {
description: 'Merges the component testing config in cypress.config.{js,ts}',
type: Query,
resolve: async (_, args, ctx) => {
await ctx.actions.migration.nextStep()
return {}
},
})
t.field('setProjectIdInConfigFile', {
description: 'Set the projectId field in the config file of the current project',
type: Query,

View File

@@ -5,7 +5,6 @@ import { CurrentProject } from './gql-CurrentProject'
import { DevState } from './gql-DevState'
import { AuthState } from './gql-AuthState'
import { LocalSettings } from './gql-LocalSettings'
import { Migration } from './gql-Migration'
import { VersionData } from './gql-VersionData'
import { Wizard } from './gql-Wizard'
import { ErrorWrapper } from './gql-ErrorWrapper'
@@ -42,23 +41,6 @@ export const Query = objectType({
resolve: (root, args, ctx) => ctx.coreData.wizard,
})
t.field('migration', {
type: Migration,
description: 'Metadata about the migration, null if we aren\'t showing it',
resolve: async (root, args, ctx) => {
// First check to see if "legacyConfigForMigration" is defined as that means we have started migration
if (ctx.coreData.migration.legacyConfigForMigration) return ctx.coreData.migration.legacyConfigForMigration
if (!ctx.migration.needsCypressJsonMigration()) {
return null
}
await ctx.lifecycleManager.legacyMigration()
return ctx.coreData.migration.legacyConfigForMigration
},
})
t.nonNull.field('dev', {
type: DevState,
description: 'The state of any info related to local development of the runner',

View File

@@ -17,7 +17,6 @@ export * from './gql-GeneratedSpecError'
export * from './gql-GitInfo'
export * from './gql-GlobalProject'
export * from './gql-LocalSettings'
export * from './gql-Migration'
export * from './gql-Mutation'
export * from './gql-ProjectPreferences'
export * from './gql-Query'

View File

@@ -9,8 +9,7 @@ It replaces the original electron app, `desktop-gui`.
- Allow users to log in through Cypress Cloud
- Onboarding for new users (configure Component Testing dev server, install dependencies, etc)
- Select testing mode (E2E, Component)
- Provide UI to perform automated migration steps (for example migrating `cypress.json` to `cypress.config.js` for projects upgrading from 9.x or below)
- Provide a dismissable Welcome Screen for every major release of Cypress
- Provide a dismissible Welcome Screen for every major release of Cypress
It is using the following technologies:
@@ -29,7 +28,7 @@ Cypress' entire back-end is powered by the `@packages/server` package. Launchpad
## Major Version Welcome Content
The content is bundled with the launchpad and at the time of writing this, it lives in `src/migration/MajorVersionWelcome.vue`. Shipping it as part of the app means it is always available upon release and it will always work offline. Guidelines for the management of the content itself are documented internally in our `prod-eng-docs`, but the implementation is documented here.
The content is bundled with the launchpad and at the time of writing this, it lives in `src/welcome/MajorVersionWelcome.vue`. Shipping it as part of the app means it is always available upon release and it will always work offline. Guidelines for the management of the content itself are documented internally in our `prod-eng-docs`, but the implementation is documented here.
A constant named `MAJOR_VERSION_FOR_CONTENT` defines which major version the content is associated with for the purposes of recording user dismissal in persisted state. This needs to be bumped to match the major version that will be released, since that value is the key that records the dismissal.

View File

@@ -1,5 +1,3 @@
import defaultMessages from '@packages/frontend-shared/src/locales/en-US.json'
import pkg from '../../../../package.json'
import { getPathForPlatform } from './support/getPathForPlatform'
const expectStackToBe = (mode: 'open' | 'closed') => {
@@ -34,54 +32,19 @@ describe('Config files error handling', () => {
cy.contains('h1', 'Welcome to Cypress', { timeout: 10000 })
})
it('shows the upgrade screen if there is a legacy config file', () => {
cy.openProject('pristine-with-e2e-testing')
cy.withCtx(async (ctx) => {
await ctx.actions.file.writeFileInProject('cypress.json', '{}')
await ctx.actions.file.removeFileInProject('cypress.config.js')
})
cy.openProject('pristine-with-e2e-testing')
cy.visitLaunchpad()
cy.get('body').should('contain.text', defaultMessages.migration.wizard.title.replace('{version}', pkg.version.split('.')[0]))
cy.get('body').should('contain.text', defaultMessages.migration.wizard.description)
})
it('handles config files with legacy config file in same project', () => {
cy.openProject('pristine-with-e2e-testing')
cy.withCtx(async (ctx) => {
await ctx.actions.file.writeFileInProject('cypress.json', '{}')
})
cy.openProject('pristine-with-e2e-testing')
cy.visitLaunchpad()
cy.contains('p', 'There is both a cypress.config.js and a cypress.json file at the location below:')
cy.contains('body', 'Cypress no longer supports cypress.json')
expectStackToBe('closed')
cy.withCtx(async (ctx) => {
await ctx.actions.file.removeFileInProject('cypress.json')
})
cy.findByRole('button', { name: 'Try again' }).click()
cy.contains('h1', 'Welcome to Cypress', { timeout: 10000 })
})
it('handles deprecated config fields', () => {
it('handles removed config fields', () => {
cy.openProject('pristine')
cy.withCtx(async (ctx) => {
await ctx.actions.file.writeFileInProject('cypress.config.js', 'module.exports = { e2e: { supportFile: false, experimentalComponentTesting: true } }')
// ensure the config set here has 'isWarning: false' to ensure it errors in UI
// supportFile is required so the config is valid
await ctx.actions.file.writeFileInProject('cypress.config.js', 'module.exports = { e2e: { supportFile: false, experimentalSkipDomainInjection: true } }')
})
cy.openProject('pristine')
cy.visitLaunchpad()
cy.get('[data-cy-testingType=e2e]').click()
cy.get('body', { timeout: 10000 }).should('contain.text', 'experimentalComponentTesting')
cy.get('body', { timeout: 10000 }).should('contain.text', 'experimentalSkipDomainInjection')
expectStackToBe('closed')
cy.withCtx(async (ctx) => {
await ctx.actions.file.writeFileInProject('cypress.config.js', 'module.exports = { e2e: { supportFile: false } }')
@@ -216,39 +179,31 @@ describe('Launchpad: Error System Tests', () => {
})
describe('setupNodeEvents', () => {
it('throws an error when in setupNodeEvents updating a config value that was removed in 10.X', () => {
cy.scaffoldProject('config-update-non-migrated-value')
cy.openProject('config-update-non-migrated-value')
it('throws an error when in setupNodeEvents updating a config value in the root config that was removed', () => {
cy.scaffoldProject('config-update-in-setup-node-events')
cy.openProject('config-update-in-setup-node-events')
cy.visitLaunchpad()
cy.findByText('E2E Testing').click()
cy.contains('h1', cy.i18n.launchpadErrors.generic.configErrorTitle, { timeout: 10000 })
cy.findAllByTestId('collapsible').should('be.visible')
cy.get('h2').contains('Error running e2e.setupNodeEvents()')
cy.get('p').contains('The integrationFolder configuration option is now invalid when set on the config object in Cypress version 10.0.0.')
cy.get('p').contains('It is now renamed to specPattern and configured separately as a end to end testing property: e2e.specPattern')
cy.get('p').contains('The experimentalSkipDomainInjection experiment is over.')
cy.get('p').contains('Read the migration guide for Cypress v14.0.0')
})
it('throws an error when in setupNodeEvents updating a config value on a clone of config that was removed in 10.X', () => {
cy.scaffoldProject('config-update-non-migrated-value-clone')
cy.openProject('config-update-non-migrated-value-clone')
it('throws an error when in setupNodeEvents updating a config value on a clone of config in the root config that was removed', () => {
cy.scaffoldProject('config-update-in-setup-node-events-clone')
cy.openProject('config-update-in-setup-node-events-clone')
cy.visitLaunchpad()
cy.findByText('E2E Testing').click()
cy.contains('h1', cy.i18n.launchpadErrors.generic.configErrorTitle, { timeout: 10000 })
cy.percySnapshot()
cy.get('[data-cy="alert-body"]').should('contain', 'integrationFolder')
cy.get('p').contains('The experimentalSkipDomainInjection experiment is over.')
cy.get('p').contains('Read the migration guide for Cypress v14.0.0')
})
it('throws an error when in setupNodeEvents updating an e2e config value that was removed in 10.X', () => {
cy.scaffoldProject('config-update-non-migrated-value-e2e')
cy.openProject('config-update-non-migrated-value-e2e')
cy.visitLaunchpad()
cy.findByText('E2E Testing').click()
cy.contains('h1', cy.i18n.launchpadErrors.generic.configErrorTitle, { timeout: 10000 })
cy.percySnapshot()
})
it('handles deprecated config fields in setupNodeEvents', () => {
it('handles removed config fields in setupNodeEvents', () => {
cy.scaffoldProject('pristine')
cy.openProject('pristine')
cy.withCtx(async (ctx) => {
@@ -257,7 +212,7 @@ describe('setupNodeEvents', () => {
e2e: {
supportFile: false,
setupNodeEvents(on, config){
config.testFiles = '**/*.spec.js'
config.experimentalSkipDomainInjection = true
return config
}
}
@@ -268,7 +223,7 @@ describe('setupNodeEvents', () => {
cy.visitLaunchpad()
cy.get('[data-cy-testingType=e2e]').click()
cy.get('body', { timeout: 10000 }).should('contain.text', 'testFiles')
cy.get('body', { timeout: 10000 }).should('contain.text', 'experimentalSkipDomainInjection')
cy.get('body', { timeout: 10000 }).should('contain.text', 'setupNodeEvents')
expectStackToBe('closed')
cy.withCtx(async (ctx) => {
@@ -300,7 +255,7 @@ describe('setupNodeEvents', () => {
cy.findByRole('button', { name: 'Try again' }).click()
cy.get('[data-cy-testingType=e2e]').click()
cy.contains('h1', cy.i18n.launchpadErrors.generic.configErrorTitle, { timeout: 10000 })
cy.get('[data-cy="alert-body"]').should('contain', 'The baseUrl configuration option is now invalid when set from the root of the config object')
cy.get('[data-cy="alert-body"]').should('contain', 'The baseUrl configuration option is invalid when set from the root of the config object')
cy.withCtx(async (ctx) => {
await ctx.actions.file.writeFileInProject('cypress.config.js', `module.exports = { e2e: { baseUrl: 'http://localhost:3000', supportFile: false } }`)

File diff suppressed because it is too large Load Diff

View File

@@ -23,9 +23,6 @@
v-else-if="query.data.value.isGlobalMode && !query.data.value?.currentProject"
:gql="query.data.value"
/>
<MigrationWizard
v-else-if="currentProject?.needsLegacyConfigMigration"
/>
<template v-else>
<ScaffoldedFiles
v-if="query.data.value.scaffoldedFiles"
@@ -93,9 +90,8 @@ import StandardModal from '@cy/components/StandardModal.vue'
import HeaderBar from '@cy/gql-components/HeaderBar.vue'
import Spinner from '@cy/components/Spinner.vue'
import CompareTestingTypes from './setup/CompareTestingTypes.vue'
import MigrationWizard from './migration/MigrationWizard.vue'
import ScaffoldedFiles from './setup/ScaffoldedFiles.vue'
import MajorVersionWelcome from './migration/MajorVersionWelcome.vue'
import MajorVersionWelcome from './welcome/MajorVersionWelcome.vue'
import { useI18n } from '@cy/i18n'
import { computed, ref, watch } from 'vue'
import LaunchpadHeader from './setup/LaunchpadHeader.vue'
@@ -131,7 +127,6 @@ fragment MainLaunchpadQueryData on Query {
isLoadingConfigFile
isLoadingNodeEvents
isFullConfigReady
needsLegacyConfigMigration
currentTestingType
activeBrowser {
id

View File

@@ -1,113 +0,0 @@
// tslint:disable-next-line: no-implicit-dependencies - need to handle this
import { defaultMessages } from '@cy/i18n'
import { ConvertConfigFileFragmentDoc } from '../generated/graphql-test'
import ConvertConfigFile from './ConvertConfigFile.vue'
describe('<ConvertConfigFile/>', { viewportWidth: 1119 }, () => {
it('renders the lines for components folder', () => {
cy.mountFragment(ConvertConfigFileFragmentDoc, {
onResult (res) {
res.hasCustomComponentFolder = true
return res
},
render (gql) {
return (<div class="p-[16px]">
<ConvertConfigFile gql={gql} />
</div>)
},
})
cy.findByText('component.specPattern').should('be.visible')
cy.findByText('componentFolder').should('be.visible')
})
it('renders the lines for components testFiles', () => {
cy.mountFragment(ConvertConfigFileFragmentDoc, {
onResult (res) {
res.hasCustomComponentTestFiles = true
return res
},
render (gql) {
return (<div class="p-[16px]">
<ConvertConfigFile gql={gql} />
</div>)
},
})
cy.findByText('component.specPattern').should('be.visible')
cy.findByText('testFiles').should('be.visible')
cy.findByText('e2e.specPattern').should('not.exist')
})
it('renders the lines for e2e folder', () => {
cy.mountFragment(ConvertConfigFileFragmentDoc, {
onResult (res) {
res.hasCustomIntegrationFolder = true
return res
},
render (gql) {
return (<div class="p-[16px]">
<ConvertConfigFile gql={gql} />
</div>)
},
})
cy.findByText('e2e.specPattern').should('be.visible')
cy.findByText('integrationFolder').should('be.visible')
})
it('renders the lines for e2e testFiles', () => {
cy.mountFragment(ConvertConfigFileFragmentDoc, {
onResult (res) {
res.hasCustomIntegrationTestFiles = true
return res
},
render (gql) {
return (<div class="p-[16px]">
<ConvertConfigFile gql={gql} />
</div>)
},
})
cy.findByText('e2e.specPattern').should('be.visible')
cy.findByText('testFiles').should('be.visible')
cy.findByText('componentFolder').should('not.exist')
})
it('renders all lines if both are custom', () => {
cy.mountFragment(ConvertConfigFileFragmentDoc, {
onResult (res) {
res.hasCustomIntegrationTestFiles = true
res.hasCustomComponentFolder = true
return res
},
render (gql) {
return (<div class="p-[16px]">
<ConvertConfigFile gql={gql} />
</div>)
},
})
cy.findByText('e2e.specPattern').should('be.visible')
cy.findByText('component.specPattern').should('be.visible')
cy.contains('li', 'testFiles').should('contain', 'e2e.specPattern')
cy.contains('li', 'componentFolder').should('contain', 'component.specPattern')
})
it('renders expected content with title', () => {
cy.mountFragment(ConvertConfigFileFragmentDoc, {
render (gql) {
return (<div class="p-[16px]">
<ConvertConfigFile gql={gql} />
</div>)
},
})
cy.contains(defaultMessages.migration.configFile.title).should('be.visible')
})
})

View File

@@ -1,185 +0,0 @@
<template>
<div class="text-[16px] leading-[24px]">
<MigrationTitle :title="t('migration.configFile.title')" />
<MigrationList>
<MigrationListItem>
{{ t("migration.configFile.changedDefault") }}
<CodeTag class="text-red-500">
cypress.json
</CodeTag>
<i-cy-arrow-right_x16 class="inline-block h-[16px] w-[16px] icon-dark-gray-300" />
<CodeTag class="text-jade-500">
{{ fileName }}
</CodeTag>
</MigrationListItem>
<MigrationListItem v-if="props.gql.hasCustomIntegrationFolder || props.gql.hasCustomIntegrationTestFiles">
<i18n-t
scope="global"
keypath="migration.configFile.customOptions"
>
<template #specPattern>
<CodeTag class="text-jade-500">
e2e.specPattern
</CodeTag>
</template>
<template #options>
<template v-if="props.gql.hasCustomIntegrationFolder && props.gql.hasCustomIntegrationTestFiles">
<CodeTag class="text-red-500">
integrationFolder
</CodeTag> and <CodeTag class="text-red-500">
testFiles
</CodeTag> options
</template>
<template v-else-if="props.gql.hasCustomIntegrationFolder">
<CodeTag class="text-red-500">
integrationFolder
</CodeTag> option
</template>
<template v-else-if="props.gql.hasCustomIntegrationTestFiles">
<CodeTag class="text-red-500">
testFiles
</CodeTag> option
</template>
</template>
</i18n-t>
</MigrationListItem>
<MigrationListItem v-if="props.gql.hasCustomComponentFolder || props.gql.hasCustomComponentTestFiles">
<i18n-t
scope="global"
keypath="migration.configFile.customOptions"
>
<template #specPattern>
<CodeTag class="text-jade-500">
component.specPattern
</CodeTag>
</template>
<template #options>
<template v-if="props.gql.hasCustomComponentFolder && props.gql.hasCustomComponentTestFiles">
<CodeTag class="text-red-500">
componentFolder
</CodeTag> and <CodeTag class="text-red-500">
testFiles
</CodeTag> options
</template>
<template v-else-if="props.gql.hasCustomComponentFolder">
<CodeTag class="text-red-500">
componentFolder
</CodeTag> option
</template>
<template v-else-if="props.gql.hasCustomComponentTestFiles">
<CodeTag class="text-red-500">
testFiles
</CodeTag> option
</template>
</template>
</i18n-t>
</MigrationListItem>
<MigrationListItem>
<i18n-t
scope="global"
keypath="migration.configFile.willConvert"
>
<template #jsonFile>
<CodeTag class="text-red-500">
{{ props.gql.configFileNameBefore }}
</CodeTag>
</template>
<template #jsFile>
<CodeTag class="text-jade-500">
{{ props.gql.configFileNameAfter }}
</CodeTag>
</template>
</i18n-t>
</MigrationListItem>
</MigrationList>
<BeforeAfter>
<template #beforeHeader>
<span class="mr-[8px]">{{ t('migration.before') }}</span>
<CodeTag
bg
class="text-red-600 bg-red-100"
>
{{ props.gql.configFileNameBefore }}
</CodeTag>
</template>
<template #afterHeader>
<span class="mr-[8px]">{{ t('migration.after') }}</span>
<CodeTag
bg
class="bg-jade-100 text-jade-600"
>
{{ props.gql.configFileNameAfter }}
</CodeTag>
</template>
<template #before>
<ShikiHighlight
:code="codeBefore"
lang="json"
line-numbers
skip-trim
/>
</template>
<template #after>
<ShikiHighlight
:code="codeAfter"
lang="js"
line-numbers
skip-trim
/>
</template>
</BeforeAfter>
</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import CodeTag from '@cy/components/CodeTag.vue'
import BeforeAfter from './fragments/BeforeAfter.vue'
import ShikiHighlight from '@packages/frontend-shared/src/components/ShikiHighlight.vue'
import MigrationList from './fragments/MigrationList.vue'
import MigrationTitle from './fragments/MigrationTitle.vue'
import { useI18n } from '@cy/i18n'
import { gql } from '@urql/vue'
import type { ConvertConfigFileFragment } from '../generated/graphql'
import MigrationListItem from './fragments/MigrationListItem.vue'
const { t } = useI18n()
gql`
fragment ConvertConfigFile on Migration {
configFileNameBefore
configFileNameAfter
configBeforeCode
configAfterCode
hasCustomIntegrationFolder
hasCustomIntegrationTestFiles
hasCustomComponentFolder
hasCustomComponentTestFiles
isUsingTypeScript
}`
const props = defineProps<{
gql: ConvertConfigFileFragment
}>()
const gqlCodeBeforeLines = computed(() => props.gql.configBeforeCode.split('\n').length)
const gqlCodeAfterLines = computed(() => props.gql.configAfterCode.split('\n').length)
const gqlCodeMaxLines = computed(() => Math.max(gqlCodeBeforeLines.value, gqlCodeAfterLines.value))
const codeBefore = computed(() => {
return props.gql.configBeforeCode + Array(gqlCodeMaxLines.value - gqlCodeBeforeLines.value).fill('\n').join('')
})
const codeAfter = computed(() => {
return props.gql.configAfterCode + Array(gqlCodeMaxLines.value - gqlCodeAfterLines.value).fill('\n').join('')
})
const fileName = computed(() => props.gql.isUsingTypeScript ? 'cypress.config.ts' : 'cypress.config.js')
</script>
<style lang="scss" scoped>
.before\:block {
content:'';
}
</style>

Some files were not shown because too many files have changed in this diff Show More