diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index 5eb84aef48..5f4eb85073 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -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/<> - - run: - name: Scaffold new config file - working_directory: /tmp/<> - 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/<> - 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/<> diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 67d2333c9f..0f6e6992b7 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -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_ diff --git a/cli/types/tests/plugins-config.ts b/cli/types/tests/plugins-config.ts index 8b92a50103..14966c2178 100644 --- a/cli/types/tests/plugins-config.ts +++ b/cli/types/tests/plugins-config.ts @@ -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) => {} diff --git a/guides/error-handling.md b/guides/error-handling.md index 4ae4daf3d0..24ca4d3578 100644 --- a/guides/error-handling.md +++ b/guides/error-handling.md @@ -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)} ` diff --git a/npm/grep/README.md b/npm/grep/README.md index e28117773d..ae9d4607d0 100644 --- a/npm/grep/README.md +++ b/npm/grep/README.md @@ -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: diff --git a/packages/config/__snapshots__/index.spec.ts.js b/packages/config/__snapshots__/index.spec.ts.js index bece472545..54fe87d293 100644 --- a/packages/config/__snapshots__/index.spec.ts.js +++ b/packages/config/__snapshots__/index.spec.ts.js @@ -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', ] diff --git a/packages/config/src/options.ts b/packages/config/src/options.ts index e86be8c013..8f266c397c 100644 --- a/packages/config/src/options.ts +++ b/packages/config/src/options.ts @@ -9,26 +9,15 @@ import type { TestingType } from '@packages/types' import * as validate from './validation' const BREAKING_OPTION_ERROR_KEY: Readonly = [ - '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 = [ */ export const breakingOptions: Readonly = [ { - 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', diff --git a/packages/config/test/index.spec.ts b/packages/config/test/index.spec.ts index 60ee518fd2..a62852715a 100644 --- a/packages/config/test/index.spec.ts +++ b/packages/config/test/index.spec.ts @@ -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', diff --git a/packages/config/test/project/utils.spec.ts b/packages/config/test/project/utils.spec.ts index ab47b455d6..b707d6a180 100644 --- a/packages/config/test/project/utils.spec.ts +++ b/packages/config/test/project/utils.spec.ts @@ -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 = { diff --git a/packages/data-context/__snapshots__/codegen.spec.ts.js b/packages/data-context/__snapshots__/codegen.spec.ts.js deleted file mode 100644 index f6bc863c2b..0000000000 --- a/packages/data-context/__snapshots__/codegen.spec.ts.js +++ /dev/null @@ -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'], - }, -}) - -` diff --git a/packages/data-context/src/DataActions.ts b/packages/data-context/src/DataActions.ts index 145b71c970..9579e6d000 100644 --- a/packages/data-context/src/DataActions.ts +++ b/packages/data-context/src/DataActions.ts @@ -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 } diff --git a/packages/data-context/src/DataContext.ts b/packages/data-context/src/DataContext.ts index f5f87c286e..f05b329199 100644 --- a/packages/data-context/src/DataContext.ts +++ b/packages/data-context/src/DataContext.ts @@ -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. */ diff --git a/packages/data-context/src/actions/MigrationActions.ts b/packages/data-context/src/actions/MigrationActions.ts deleted file mode 100644 index 4b2b496483..0000000000 --- a/packages/data-context/src/actions/MigrationActions.ts +++ /dev/null @@ -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 { - 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 } - }) - } -} diff --git a/packages/data-context/src/actions/ProjectActions.ts b/packages/data-context/src/actions/ProjectActions.ts index fb9a8fa2ac..f2120ec5a5 100644 --- a/packages/data-context/src/actions/ProjectActions.ts +++ b/packages/data-context/src/actions/ProjectActions.ts @@ -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() diff --git a/packages/data-context/src/actions/index.ts b/packages/data-context/src/actions/index.ts index 9616fd729e..b5bae1d192 100644 --- a/packages/data-context/src/actions/index.ts +++ b/packages/data-context/src/actions/index.ts @@ -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' diff --git a/packages/data-context/src/codegen/spec-options.ts b/packages/data-context/src/codegen/spec-options.ts index e4b08eb8ec..18bda803ce 100644 --- a/packages/data-context/src/codegen/spec-options.ts +++ b/packages/data-context/src/codegen/spec-options.ts @@ -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' diff --git a/packages/data-context/src/data/LegacyPluginsIpc.ts b/packages/data-context/src/data/LegacyPluginsIpc.ts deleted file mode 100644 index 7687e4bc40..0000000000 --- a/packages/data-context/src/data/LegacyPluginsIpc.ts +++ /dev/null @@ -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() - } -} diff --git a/packages/data-context/src/data/ProjectLifecycleManager.ts b/packages/data-context/src/data/ProjectLifecycleManager.ts index 86bd1c9e7a..84c14cd83f 100644 --- a/packages/data-context/src/data/ProjectLifecycleManager.ts +++ b/packages/data-context/src/data/ProjectLifecycleManager.ts @@ -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 { 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)) - } } /** diff --git a/packages/data-context/src/data/coreDataShape.ts b/packages/data-context/src/data/coreDataShape.ts index 73613a8233..c23ec5ec8b 100644 --- a/packages/data-context/src/data/coreDataShape.ts +++ b/packages/data-context/src/data/coreDataShape.ts @@ -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 = {}): 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: { diff --git a/packages/data-context/src/data/index.ts b/packages/data-context/src/data/index.ts index 5b0716252f..218bed9a2c 100644 --- a/packages/data-context/src/data/index.ts +++ b/packages/data-context/src/data/index.ts @@ -3,7 +3,6 @@ export * from './CypressEnv' export * from './EventRegistrar' -export * from './LegacyPluginsIpc' export * from './ProjectConfigIpc' export * from './ProjectConfigManager' export * from './ProjectLifecycleManager' diff --git a/packages/data-context/src/sources/MigrationDataSource.ts b/packages/data-context/src/sources/MigrationDataSource.ts deleted file mode 100644 index 0d2d67790a..0000000000 --- a/packages/data-context/src/sources/MigrationDataSource.ts +++ /dev/null @@ -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 - e2e: Omit - 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 { - 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 { - 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) - }) - } -} diff --git a/packages/data-context/src/sources/ProjectDataSource.ts b/packages/data-context/src/sources/ProjectDataSource.ts index 8476c15c6f..77bd5bea9f 100644 --- a/packages/data-context/src/sources/ProjectDataSource.ts +++ b/packages/data-context/src/sources/ProjectDataSource.ts @@ -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[] diff --git a/packages/data-context/src/sources/index.ts b/packages/data-context/src/sources/index.ts index 7a9585f473..c7ab850f1b 100644 --- a/packages/data-context/src/sources/index.ts +++ b/packages/data-context/src/sources/index.ts @@ -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/' diff --git a/packages/data-context/src/sources/migration/autoRename.ts b/packages/data-context/src/sources/migration/autoRename.ts deleted file mode 100644 index 9d2df33727..0000000000 --- a/packages/data-context/src/sources/migration/autoRename.ts +++ /dev/null @@ -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 { - 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, - } -} diff --git a/packages/data-context/src/sources/migration/codegen.ts b/packages/data-context/src/sources/migration/codegen.ts deleted file mode 100644 index b0eb3ddb3a..0000000000 --- a/packages/data-context/src/sources/migration/codegen.ts +++ /dev/null @@ -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 - e2e: Record - component: Record -} - -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 - 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 = (await globby(watchPaths, { - cwd: projectRoot, - })).reduce>((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 { - return cfg.pluginsFile ? cfg.pluginsFile : await tryGetDefaultLegacyPluginsFile(projectRoot) -} - -async function createCypressConfig (config: ConfigOptions, pluginPath: string | undefined, options: CreateConfigOptions): Promise { - 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) { - 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 { - 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) { - 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) { - 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 { - 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 { - 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 (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) - } -} diff --git a/packages/data-context/src/sources/migration/format.ts b/packages/data-context/src/sources/migration/format.ts deleted file mode 100644 index ec81940588..0000000000 --- a/packages/data-context/src/sources/migration/format.ts +++ /dev/null @@ -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[] -} diff --git a/packages/data-context/src/sources/migration/index.ts b/packages/data-context/src/sources/migration/index.ts deleted file mode 100644 index a3b7b675ba..0000000000 --- a/packages/data-context/src/sources/migration/index.ts +++ /dev/null @@ -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' diff --git a/packages/data-context/src/sources/migration/legacyOptions.ts b/packages/data-context/src/sources/migration/legacyOptions.ts deleted file mode 100644 index c0cd4ba8da..0000000000 --- a/packages/data-context/src/sources/migration/legacyOptions.ts +++ /dev/null @@ -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 = [ - { - 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 = {}) => 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 = [ - { - 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 = [ - ...resolvedOptions, - ...runtimeOptions, -] diff --git a/packages/data-context/src/sources/migration/parserUtils.ts b/packages/data-context/src/sources/migration/parserUtils.ts deleted file mode 100644 index 7cdc987f81..0000000000 --- a/packages/data-context/src/sources/migration/parserUtils.ts +++ /dev/null @@ -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 -} diff --git a/packages/data-context/src/sources/migration/regexps.ts b/packages/data-context/src/sources/migration/regexps.ts deleted file mode 100644 index b160ab1fce..0000000000 --- a/packages/data-context/src/sources/migration/regexps.ts +++ /dev/null @@ -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))?[.])(?(?:[j|t]s[x]?|coffee|cjsx)' - -export const regexps = { - e2e: { - before: { - defaultFolderDefaultTestFiles: `cypress\/(?integration)\/(?.+?)(?${specExtRe})$`, - defaultFolderCustomTestFiles: `cypress\/(?integration)\/(?.+)$`, - customFolderDefaultTestFiles: `(?.+?)(?${specExtRe})$`, - }, - }, - component: { - before: { - defaultFolderDefaultTestFiles: `(?cypress\/component\/.+?)(?${specExtRe})`, - customFolderDefaultTestFiles: `(?.+?)(?${specExtRe})`, - }, - }, -} as const - -export const supportFileRegexps = { - e2e: { - beforeRegexp: 'cypress[\\\\/]support[\\\\/](?index)(?\.(?:[j|t]sx?|coffee))', - afterRegexp: 'cypress[\\\\/]support[\\\\/](?e2e)(?\.(?:[j|t]sx?|coffee))', - }, -} as const diff --git a/packages/data-context/src/sources/migration/shouldShowSteps.ts b/packages/data-context/src/sources/migration/shouldShowSteps.ts deleted file mode 100644 index 0d292f3257..0000000000 --- a/packages/data-context/src/sources/migration/shouldShowSteps.ts +++ /dev/null @@ -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 { - 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 { - 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 -} diff --git a/packages/data-context/src/sources/migration/utils.ts b/packages/data-context/src/util/files.ts similarity index 96% rename from packages/data-context/src/sources/migration/utils.ts rename to packages/data-context/src/util/files.ts index ff2e8e6693..5390e77009 100644 --- a/packages/data-context/src/sources/migration/utils.ts +++ b/packages/data-context/src/util/files.ts @@ -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 { - 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) diff --git a/packages/data-context/src/util/index.ts b/packages/data-context/src/util/index.ts index 564e2f7b9b..fb634a8c81 100644 --- a/packages/data-context/src/util/index.ts +++ b/packages/data-context/src/util/index.ts @@ -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' diff --git a/packages/data-context/src/util/urqlCacheKeys.ts b/packages/data-context/src/util/urqlCacheKeys.ts index d4a7644ada..a992c05a3a 100644 --- a/packages/data-context/src/util/urqlCacheKeys.ts +++ b/packages/data-context/src/util/urqlCacheKeys.ts @@ -22,11 +22,8 @@ export const urqlCacheKeys: Partial = { 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, diff --git a/packages/data-context/test/unit/actions/MigrationActions.spec.ts b/packages/data-context/test/unit/actions/MigrationActions.spec.ts deleted file mode 100644 index c591a2ff39..0000000000 --- a/packages/data-context/test/unit/actions/MigrationActions.spec.ts +++ /dev/null @@ -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 - ) - }) - }) -}) diff --git a/packages/data-context/test/unit/sources/migration/autoRename.spec.ts b/packages/data-context/test/unit/sources/migration/autoRename.spec.ts deleted file mode 100644 index 0569f98c85..0000000000 --- a/packages/data-context/test/unit/sources/migration/autoRename.spec.ts +++ /dev/null @@ -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) - }) - }) -}) diff --git a/packages/data-context/test/unit/sources/migration/codegen.spec.ts b/packages/data-context/test/unit/sources/migration/codegen.spec.ts deleted file mode 100644 index bd4714f4d2..0000000000 --- a/packages/data-context/test/unit/sources/migration/codegen.spec.ts +++ /dev/null @@ -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 = { - 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 = { - 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 - }) -}) diff --git a/packages/data-context/test/unit/sources/migration/format.spec.ts b/packages/data-context/test/unit/sources/migration/format.spec.ts deleted file mode 100644 index 133afa07ad..0000000000 --- a/packages/data-context/test/unit/sources/migration/format.spec.ts +++ /dev/null @@ -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 }, - ]) - }) - }) -}) diff --git a/packages/data-context/test/unit/sources/migration/resolveLegacyConfig.spec.ts b/packages/data-context/test/unit/sources/migration/resolveLegacyConfig.spec.ts deleted file mode 100644 index b39b248924..0000000000 --- a/packages/data-context/test/unit/sources/migration/resolveLegacyConfig.spec.ts +++ /dev/null @@ -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, - }) - }) -}) diff --git a/packages/data-context/test/unit/sources/migration/shouldShowSteps.spec.ts b/packages/data-context/test/unit/sources/migration/shouldShowSteps.spec.ts deleted file mode 100644 index 02705b573c..0000000000 --- a/packages/data-context/test/unit/sources/migration/shouldShowSteps.spec.ts +++ /dev/null @@ -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) - }) -}) diff --git a/packages/driver/cypress/e2e/commands/net_stubbing.cy.ts b/packages/driver/cypress/e2e/commands/net_stubbing.cy.ts index 25d08e915d..ac9394d132 100644 --- a/packages/driver/cypress/e2e/commands/net_stubbing.cy.ts +++ b/packages/driver/cypress/e2e/commands/net_stubbing.cy.ts @@ -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(() => { diff --git a/packages/driver/src/cy/net-stubbing/add-command.ts b/packages/driver/src/cy/net-stubbing/add-command.ts index 3237753c39..4284288d7e 100644 --- a/packages/driver/src/cy/net-stubbing/add-command.ts +++ b/packages/driver/src/cy/net-stubbing/add-command.ts @@ -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(', ')}`) diff --git a/packages/errors/__snapshot-html__/BROWSER_NOT_FOUND_BY_NAME - canary.html b/packages/errors/__snapshot-html__/BROWSER_NOT_FOUND_BY_NAME - canary.html index f3176b9f8e..8fc4dce047 100644 --- a/packages/errors/__snapshot-html__/BROWSER_NOT_FOUND_BY_NAME - canary.html +++ b/packages/errors/__snapshot-html__/BROWSER_NOT_FOUND_BY_NAME - canary.html @@ -60,9 +60,5 @@ - edge - edge:beta - edge:canary - - edge:dev - -Note: In Cypress version 4.0.0, Canary must be launched as chrome:canary, not canary. - -See https://on.cypress.io/migration-guide for more information on breaking changes in 4.0.0. + - edge:dev \ No newline at end of file diff --git a/packages/errors/__snapshot-html__/CANNOT_RECORD_NO_PROJECT_ID.html b/packages/errors/__snapshot-html__/CANNOT_RECORD_NO_PROJECT_ID.html index c682601b3f..b41d0bb115 100644 --- a/packages/errors/__snapshot-html__/CANNOT_RECORD_NO_PROJECT_ID.html +++ b/packages/errors/__snapshot-html__/CANNOT_RECORD_NO_PROJECT_ID.html @@ -36,7 +36,7 @@
You passed the --record flag but this project has not been setup to record.
 
-This project is missing the projectId inside of: /path/to/cypress.json
+This project is missing the projectId inside of: /path/to/cypress.config.js
 
 We cannot uniquely identify this project without this id.
 
diff --git a/packages/errors/__snapshot-html__/CLOUD_PROJECT_NOT_FOUND.html b/packages/errors/__snapshot-html__/CLOUD_PROJECT_NOT_FOUND.html
index aa52d1cbda..dd4b50111f 100644
--- a/packages/errors/__snapshot-html__/CLOUD_PROJECT_NOT_FOUND.html
+++ b/packages/errors/__snapshot-html__/CLOUD_PROJECT_NOT_FOUND.html
@@ -36,7 +36,7 @@
     
     
We could not find a Cypress Cloud project with the projectId: project-id-123
 
-This projectId came from your /path/to/cypress.json file or an environment variable.
+This projectId came from your /path/to/cypress.config.js file or an environment variable.
 
 Please log into Cypress Cloud and find your project.
 
diff --git a/packages/errors/__snapshot-html__/COMPONENT_FOLDER_REMOVED.html b/packages/errors/__snapshot-html__/COMPONENT_FOLDER_REMOVED.html
deleted file mode 100644
index 5a67e5f799..0000000000
--- a/packages/errors/__snapshot-html__/COMPONENT_FOLDER_REMOVED.html
+++ /dev/null
@@ -1,48 +0,0 @@
-
-    
-    
-      
-      
-    
-    
-    
-    
-  
-    
-    
The componentFolder configuration option is now invalid when set on the config object in Cypress version 10.0.0.
-
-It is now renamed to specPattern and configured separately as a component testing property: component.specPattern
-
-{
-  component: {
-    specPattern: '...',
-  },
-}
-
-https://on.cypress.io/migration-guide
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_DEV_START_EVENT.html b/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_DEV_START_EVENT.html deleted file mode 100644 index cd88c89454..0000000000 --- a/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_DEV_START_EVENT.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - -
To run component tests, Cypress needs you to configure the dev-server:start event.
-
-Please update this file: /path/to/plugins/file.js
-
-module.exports = (on, config) => {
-  on('dev-server:start', () => {
-    // start dev server here
-    return startDevServer(...)
-  }
-}
-
-https://on.cypress.io/component-testing
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_ROOT_CONFIG.html b/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_ROOT_CONFIG.html index 27000687e8..e4fd80194d 100644 --- a/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_ROOT_CONFIG.html +++ b/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_ROOT_CONFIG.html @@ -34,9 +34,9 @@ -
The specPattern configuration option is now invalid when set from the root of the config object in Cypress version 10.0.0.
+    
The specPattern configuration option is invalid when set from the root of the config object.
 
-It is now configured separately as a testing type property: e2e.specPattern and component.specPattern
+Set it within a testing type property: e2e.specPattern and component.specPattern
 
 {
   e2e: {
@@ -45,7 +45,5 @@
   component: {
     specPattern: '...',
   },
-}
-
-https://on.cypress.io/migration-guide
+}
 
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_ROOT_CONFIG_COMPONENT.html b/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_ROOT_CONFIG_COMPONENT.html index a2e8b2fe13..05a70bb0cd 100644 --- a/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_ROOT_CONFIG_COMPONENT.html +++ b/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_ROOT_CONFIG_COMPONENT.html @@ -34,15 +34,13 @@ -
The indexHtmlFile configuration option is now invalid when set from the root of the config object in Cypress version 10.0.0.
+    
The indexHtmlFile configuration option is invalid when set from the root of the config object.
 
-It is now configured separately as a testing type property: component.indexHtmlFile
+Set it within a testing type property: component.indexHtmlFile
 
 {
   component: {
     indexHtmlFile: '...',
   }
-}
-
-https://on.cypress.io/migration-guide
+}
 
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_ROOT_CONFIG_E2E.html b/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_ROOT_CONFIG_E2E.html index a1453f31d8..376eb5f580 100644 --- a/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_ROOT_CONFIG_E2E.html +++ b/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_ROOT_CONFIG_E2E.html @@ -34,15 +34,13 @@ -
The baseUrl configuration option is now invalid when set from the root of the config object in Cypress version 10.0.0.
+    
The baseUrl configuration option is invalid when set from the root of the config object.
 
-It is now configured separately as a testing type property: e2e.baseUrl
+Set it within a testing type property: e2e.baseUrl
 
 {
   e2e: {
     baseUrl: '...',
   }
-}
-
-https://on.cypress.io/migration-guide
+}
 
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_COMPONENT.html b/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_COMPONENT.html index bbcb84ee39..13fd6906b4 100644 --- a/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_COMPONENT.html +++ b/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_COMPONENT.html @@ -42,7 +42,5 @@ e2e: { baseUrl: '...', } -} - -https://on.cypress.io/migration-guide
+}
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_E2E.html b/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_E2E.html index 1dfb573ebd..7878f398fc 100644 --- a/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_E2E.html +++ b/packages/errors/__snapshot-html__/CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_E2E.html @@ -42,7 +42,5 @@ e2e: { indexHtmlFile: '...', } -} - -https://on.cypress.io/migration-guide
+}
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/CONFIG_FILE_MIGRATION_NEEDED.html b/packages/errors/__snapshot-html__/CONFIG_FILE_MIGRATION_NEEDED.html deleted file mode 100644 index 384c9e6a36..0000000000 --- a/packages/errors/__snapshot-html__/CONFIG_FILE_MIGRATION_NEEDED.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - -
There is a cypress.json file at the path: /path/to/projectRoot
-
-Cypress version 10.0.0 no longer supports cypress.json.
-
-Please run cypress open to launch the migration tool to migrate to cypress.config.{js,ts,mjs,cjs}.
-
-https://on.cypress.io/migration-guide
-
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/CONFIG_FILE_NOT_FOUND.html b/packages/errors/__snapshot-html__/CONFIG_FILE_NOT_FOUND.html index f2cf222723..fae8c30497 100644 --- a/packages/errors/__snapshot-html__/CONFIG_FILE_NOT_FOUND.html +++ b/packages/errors/__snapshot-html__/CONFIG_FILE_NOT_FOUND.html @@ -36,5 +36,5 @@
Could not find a Cypress configuration file.
 
-We looked but did not find a cypress.json file in this folder: /path/to/project/root
+We looked but did not find a cypress.config.js file in this folder: /path/to/project/root
 
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - invalidArray.html b/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - invalidArray.html index d9c965fe50..80078cd1b6 100644 --- a/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - invalidArray.html +++ b/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - invalidArray.html @@ -34,7 +34,7 @@ -
Your configFile at cypress.json set an invalid value:
+    
Your configFile at cypress.config.js set an invalid value:
 
 Expected defaultCommandTimeout to be a number.
 
diff --git a/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - invalidObject.html b/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - invalidObject.html
index c512125bd3..7bc565be88 100644
--- a/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - invalidObject.html	
+++ b/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - invalidObject.html	
@@ -34,7 +34,7 @@
     
   
     
-    
Your configFile at cypress.json set an invalid value:
+    
Your configFile at cypress.config.js set an invalid value:
 
 Expected defaultCommandTimeout to be a number.
 
diff --git a/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - invalidString.html b/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - invalidString.html
index cf809bf987..683e42e090 100644
--- a/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - invalidString.html	
+++ b/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - invalidString.html	
@@ -34,7 +34,7 @@
     
   
     
-    
Your configFile at cypress.json set an invalid value:
+    
Your configFile at cypress.config.js set an invalid value:
 
 Expected defaultCommandTimeout to be a number.
 
diff --git a/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - list.html b/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - list.html
index 8bb8df4611..5f9ae2353e 100644
--- a/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - list.html	
+++ b/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR - list.html	
@@ -34,7 +34,7 @@
     
   
     
-    
Your configFile at cypress.json set an invalid value:
+    
Your configFile at cypress.config.js set an invalid value:
 
 The error occurred while validating the browsers list.
 
diff --git a/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR.html b/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR.html
index 93838e9fc1..bffbd09b96 100644
--- a/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR.html
+++ b/packages/errors/__snapshot-html__/CONFIG_VALIDATION_ERROR.html
@@ -34,7 +34,7 @@
     
   
     
-    
Your configFile at cypress.json set an invalid value:
+    
Your configFile at cypress.config.js set an invalid value:
 
 Expected defaultCommandTimeout to be a number.
 
diff --git a/packages/errors/__snapshot-html__/CONFIG_VALIDATION_MSG_ERROR.html b/packages/errors/__snapshot-html__/CONFIG_VALIDATION_MSG_ERROR.html
index bd6b08ad78..6fce3bfc58 100644
--- a/packages/errors/__snapshot-html__/CONFIG_VALIDATION_MSG_ERROR.html
+++ b/packages/errors/__snapshot-html__/CONFIG_VALIDATION_MSG_ERROR.html
@@ -34,7 +34,7 @@
     
   
     
-    
Your configFile as cypress.json set an invalid value:
+    
Your configFile as cypress.config.js set an invalid value:
 
 `something` was not right
 
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/EXPERIMENTAL_COMPONENT_TESTING_REMOVED.html b/packages/errors/__snapshot-html__/EXPERIMENTAL_COMPONENT_TESTING_REMOVED.html deleted file mode 100644 index 349eec66a2..0000000000 --- a/packages/errors/__snapshot-html__/EXPERIMENTAL_COMPONENT_TESTING_REMOVED.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - -
The experimentalComponentTesting configuration option was removed in Cypress version 7.0.0.
-
-Please remove this flag from: /path/to/cypress.config.js
-
-Component Testing is now a supported testing type. You can run your component tests with:
-
-  $ cypress open --component
-
-https://on.cypress.io/migration-guide
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/EXPERIMENTAL_NETWORK_STUBBING_REMOVED.html b/packages/errors/__snapshot-html__/EXPERIMENTAL_NETWORK_STUBBING_REMOVED.html deleted file mode 100644 index c127cab16d..0000000000 --- a/packages/errors/__snapshot-html__/EXPERIMENTAL_NETWORK_STUBBING_REMOVED.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - -
The experimentalNetworkStubbing configuration option was removed in Cypress version 6.0.0.
-
-It is no longer necessary for using cy.intercept(). You can safely remove this option from your config.
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/EXPERIMENTAL_RUN_EVENTS_REMOVED.html b/packages/errors/__snapshot-html__/EXPERIMENTAL_RUN_EVENTS_REMOVED.html deleted file mode 100644 index d16b743fd4..0000000000 --- a/packages/errors/__snapshot-html__/EXPERIMENTAL_RUN_EVENTS_REMOVED.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - -
The experimentalRunEvents configuration option was removed in Cypress version 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.
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/EXPERIMENTAL_SAMESITE_REMOVED.html b/packages/errors/__snapshot-html__/EXPERIMENTAL_SAMESITE_REMOVED.html deleted file mode 100644 index 1aa0127b6c..0000000000 --- a/packages/errors/__snapshot-html__/EXPERIMENTAL_SAMESITE_REMOVED.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - -
The experimentalGetCookiesSameSite configuration option was removed in Cypress version 5.0.0.
-
-Returning the sameSite property is now the default behavior of the cy.cookie commands.
-
-You can safely remove this option from your config.
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/EXPERIMENTAL_SESSION_SUPPORT_REMOVED.html b/packages/errors/__snapshot-html__/EXPERIMENTAL_SESSION_SUPPORT_REMOVED.html deleted file mode 100644 index 4c61605dc5..0000000000 --- a/packages/errors/__snapshot-html__/EXPERIMENTAL_SESSION_SUPPORT_REMOVED.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - -
The experimentalSessionSupport configuration option was removed in Cypress version 9.6.0.
-
-You can safely remove this option from your config.
-
-https://on.cypress.io/session
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/EXPERIMENTAL_SHADOW_DOM_REMOVED.html b/packages/errors/__snapshot-html__/EXPERIMENTAL_SHADOW_DOM_REMOVED.html deleted file mode 100644 index 383ed62ad2..0000000000 --- a/packages/errors/__snapshot-html__/EXPERIMENTAL_SHADOW_DOM_REMOVED.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - -
The experimentalShadowDomSupport configuration option was removed in Cypress version 5.2.0. It is no longer necessary when utilizing the includeShadowDom option.
-
-You can safely remove this option from your config.
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/EXPERIMENTAL_STUDIO_REMOVED.html b/packages/errors/__snapshot-html__/EXPERIMENTAL_STUDIO_REMOVED.html deleted file mode 100644 index cfc2257dc3..0000000000 --- a/packages/errors/__snapshot-html__/EXPERIMENTAL_STUDIO_REMOVED.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - -
We're ending the experimental phase of Cypress Studio in Cypress version 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 experimentalStudio configuration option from your config.
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/FIREFOX_GC_INTERVAL_REMOVED.html b/packages/errors/__snapshot-html__/FIREFOX_GC_INTERVAL_REMOVED.html deleted file mode 100644 index 19925cc65c..0000000000 --- a/packages/errors/__snapshot-html__/FIREFOX_GC_INTERVAL_REMOVED.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - -
The firefoxGcInterval configuration option was removed in Cypress version 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 Cypress version 8.0.0, this option was removed.
-
-You can safely remove this option from your config.
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/INCOMPATIBLE_PLUGIN_RETRIES.html b/packages/errors/__snapshot-html__/INCOMPATIBLE_PLUGIN_RETRIES.html deleted file mode 100644 index 6b1a26de53..0000000000 --- a/packages/errors/__snapshot-html__/INCOMPATIBLE_PLUGIN_RETRIES.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - -
We've detected that the incompatible plugin cypress-plugin-retries is installed at: ./path/to/cypress-plugin-retries
-
-Test retries is now natively supported in Cypress version 5.0.0.
-
-Remove the plugin from your dependencies to silence this warning.
-
-https://on.cypress.io/test-retries
-
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/INTEGRATION_FOLDER_REMOVED.html b/packages/errors/__snapshot-html__/INTEGRATION_FOLDER_REMOVED.html deleted file mode 100644 index 03cdbee6c5..0000000000 --- a/packages/errors/__snapshot-html__/INTEGRATION_FOLDER_REMOVED.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - -
The integrationFolder configuration option is now invalid when set on the config object in Cypress version 10.0.0.
-
-It is now renamed to specPattern and configured separately as a end to end testing property: e2e.specPattern
-
-{
-  e2e: {
-    specPattern: '...',
-  },
-}
-
-https://on.cypress.io/migration-guide
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/LEGACY_CONFIG_ERROR_DURING_MIGRATION.html b/packages/errors/__snapshot-html__/LEGACY_CONFIG_ERROR_DURING_MIGRATION.html deleted file mode 100644 index 9e5ce57be9..0000000000 --- a/packages/errors/__snapshot-html__/LEGACY_CONFIG_ERROR_DURING_MIGRATION.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - -
Your cypress/plugins/index.js file threw an error. 
-
-Please ensure your pluginsFile is valid and relaunch the migration tool to migrate to Cypress version 10.0.0.
-
-
-Error: fail whale
-    at makeErr (cypress/packages/errors/test/unit/visualSnapshotErrors_spec.ts)
-    at LEGACY_CONFIG_ERROR_DURING_MIGRATION (cypress/packages/errors/test/unit/visualSnapshotErrors_spec.ts)
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/LEGACY_CONFIG_FILE.html b/packages/errors/__snapshot-html__/LEGACY_CONFIG_FILE.html deleted file mode 100644 index 2b8e82ac8a..0000000000 --- a/packages/errors/__snapshot-html__/LEGACY_CONFIG_FILE.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - -
There is both a cypress.json and a cypress.json file at the location below:
-
-/path/to/projectRoot
-
-Cypress no longer supports cypress.json, please remove it from your project.
-
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/MIGRATION_ALREADY_OCURRED.html b/packages/errors/__snapshot-html__/MIGRATION_ALREADY_OCURRED.html deleted file mode 100644 index f54683c0ab..0000000000 --- a/packages/errors/__snapshot-html__/MIGRATION_ALREADY_OCURRED.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - -
You are attempting to use Cypress with an older config file: custom.json
-When you upgraded to Cypress v10.0 the config file was updated and moved to a new location: custom.config.js
-
-You may need to update any CLI scripts to ensure that they are referring the new version. This would typically look something like:
-"cypress open --config-file=custom.config.js"
-
-https://on.cypress.io/migration-guide
-
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/MIGRATION_CYPRESS_NOT_FOUND.html b/packages/errors/__snapshot-html__/MIGRATION_CYPRESS_NOT_FOUND.html deleted file mode 100644 index 8f4e67f897..0000000000 --- a/packages/errors/__snapshot-html__/MIGRATION_CYPRESS_NOT_FOUND.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - -
You are running Cypress 10+ in global mode and attempting to open or migrate a project where an install of cypress cannot be found.
-
-Ensure that cypress@10 or greater is installed in the project you are attempting to open or migrate.
-
-https://on.cypress.io/migration-guide
-
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/MIGRATION_MISMATCHED_CYPRESS_VERSIONS.html b/packages/errors/__snapshot-html__/MIGRATION_MISMATCHED_CYPRESS_VERSIONS.html deleted file mode 100644 index ce18942189..0000000000 --- a/packages/errors/__snapshot-html__/MIGRATION_MISMATCHED_CYPRESS_VERSIONS.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - -
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.
-
-Ensure the project you are migrating has Cypress version Cypress version 10.0.0 installed.
-
-https://on.cypress.io/migration-guide
-
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/NO_PROJECT_ID.html b/packages/errors/__snapshot-html__/NO_PROJECT_ID.html index 971dabfdcf..cd58b21179 100644 --- a/packages/errors/__snapshot-html__/NO_PROJECT_ID.html +++ b/packages/errors/__snapshot-html__/NO_PROJECT_ID.html @@ -34,5 +34,5 @@ -
Can't find projectId in the config file: /path/to/project/cypress.json
+    
Can't find projectId in the config file: /path/to/project/cypress.config.js
 
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/PLUGINS_FILE_CONFIG_OPTION_REMOVED.html b/packages/errors/__snapshot-html__/PLUGINS_FILE_CONFIG_OPTION_REMOVED.html deleted file mode 100644 index 4251146476..0000000000 --- a/packages/errors/__snapshot-html__/PLUGINS_FILE_CONFIG_OPTION_REMOVED.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - -
The pluginsFile configuration option you have supplied has been replaced with setupNodeEvents.
-
-This new option is not a one-to-one correlation and it must be configured separately as a testing type property: e2e.setupNodeEvents and component.setupNodeEvents
-
-{
-  e2e: {
-    setupNodeEvents()
-  },
-  component: {
-    setupNodeEvents()
-  },
-}
-
-https://on.cypress.io/migration-guide
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/SETUP_NODE_EVENTS_DO_NOT_SUPPORT_DEV_SERVER.html b/packages/errors/__snapshot-html__/SETUP_NODE_EVENTS_DO_NOT_SUPPORT_DEV_SERVER.html deleted file mode 100644 index d1596bcbf2..0000000000 --- a/packages/errors/__snapshot-html__/SETUP_NODE_EVENTS_DO_NOT_SUPPORT_DEV_SERVER.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - -
Your configFile is invalid: /path/to/project/cypress.config.js
-
-Binding to the on('dev-server:start') event is no longer necessary.
-
-Please update your code to use the component.devServer() function.
-
-{
-  component: {
-    devServer (cypressDevServerConfig, devServerConfig) {
-      // start dev server here
-  }
-}
-
-Learn more: https://on.cypress.io/dev-server
-
-
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/TEST_FILES_RENAMED.html b/packages/errors/__snapshot-html__/TEST_FILES_RENAMED.html deleted file mode 100644 index 51a9f06917..0000000000 --- a/packages/errors/__snapshot-html__/TEST_FILES_RENAMED.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - -
The testFiles configuration option is now invalid when set on the config object in Cypress version 10.0.0.
-
-It is now renamed to specPattern and configured separately as a testing type property: e2e.specPattern or component.specPattern
-
-{
-  e2e: {
-    specPattern: '...',
-  },
-  component: {
-    specPattern: '...',
-  },
-}
-
-https://on.cypress.io/migration-guide
-
\ No newline at end of file diff --git a/packages/errors/src/errors.ts b/packages/errors/src/errors.ts index 0f0a9feb09..0129b234ad 100644 --- a/packages/errors/src/errors.ts +++ b/packages/errors/src/errors.ts @@ -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 || '' - - 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` diff --git a/packages/errors/test/unit/errors_spec.ts b/packages/errors/test/unit/errors_spec.ts index 724cf2d01a..2fa5fc56ef 100644 --- a/packages/errors/test/unit/errors_spec.ts +++ b/packages/errors/test/unit/errors_spec.ts @@ -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', () => { diff --git a/packages/errors/test/unit/visualSnapshotErrors_spec.ts b/packages/errors/test/unit/visualSnapshotErrors_spec.ts index 7b80e33287..c99e1bbd74 100644 --- a/packages/errors/test/unit/visualSnapshotErrors_spec.ts +++ b/packages/errors/test/unit/visualSnapshotErrors_spec.ts @@ -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']], diff --git a/packages/frontend-shared/cypress/support/mock-graphql/clientTestContext.ts b/packages/frontend-shared/cypress/support/mock-graphql/clientTestContext.ts index 0b5a9cdcd7..55519ef85f 100644 --- a/packages/frontend-shared/cypress/support/mock-graphql/clientTestContext.ts +++ b/packages/frontend-shared/cypress/support/mock-graphql/clientTestContext.ts @@ -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: {}, } } diff --git a/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Migration.ts b/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Migration.ts deleted file mode 100644 index 38e429b7bb..0000000000 --- a/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Migration.ts +++ /dev/null @@ -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 = { - __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, -} diff --git a/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Query.ts b/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Query.ts index 14dabf0b80..648a126b03 100644 --- a/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Query.ts +++ b/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Query.ts @@ -13,9 +13,6 @@ export const stubQuery: MaybeResolver = { wizard (source, args, ctx) { return ctx.wizard }, - migration (source, args, ctx) { - return ctx.migration - }, currentProject (source, args, ctx) { return ctx.currentProject }, diff --git a/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Registry.ts b/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Registry.ts index 384862cb86..3dec22ae32 100644 --- a/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Registry.ts +++ b/packages/frontend-shared/cypress/support/mock-graphql/stubgql-Registry.ts @@ -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, diff --git a/packages/frontend-shared/src/gql-components/HeaderBarContent.vue b/packages/frontend-shared/src/gql-components/HeaderBarContent.vue index 0820e23a9c..64c575848b 100644 --- a/packages/frontend-shared/src/gql-components/HeaderBarContent.vue +++ b/packages/frontend-shared/src/gql-components/HeaderBarContent.vue @@ -221,10 +221,6 @@ mutation GlobalPageHeader_clearCurrentProject { currentProject { id } - # This ensures the cache is updated with null after clearing project - migration { - configFileNameBefore - } } } ` diff --git a/packages/frontend-shared/src/locales/en-US.json b/packages/frontend-shared/src/locales/en-US.json index 15f7658290..fc7466b6b3 100644 --- a/packages/frontend-shared/src/locales/en-US.json +++ b/packages/frontend-shared/src/locales/en-US.json @@ -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", diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index 72a0045247..2c270f911e 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -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""" diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-CurrentProject.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-CurrentProject.ts index 9f80d0de88..724e8ad167 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-CurrentProject.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-CurrentProject.ts @@ -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) { diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-Migration.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-Migration.ts deleted file mode 100644 index 1ed9082a0b..0000000000 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-Migration.ts +++ /dev/null @@ -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 - }, - }) - }, -}) diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts index 3bd84c9a21..c0fbc5d9c4 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts @@ -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, diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-Query.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-Query.ts index df38bd5b40..3d867fcbc6 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-Query.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-Query.ts @@ -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', diff --git a/packages/graphql/src/schemaTypes/objectTypes/index.ts b/packages/graphql/src/schemaTypes/objectTypes/index.ts index 127bcb5d8a..994260fdca 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/index.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/index.ts @@ -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' diff --git a/packages/launchpad/README.md b/packages/launchpad/README.md index 31677558cb..c05f85bb1d 100644 --- a/packages/launchpad/README.md +++ b/packages/launchpad/README.md @@ -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. diff --git a/packages/launchpad/cypress/e2e/config-files-error-handling.cy.ts b/packages/launchpad/cypress/e2e/config-files-error-handling.cy.ts index 7a2c98fb88..cbac655cca 100644 --- a/packages/launchpad/cypress/e2e/config-files-error-handling.cy.ts +++ b/packages/launchpad/cypress/e2e/config-files-error-handling.cy.ts @@ -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 } }`) diff --git a/packages/launchpad/cypress/e2e/migration.cy.ts b/packages/launchpad/cypress/e2e/migration.cy.ts deleted file mode 100644 index 94d3e96610..0000000000 --- a/packages/launchpad/cypress/e2e/migration.cy.ts +++ /dev/null @@ -1,1662 +0,0 @@ -import type { ProjectFixtureDir } from '@tooling/system-tests' -import { getPathForPlatform } from './support/getPathForPlatform' - -// @ts-ignore -const platform = window.Cypress.platform - -const renameAutoStep = `[data-cy="migration-step renameAuto"]` -const renameManualStep = `[data-cy="migration-step renameManual"]` -const renameSupportStep = `[data-cy="migration-step renameSupport"]` -const configFileStep = `[data-cy="migration-step configFile"]` -const setupComponentStep = `[data-cy="migration-step setupComponent"]` - -declare global { - namespace Cypress { - interface Chainable { - waitForWizard(): Cypress.Chainable> - } - } -} - -Cypress.Commands.add('waitForWizard', () => { - return cy.get('[data-cy="migration-wizard"]') -}) - -function scaffoldAndVisitLaunchpad (project: ProjectFixtureDir, argv?: string[]) { - cy.scaffoldProject(project) - cy.openProject(project, argv) - cy.visitLaunchpad() -} - -function startMigrationFor (project: ProjectFixtureDir, argv?: string[]) { - scaffoldAndVisitLaunchpad(project, argv) - cy.waitForWizard() -} - -function skipCTMigration () { - cy.contains(`I'll do this later`).click() -} - -function migrateAndVerifyConfig (migratedConfigFile: string = 'cypress.config.js') { - cy.contains('Migrate the configuration for me').click() - - cy.withRetryableCtx(async (ctx, o) => { - const configStats = await ctx.file.checkIfFileExists(o.migratedConfigFile) - - expect(configStats).to.not.be.null.and.not.be.undefined - - const oldConfigStats = ctx.migration.legacyConfigFileExists() - - expect(oldConfigStats).to.be.false - - await ctx.actions.migration.assertSuccessfulConfigMigration(o.migratedConfigFile) - }, { migratedConfigFile }) -} - -function finishMigrationAndContinue () { - cy.contains('Finish migration and continue').click() -} - -function checkOutcome () { - cy.contains('Welcome to Cypress!', { timeout: 10000 }).should('be.visible') -} - -function runAutoRename () { - cy.get('button').contains('Rename these specs for me').click() -} - -function renameSupport (lang: 'js' | 'ts' | 'coffee' = 'js') { - cy.contains(`Rename the support file for me`).click() - - // give to to finish the file rename - cy.wait(200) - - cy.withCtx(async (ctx, { lang }) => { - expect( - await ctx.file.checkIfFileExists(ctx.path.join('cypress', 'support', `e2e.${lang}`)), 'support file not renamed', - ).not.to.be.null - }, { lang }) -} - -describe('global mode', () => { - it('migrates 2 projects in global mode', () => { - cy.openGlobalMode() - cy.addProject('migration-e2e-export-default') - cy.addProject('migration-e2e-custom-integration-with-projectId') - cy.visitLaunchpad() - - cy.withCtx(async (ctx, o) => { - o.sinon.stub(ctx.actions.migration, 'locallyInstalledCypressVersion').resolves((await ctx.versions.versionData()).current.version) - }) - - cy.contains('migration-e2e-export-default').click() - - // rename integration->e2e - cy.get(renameAutoStep, { timeout: 10000 }).should('exist') - cy.get(renameManualStep).should('not.exist') - - // cypress/support/index.ts -> cypress/support/e2e.ts - cy.get(renameSupportStep).should('exist') - // no component specs - cy.get(setupComponentStep).should('not.exist') - - cy.get(configFileStep).should('exist') - - runAutoRename() - renameSupport('ts') - migrateAndVerifyConfig('cypress.config.ts') - checkOutcome() - - cy.contains('Projects').click() - cy.contains('migration-e2e-custom-integration-with-projectId').click() - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('not.exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - renameSupport() - migrateAndVerifyConfig() - checkOutcome() - }) -}) - -describe('Opening unmigrated project', () => { - it('legacy project with --e2e', () => { - cy.scaffoldProject('migration') - cy.openProject('migration', ['--e2e']) - cy.visitLaunchpad() - cy.get('h1').should('contain', 'Migrating') - }) - - it('legacy project with --component', () => { - cy.scaffoldProject('migration-component-testing') - cy.openProject('migration-component-testing', ['--component']) - cy.visitLaunchpad() - cy.get('h1').should('contain', 'Migrating') - }) - - it('major version welcome page appears with correct links and can be dismissed', () => { - cy.scaffoldProject('migration') - cy.openProject('migration') - cy.visitLaunchpad({ showWelcome: true }) - - cy.contains(cy.i18n.majorVersionWelcome.title).should('be.visible') - - cy.validateExternalLink({ - name: cy.i18n.majorVersionWelcome.linkReleaseNotes, - href: 'https://on.cypress.io/changelog', - }) - - cy.validateExternalLink({ - name: '11.0.0', - href: 'https://on.cypress.io/changelog#11-0-0', - }) - - cy.validateExternalLink({ - name: '10.0.0', - href: 'https://on.cypress.io/changelog#10-0-0', - }) - - cy.contains('button', cy.i18n.majorVersionWelcome.actionContinue).click() - cy.contains(cy.i18n.majorVersionWelcome.title).should('not.exist') - cy.contains('h1', `Migrating to Cypress ${Cypress.version.split('.')[0]}`).should('be.visible') - - // Wait for migration prompt and current version to load before taking a snapshot - cy.get('.spinner').should('not.exist') - cy.findByTestId('top-nav-cypress-version-current-link').should('be.visible') - - cy.percySnapshot() - }) -}) - -describe('Full migration flow for each project', { retries: { openMode: 0, runMode: 2 }, defaultCommandTimeout: 10000 }, () => { - it('completes journey for migration-component-testing', () => { - startMigrationFor('migration-component-testing') - // custom testFiles - cannot auto - cy.get(renameAutoStep).should('not.exist') - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('not.exist') - cy.get(setupComponentStep).should('exist') - cy.get(configFileStep).should('exist') - - migrateAndVerifyConfig() - finishMigrationAndContinue() - checkOutcome() - }) - - it('completes journey for migration-component-testing-defaults', () => { - startMigrationFor('migration-component-testing-defaults') - // default testFiles - auto - cy.get(renameAutoStep).should('exist') - cy.get(renameManualStep).should('exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('not.exist') - cy.get(setupComponentStep).should('exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/component/button.spec.js') - cy.contains('cypress/component/input-spec.tsx') - - // after auto migration - cy.contains('cypress/component/button.cy.js') - cy.contains('cypress/component/input.cy.tsx') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = [ - 'cypress/component/button.cy.js', - 'cypress/component/input.cy.tsx', - ] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - skipCTMigration() - migrateAndVerifyConfig() - finishMigrationAndContinue() - checkOutcome() - }) - - describe('migration-e2e-component-default-everything', () => { - it('completes journey for migration-e2e-component-default-everything', () => { - startMigrationFor('migration-e2e-component-default-everything') - // default testFiles - auto - cy.get(renameAutoStep).should('exist') - cy.get(renameManualStep).should('exist') - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.ts') - cy.contains('cypress/integration/spec.ts') - cy.contains('cypress/component/button.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.ts') - cy.contains('cypress/e2e/spec.cy.ts') - cy.contains('cypress/component/button.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = [ - 'cypress/e2e/foo.cy.ts', - 'cypress/component/button.cy.js', - ] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - skipCTMigration() - renameSupport() - migrateAndVerifyConfig() - finishMigrationAndContinue() - - cy.withRetryableCtx(async (ctx) => { - const integrationFolderStats = await ctx.file.checkIfFileExists(ctx.path.join('cypress', 'integration')) - - expect(integrationFolderStats).to.be.null - }) - - checkOutcome() - }) - - it('renames only the folder renaming migration-e2e-defaults-rename-folder-only', () => { - startMigrationFor('migration-e2e-defaults-rename-folder-only') - // default testFiles - auto - cy.get(renameAutoStep).should('exist') - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - - // after auto migration - - cy.get('[data-cy="migrate-before"]').within(() => { - cy.contains('cypress/integration/foo.spec.js') - cy.get('.text-red-500').should('contain', 'spec') - }) - - cy.get('[data-cy="migrate-after"]').within(() => { - cy.contains('cypress/e2e/foo.cy.js') - cy.get('.text-jade-500').should('contain', 'cy') - }) - - cy.findByText('change').click() - - // this project has a default integration folder and default testFiles. - // We rename the integration folder, even if the user skips the spec rename - cy.findByText('Rename folder only.').click() - - cy.findByText('Save changes').click() - - cy.percySnapshot() - - cy.get('[data-cy="migrate-before"]').within(() => { - cy.get('.text-red-500').should('not.contain', 'spec') - }) - - cy.get('[data-cy="migrate-after"]').within(() => { - cy.get('.text-jade-500').should('not.contain', 'cy') - cy.get('.text-jade-500').should('not.contain', 'spec') - }) - - cy.findByText('Rename the folder for me').click() - - cy.withRetryableCtx(async (ctx) => { - const specs = [ - 'cypress/e2e/foo.spec.js', - ] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - renameSupport() - migrateAndVerifyConfig() - checkOutcome() - }) - }) - - it('completes journey for migration-e2e-component-with-json-files', () => { - startMigrationFor('migration-e2e-component-with-json-files') - // default testFiles - auto - cy.get(renameAutoStep).should('exist') - cy.get(renameManualStep).should('exist') - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.ts') - cy.contains('cypress/integration/spec.ts') - cy.contains('cypress/component/button.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.ts') - cy.contains('cypress/e2e/spec.cy.ts') - cy.contains('cypress/component/button.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = [ - 'cypress/e2e/foo.cy.ts', - 'cypress/component/button.cy.js', - ] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - skipCTMigration() - renameSupport() - migrateAndVerifyConfig() - finishMigrationAndContinue() - - cy.withCtx(async (ctx) => { - const integrationFolderStats = await ctx.file.checkIfFileExists(ctx.path.join('cypress', 'integration')) - - expect(integrationFolderStats).to.be.null - }) - - checkOutcome() - }) - - it('completes journey for migration-e2e-component-default-with-types', () => { - startMigrationFor('migration-e2e-component-default-with-types') - // default testFiles - auto - cy.get(renameAutoStep).should('exist') - cy.get(renameManualStep).should('exist') - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.ts') - cy.contains('cypress/integration/spec.ts') - cy.contains('cypress/component/button.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.ts') - cy.contains('cypress/e2e/spec.cy.ts') - cy.contains('cypress/component/button.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = [ - 'cypress/e2e/foo.cy.ts', - 'cypress/component/button.cy.js', - ] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - skipCTMigration() - renameSupport() - migrateAndVerifyConfig() - finishMigrationAndContinue() - - cy.withRetryableCtx(async (ctx) => { - const integrationFolderStats = await ctx.file.checkIfFileExists(ctx.path.join('cypress', 'integration')) - - expect(integrationFolderStats).to.be.null - }) - - checkOutcome() - }) - - it('completes journey for migration-e2e-custom-integration', () => { - startMigrationFor('migration-e2e-custom-integration') - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('src/basic.spec.js') - - // after auto migration - cy.contains('src/basic.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['src/basic.cy.js'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - renameSupport() - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-custom-supportFile', () => { - startMigrationFor('migration-e2e-custom-supportFile') - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is custom - cannot rename - cy.get(renameSupportStep).should('not.exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/basic.spec.js') - - // after auto migration - cy.contains('cypress/e2e/basic.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/basic.cy.js'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-custom-supportFile-default-value', () => { - startMigrationFor('migration-e2e-custom-supportFile-default-value') - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/basic.spec.js') - - // after auto migration - cy.contains('cypress/e2e/basic.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/basic.cy.js'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - renameSupport() - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-custom-integration-default-value', () => { - startMigrationFor('migration-e2e-custom-integration-default-value') - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/basic.spec.js') - - // after auto migration - cy.contains('cypress/e2e/basic.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/basic.cy.js'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - renameSupport() - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-custom-integration-default-value-and-testFiles', () => { - startMigrationFor('migration-e2e-custom-integration-default-value-and-testFiles') - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/basic.spec.js') - - // after auto migration - cy.contains('cypress/e2e/basic.spec.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/basic.spec.js'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - renameSupport() - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-custom-integration-with-projectId', () => { - startMigrationFor('migration-e2e-custom-integration-with-projectId') - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('not.exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - renameSupport() - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-custom-test-files', () => { - const project = 'migration-e2e-custom-test-files-array' - - startMigrationFor(project) - // default integration but custom testFiles - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - cy.scaffoldProject(project) - cy.openProject(project) - cy.visitLaunchpad() - - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/basic.test.js') - cy.contains('cypress/integration/basic.spec.js') - - // after auto migration - cy.contains('cypress/e2e/basic.test.js') - cy.contains('cypress/e2e/basic.spec.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/basic.test.js', 'cypress/e2e/basic.spec.js'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - renameSupport() - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-defaults', () => { - startMigrationFor('migration-e2e-defaults') - // defaults, rename all the things - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/foo.cy.js'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats, `spec file not renamed ${spec}`).to.not.be.null - } - }) - - renameSupport('ts') - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-defaults-with-projectId', () => { - startMigrationFor('migration-e2e-defaults-with-projectId') - // defaults, rename all the things - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.spec.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/foo.spec.js'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats, `spec file not renamed ${spec}`).to.not.be.null - } - }) - - renameSupport('ts') - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-defaults-with-nested-projectId', () => { - startMigrationFor('migration-e2e-defaults-with-nested-projectId') - // defaults, rename all the things - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.spec.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/foo.spec.js'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats, `spec file not renamed ${spec}`).to.not.be.null - } - }) - - renameSupport('ts') - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-module', () => { - startMigrationFor('migration-e2e-module') - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/foo.cy.js'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats, `spec file not renamed ${spec}`).to.not.be.null - } - }) - - renameSupport('js') - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-coffeescript', () => { - startMigrationFor('migration-e2e-coffeescript') - // defaults, rename all the things - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.coffee') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.coffee') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/foo.cy.coffee'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats, `spec file not renamed ${spec}`).to.not.be.null - } - }) - - renameSupport('coffee') - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-cjsx', () => { - startMigrationFor('migration-e2e-cjsx') - // defaults, rename all the things - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.cjsx') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.cjsx') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/foo.cy.cjsx'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats, `spec file not renamed ${spec}`).to.not.be.null - } - }) - - renameSupport() - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-plugins-modify-config', () => { - startMigrationFor('migration-e2e-plugins-modify-config') - // No rename, integrationFolder and testFiles are custom (via plugins) - cy.get(renameAutoStep).should('not.exist') - // no CT - cy.get(renameManualStep).should('not.exist') - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - renameSupport() - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-no-plugins-support-file', () => { - startMigrationFor('migration-e2e-no-plugins-support-file') - // defaults, rename all the things - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // no supportFile - cy.get(renameSupportStep).should('not.exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('not.exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.js') - - runAutoRename() - - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-false-plugins-support-file', () => { - startMigrationFor('migration-e2e-false-plugins-support-file') - // defaults, rename all the things - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // no supportFile - cy.get(renameSupportStep).should('not.exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('not.exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.js') - - runAutoRename() - - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-plugins-implicit-index-js', () => { - startMigrationFor('migration-e2e-plugins-implicit-index-js') - // no specs, nothing to rename? - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - runAutoRename() - renameSupport() - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-e2e-fully-custom', () => { - startMigrationFor('migration-e2e-fully-custom') - // integration folder and testFiles are custom, cannot rename anything - cy.get(renameAutoStep).should('not.exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is custom - cannot rename - cy.get(renameSupportStep).should('not.exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - migrateAndVerifyConfig() - checkOutcome() - }) - - it('completes journey for migration-component-testing-customized', () => { - startMigrationFor('migration-component-testing-customized') - // cannot rename anything automatically here, testFiles are customized - cy.get(renameAutoStep).should('not.exist') - - cy.get(renameManualStep).should('not.exist') - - // no supportFile rename for CT - cy.get(renameSupportStep).should('not.exist') - - cy.get(setupComponentStep).should('exist') - cy.get(configFileStep).should('exist') - - migrateAndVerifyConfig() - finishMigrationAndContinue() - checkOutcome() - }) - - it('completes journey for migration-e2e-export-default', () => { - startMigrationFor('migration-e2e-export-default') - // rename integration->e2e - cy.get(renameAutoStep).should('exist') - cy.get(renameManualStep).should('not.exist') - - // cypress/support/index.ts -> cypress/support/e2e.ts - cy.get(renameSupportStep).should('exist') - // no component specs - cy.get(setupComponentStep).should('not.exist') - - cy.get(configFileStep).should('exist') - - runAutoRename() - renameSupport('ts') - migrateAndVerifyConfig('cypress.config.ts') - checkOutcome() - }) - - it('completes journey for migration-typescript-project', () => { - startMigrationFor('migration-typescript-project') - // defaults, rename all the things - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.js') - - runAutoRename() - - cy.withRetryableCtx((ctx) => { - ['cypress/e2e/foo.cy.js'].forEach(async (spec) => { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - }) - }) - - renameSupport() - migrateAndVerifyConfig('cypress.config.ts') - checkOutcome() - }) - - it('handles re-migrating a partially migrated codebase', { retries: 0 }, () => { - startMigrationFor('migration-specs-already-migrated') - cy.get(renameAutoStep).should('not.exist') - - cy.withRetryableCtx(async (ctx) => { - const specs = [ - 'cypress/tests/foo.cy.js', - ] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - renameSupport('ts') - migrateAndVerifyConfig() - }) - - it('completes journey for migration-e2e-duplicated-spec-names', () => { - startMigrationFor('migration-e2e-duplicated-spec-names') - // default testFiles - auto - cy.get(renameAutoStep).should('exist') - cy.get(configFileStep).should('exist') - - cy.get('[data-cy="migrate-before"]').within(() => { - cy.get('code').eq(0).should('contain', 'cypress/integration/app-spec2.js') - cy.get('code').eq(1).should('contain', 'cypress/integration/app_spec2.js') - cy.get('code').eq(2).should('contain', 'cypress/integration/app.spec.js') - cy.get('code').eq(3).should('contain', 'cypress/integration/app2_spec.js') - cy.get('code').eq(4).should('contain', 'cypress/integration/app_spec.js') - cy.get('code').eq(5).should('contain', 'cypress/integration/app-spec.js') - }) - - cy.get('[data-cy="migrate-after"]').within(() => { - cy.get('code').eq(0).should('contain', 'cypress/e2e/app-spec2.cy.js') - cy.get('code').eq(1).should('contain', 'cypress/e2e/app_spec2.cy.js') - cy.get('code').eq(2).should('contain', 'cypress/e2e/app.cy.js') - cy.get('code').eq(3).should('contain', 'cypress/e2e/app2.cy.js') - cy.get('code').eq(4).should('contain', 'cypress/e2e/app_spec.cy.js') - cy.get('code').eq(5).should('contain', 'cypress/e2e/app-spec.cy.js') - }) - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = [ - 'cypress/e2e/app-spec2.cy.js', - 'cypress/e2e/app_spec2.cy.js', - 'cypress/e2e/app.cy.js', - 'cypress/e2e/app2.cy.js', - 'cypress/e2e/app_spec.cy.js', - 'cypress/e2e/app-spec.cy.js', - ] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - migrateAndVerifyConfig() - - cy.withCtx(async (ctx) => { - const integrationFolderStats = await ctx.file.checkIfFileExists(ctx.path.join('cypress', 'integration')) - - expect(integrationFolderStats).to.be.null - }) - - checkOutcome() - }) - - context('migration-e2e-component-default-test-files', () => { - it('completes journey', () => { - startMigrationFor('migration-e2e-component-default-test-files') - // default testFiles - auto - cy.get(renameAutoStep).should('exist') - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/custom-integration/foo.spec.ts') - cy.contains('cypress/custom-component/button.spec.js') - - // after auto migration - cy.contains('cypress/custom-integration/foo.cy.ts') - cy.contains('cypress/custom-component/button.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = [ - 'cypress/custom-integration/foo.cy.ts', - 'cypress/custom-component/button.cy.js', - ] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - renameSupport() - migrateAndVerifyConfig() - finishMigrationAndContinue() - }) - - it('skips the file renaming', () => { - startMigrationFor('migration-e2e-component-default-test-files') - // default testFiles - auto - cy.get(renameAutoStep).should('exist') - // non default component folder - should skip. - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/custom-integration/foo.spec.ts') - cy.contains('cypress/custom-component/button.spec.js') - - // after auto migration - cy.contains('cypress/custom-integration/foo.cy.ts') - cy.contains('cypress/custom-component/button.cy.js') - - cy.findByText('change').click() - - cy.contains('I may need to change my specPattern later').should('not.exist') - cy.findByText('Don\'t rename anything — keep what I have.').click() - cy.contains('I may need to change my specPattern later') - - cy.findByText('Save changes').click() - - cy.findByText('Skip renaming specs').click() - - cy.withRetryableCtx(async (ctx) => { - const specs = [ - 'cypress/custom-integration/foo.spec.ts', - 'cypress/custom-component/button.spec.js', - ] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats).to.not.be.null - } - }) - - renameSupport() - migrateAndVerifyConfig() - }) - }) - - it('completes journey for migration-e2e-legacy-plugins-throws-error and recovers', () => { - scaffoldAndVisitLaunchpad('migration-e2e-legacy-plugins-throws-error') - // no steps are shown - we show the error that surfaced when executing pluginsFile. - cy.get(renameAutoStep).should('not.exist') - cy.get(renameManualStep).should('not.exist') - cy.get(renameSupportStep).should('not.exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('not.exist') - - cy.contains(cy.i18n.launchpadErrors.generic.configErrorTitle) - // correct location of error - const pluginsPath = platform === 'win32' ? 'cypress\\plugins\\index.js:2:9' : 'cypress/plugins/index.js:2:9' - - cy.get('[data-testid="error-code-frame"]').contains(pluginsPath) - // correct error from pluginsFile - cy.contains(`throw Error('Uh oh, there was an error!')`) - - cy.withCtx(async (ctx, o) => { - await ctx.actions.file.writeFileInProject(o.path, 'module.exports = (on, config) => {}') - }, { path: getPathForPlatform('cypress/plugins/index.js') }) - - cy.findByRole('button', { name: 'Try again' }).click() - - cy.waitForWizard() - }) - - it('completes journey for migration-e2e-with-extra-files', () => { - startMigrationFor('migration-e2e-with-extra-files') - // defaults, rename all the things - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const files = ['cypress/e2e/foo.cy.js', 'cypress/e2e/example.json'] - - for (const file of files) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(file)) - - expect(stats, `file ${file}`).to.not.be.null - } - }) - - renameSupport('ts') - migrateAndVerifyConfig() - checkOutcome() - }) -}) - -// TODO: UNIFY-1350 toLaunchpad emitter not working in Cypress in Cypress, -// re-evaluate after conversion to subscriptions -describe.skip('component testing migration - defaults', () => { - it('live update migration UI as user moves files', () => { - cy.scaffoldProject('migration-component-testing-customized') - cy.openProject('migration-component-testing-customized') - cy.visitLaunchpad() - cy.waitForWizard() - - // need to move your specs before this button shows - cy.get('button').contains('I have moved my component specs').should('not.exist') - - // two files to move, src/button.spec.js and src/input-spec.tsx. - cy.withCtx((ctx) => { - return ctx.actions.file.moveFileInProject('src/button.spec.js', 'src/button.cy.js') - }).then(() => { - cy.get('[data-cy="moved"]').contains('src/button.spec.js') - }) - - cy.withCtx((ctx) => { - return ctx.actions.file.moveFileInProject('src/input-spec.tsx', 'src/input.cy.tsx') - }).then(() => { - cy.get('[data-cy="moved"]').contains('src/input-spec.tsx') - }) - - cy.get('button').contains('I have moved my component specs') - }) - - it('live update migration UI as user moves files', () => { - cy.scaffoldProject('migration-component-testing-customized') - cy.openProject('migration-component-testing-customized') - cy.visitLaunchpad() - cy.waitForWizard() - - // need to move your specs before this button shows - cy.get('button').contains('I have moved my component specs').should('not.exist') - - // two files to move, src/button.spec.js and src/input-spec.tsx. - cy.withCtx((ctx) => { - return ctx.actions.file.moveFileInProject('src/button.spec.js', 'src/button.cy.js') - }).then(() => { - cy.get('[data-cy="moved"]').contains('src/button.spec.js') - }) - - cy.withCtx((ctx) => { - return ctx.actions.file.moveFileInProject('src/input-spec.tsx', 'src/input.cy.tsx') - }).then(() => { - cy.get('[data-cy="moved"]').contains('src/input-spec.tsx') - }) - - cy.get('button').contains('I have moved my component specs') - }) -}) - -describe('Migration', { viewportWidth: 1200, retries: { openMode: 0, runMode: 2 } }, () => { - it('should create the cypress.config.js file and delete old config', () => { - startMigrationFor('migration') - - // all steps - cy.get(renameAutoStep).should('exist') - cy.get(renameManualStep).should('not.exist') - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('exist') - cy.get(configFileStep).should('exist') - - cy.get('button').contains('Rename these specs for me').scrollIntoView().click() - cy.findByText('Rename the support file for me').click() - cy.findByText('Migrate the configuration for me').click() - - cy.withRetryableCtx(async (ctx) => { - const configStats = await ctx.file.checkIfFileExists('cypress.config.js') - - expect(configStats).to.not.be.null.and.not.be.undefined - - const oldConfigStats = await ctx.file.checkIfFileExists('cypress.json') - - expect(oldConfigStats).to.be.null - - await ctx.actions.migration.assertSuccessfulConfigMigration() - }) - - finishMigrationAndContinue() - checkOutcome() - }) - - it('should show spec pattern rename change modal', () => { - startMigrationFor('migration') - - cy.get(renameAutoStep).should('exist') - cy.get(renameManualStep).should('not.exist') - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('exist') - cy.get(configFileStep).should('exist') - - cy.findByText('change').click() - cy.get('h2').should('contain', 'Change the existing spec file extension') - cy.get('button').get('[aria-label="Close"]').click() - cy.get('h2').should('not.contain', 'Change the existing spec file extension') - - cy.findByText('change').click() - cy.get('h2').should('contain', 'Change the existing spec file extension') - cy.get(renameAutoStep).click({ force: true }) - cy.get('h2').should('not.contain', 'Change the existing spec file extension') - - cy.findByText('change').click() - cy.get('h2').should('contain', 'Change the existing spec file extension') - cy.get('button').contains('Save changes').click() - cy.get('h2').should('not.contain', 'Change the existing spec file extension') - - cy.findByText('change').click() - cy.get('h2').should('contain', 'Change the existing spec file extension') - cy.get('button').contains('Cancel').click() - cy.get('h2').should('not.contain', 'Change the existing spec file extension') - }) - - it('shows error if plugins file throws an error', () => { - scaffoldAndVisitLaunchpad('migration-e2e-plugins-throw-error') - - cy.contains(`${getPathForPlatform('cypress/plugins/index.js')} file threw an error.`) - cy.contains('Please ensure your pluginsFile is valid and relaunch the migration tool to migrate to Cypress version 10.0.0.') - cy.contains('throw new Error(\'New error from plugin\')') - }) -}) - -describe('Migrate custom config files', () => { - it('completes journey for migration-custom-config-file-root-level spaces', () => { - startMigrationFor('migration-custom-config-file-root-level spaces', ['--config-file', 'customConfig.json']) - - // defaults, rename all the things - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/foo.cy.js'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats, `spec file not renamed ${spec}`).to.not.be.null - } - }) - - renameSupport('ts') - - cy.contains('customConfig.json') - cy.contains('customConfig.config.js') - - migrateAndVerifyConfig('customConfig.config.js') - checkOutcome() - }) - - it('completes journey for migration-custom-config-file-respect-pathname', () => { - startMigrationFor('migration-custom-config-file-respect-pathname', ['--config-file', 'cypress.foo.json']) - - // defaults, rename all the things - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/foo.cy.js'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats, `spec file not renamed ${spec}`).to.not.be.null - } - }) - - renameSupport('ts') - - cy.contains('cypress.foo.json') - cy.contains('cypress.foo.config.js') - - migrateAndVerifyConfig('cypress.foo.config.js') - checkOutcome() - }) - - it('completes journey for migration-custom-config-file-respect-dirname', () => { - startMigrationFor('migration-custom-config-file-respect-dirname', ['--config-file', 'config/cypress.foo.json']) - - // defaults, rename all the things - // can rename integration->e2e - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // default testFiles but custom integration - can rename automatically - cy.get(renameAutoStep).should('exist') - // no CT - cy.get(renameManualStep).should('not.exist') - // supportFile is false - cannot migrate - cy.get(renameSupportStep).should('exist') - cy.get(setupComponentStep).should('not.exist') - cy.get(configFileStep).should('exist') - - // Migration workflow - // before auto migration - cy.contains('cypress/integration/foo.spec.js') - - // after auto migration - cy.contains('cypress/e2e/foo.cy.js') - - runAutoRename() - - cy.withRetryableCtx(async (ctx) => { - const specs = ['cypress/e2e/foo.cy.js'] - - for (const spec of specs) { - const stats = await ctx.file.checkIfFileExists(ctx.path.join(spec)) - - expect(stats, `spec file not renamed ${spec}`).to.not.be.null - } - }) - - renameSupport('ts') - - cy.contains('config/cypress.foo.json') - cy.contains('config/cypress.foo.config.js') - - migrateAndVerifyConfig('config/cypress.foo.config.js') - checkOutcome() - }) - - it('shows error for migration-custom-config-file-migration-already-ocurred', () => { - scaffoldAndVisitLaunchpad('migration-custom-config-file-migration-already-ocurred', ['--config-file', 'customConfig.json']) - - cy.contains('You are attempting to use Cypress with an older config file: customConfig.json') - cy.contains('When you upgraded to Cypress v10.0 the config file was updated and moved to a new location: customConfig.config.js') - }) - - it('shows error for migration-custom-config-file-with-existing-v10-config-file', () => { - scaffoldAndVisitLaunchpad('migration-custom-config-file-with-existing-v10-config-file', ['--config-file', 'customConfig.json']) - - cy.contains('There is both a customConfig.config.js and a customConfig.json file at the location below:') - cy.contains('Cypress no longer supports customConfig.json, please remove it from your project.') - }) - - it('shows error if plugins file do not exist', () => { - scaffoldAndVisitLaunchpad('migration', ['--config-file', 'erroredConfigFiles/incorrectPluginsFile.json']) - - const err = `Looked for pluginsFile at foo/bar, but it was not found.` - - cy.contains(err) - }) -}) diff --git a/packages/launchpad/src/Main.vue b/packages/launchpad/src/Main.vue index 4e9e0b0912..f60256f5aa 100644 --- a/packages/launchpad/src/Main.vue +++ b/packages/launchpad/src/Main.vue @@ -23,9 +23,6 @@ v-else-if="query.data.value.isGlobalMode && !query.data.value?.currentProject" :gql="query.data.value" /> -