fix: config migration when --config-file flag is set (#20470)

* fix: config migration when --config-file flag is set

* Fix/add tests

* Update migration for config migrations that already ocurred

* Fix TS, fix test

* Update comment

* Refactor code

* Update error, prevent clicking the project on topNav

* Fix TS

* Fix error - update error message

* Update test

* Refactor code / add test for legacy config

* Refactor code

* Update with feedback

* Update with code review
This commit is contained in:
Alejandro Estrada
2022-03-09 17:11:39 -05:00
committed by GitHub
parent 59ba5c764e
commit 7636287eee
49 changed files with 488 additions and 50 deletions
@@ -16,13 +16,19 @@ export class MigrationActions {
async createConfigFile () {
const config = await this.ctx.migration.createConfigString()
this.ctx.lifecycleManager.setConfigFilePath(this.ctx.migration.configFileNameAfterMigration)
await this.ctx.fs.writeFile(this.ctx.lifecycleManager.configFilePath, config).catch((error) => {
throw error
})
await this.ctx.actions.file.removeFileInProject('cypress.json').catch((error) => {
await this.ctx.actions.file.removeFileInProject(this.ctx.lifecycleManager.legacyConfigFile).catch((error) => {
throw error
})
// @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
}
initialize () {
@@ -116,9 +122,11 @@ export class MigrationActions {
await this.ctx.migration.closeManualRenameWatcher()
}
async assertSuccessfulConfigMigration (configExtension: 'js' | 'ts' | 'coffee' = 'js') {
const actual = formatConfig(await this.ctx.actions.file.readFileInProject(`cypress.config.${configExtension}`))
const expected = formatConfig(await this.ctx.actions.file.readFileInProject(`expected-cypress.config.${configExtension}`))
async assertSuccessfulConfigMigration (migratedConfigFile: string = 'cypress.config.js') {
const actual = formatConfig(await this.ctx.actions.file.readFileInProject(migratedConfigFile))
const configExtension = path.extname(migratedConfigFile)
const expected = formatConfig(await this.ctx.actions.file.readFileInProject(`expected-cypress.config${configExtension}`))
if (actual !== expected) {
throw Error(`Expected ${actual} to equal ${expected}`)
@@ -251,7 +251,7 @@ export class WizardActions {
const configCode = this.configCode(testingType, this.ctx.coreData.wizard.chosenLanguage)
// only do this if config file doesn't exist
this.ctx.lifecycleManager.setConfigFilePath(this.ctx.coreData.wizard.chosenLanguage)
this.ctx.lifecycleManager.setConfigFilePath(`cypress.config.${this.ctx.coreData.wizard.chosenLanguage}`)
return this.scaffoldFile(
this.ctx.lifecycleManager.configFilePath,
@@ -158,7 +158,15 @@ export class ProjectLifecycleManager {
}
get legacyJsonPath () {
return path.join(this.configFilePath, 'cypress.json')
return path.join(this.configFilePath, this.legacyConfigFile)
}
get legacyConfigFile () {
if (this.ctx.modeOptions.configFile && this.ctx.modeOptions.configFile.endsWith('.json')) {
return this.ctx.modeOptions.configFile
}
return 'cypress.json'
}
get configFile () {
@@ -209,6 +217,12 @@ export class ProjectLifecycleManager {
return path.basename(this.projectRoot)
}
async checkIfLegacyConfigFileExist () {
const legacyConfigFileExist = await this.ctx.deref.actions.file.checkIfFileExists(this.legacyConfigFile)
return Boolean(legacyConfigFileExist)
}
clearCurrentProject () {
this.resetInternalState()
this._initializedProject = undefined
@@ -594,7 +608,7 @@ export class ProjectLifecycleManager {
}
const legacyFileWatcher = this.addWatcher([
this._pathToFile('cypress.json'),
this._pathToFile(this.legacyConfigFile),
this._pathToFile('cypress.config.js'),
this._pathToFile('cypress.config.ts'),
])
@@ -1067,7 +1081,7 @@ export class ProjectLifecycleManager {
const configFile = this.ctx.modeOptions.configFile
const metaState: ProjectMetaState = {
...PROJECT_META_STATE,
hasLegacyCypressJson: fs.existsSync(this._pathToFile('cypress.json')),
hasLegacyCypressJson: fs.existsSync(this._pathToFile(this.legacyConfigFile)),
hasCypressEnvFile: fs.existsSync(this._pathToFile('cypress.env.json')),
}
@@ -1097,6 +1111,16 @@ export class ProjectLifecycleManager {
metaState.hasSpecifiedConfigViaCLI = this._pathToFile(configFile)
if (configFile.endsWith('.json')) {
metaState.needsCypressJsonMigration = true
const configFileNameAfterMigration = configFile.replace('.json', `.config.${metaState.hasTypescript ? '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._configFilePath = this._pathToFile(configFile)
if (fs.existsSync(this._configFilePath)) {
@@ -1114,7 +1138,7 @@ export class ProjectLifecycleManager {
if (fs.existsSync(configFileTs)) {
metaState.hasValidConfigFile = true
this.setConfigFilePath('ts')
this.setConfigFilePath('cypress.config.ts')
}
if (fs.existsSync(configFileJs)) {
@@ -1122,12 +1146,12 @@ export class ProjectLifecycleManager {
if (this._configFilePath) {
metaState.hasMultipleConfigPaths = true
} else {
this.setConfigFilePath('js')
this.setConfigFilePath('cypress.config.js')
}
}
if (!this._configFilePath) {
this.setConfigFilePath(metaState.hasTypescript ? 'ts' : 'js')
this.setConfigFilePath(`cypress.config.${metaState.hasTypescript ? 'ts' : 'js'}`)
}
if (metaState.hasLegacyCypressJson && !metaState.hasValidConfigFile) {
@@ -1139,8 +1163,8 @@ export class ProjectLifecycleManager {
return metaState
}
setConfigFilePath (lang: 'ts' | 'js') {
this._configFilePath = this._pathToFile(`cypress.config.${lang}`)
setConfigFilePath (fileName: string) {
this._configFilePath = this._pathToFile(fileName)
}
private _pathToFile (file: string) {
@@ -1288,6 +1312,12 @@ export class ProjectLifecycleManager {
return true
}
async needsCypressJsonMigration () {
const legacyConfigFileExist = await this.checkIfLegacyConfigFileExist()
return this.metaState.needsCypressJsonMigration && Boolean(legacyConfigFileExist)
}
private _pendingInitialize?: pDefer.DeferredPromise<FullConfig>
async initializeRunMode () {
@@ -91,7 +91,9 @@ export class MigrationDataSource {
await this.initializeFlags()
this.filteredSteps = await getStepsForMigration(this.ctx.currentProject, config)
const legacyConfigFileExist = await this.ctx.lifecycleManager.checkIfLegacyConfigFileExist()
this.filteredSteps = await getStepsForMigration(this.ctx.currentProject, config, Boolean(legacyConfigFileExist))
if (!this.filteredSteps[0]) {
throw Error(`Impossible to initialize a migration. No steps fit the configuration of this project.`)
@@ -252,7 +254,7 @@ export class MigrationDataSource {
// avoid reading the same file over and over again before it was finished reading
if (this.ctx.lifecycleManager.metaState.hasLegacyCypressJson && !this._oldConfigPromise) {
const cfgPath = path.join(this.ctx.lifecycleManager?.projectRoot, 'cypress.json')
const cfgPath = path.join(this.ctx.lifecycleManager?.projectRoot, this.ctx.lifecycleManager.legacyConfigFile)
this._oldConfigPromise = this.ctx.file.readJsonFile(cfgPath) as Promise<OldCypressConfig>
}
@@ -351,4 +353,8 @@ export class MigrationDataSource {
setStep (step: MIGRATION_STEP) {
this._step = step
}
get configFileNameAfterMigration () {
return this.ctx.lifecycleManager.legacyConfigFile.replace('.json', `.config.${this.ctx.lifecycleManager.metaState.hasTypescript ? 'ts' : 'js'}`)
}
}
@@ -152,16 +152,12 @@ async function shouldShowRenameManual (projectRoot: string, config: OldCypressCo
return anyComponentSpecsExist(projectRoot, config)
}
// All projects must move from cypress.json to cypress.config.js!
export function shouldShowConfigFileStep (config: OldCypressConfig) {
return true
}
export type Step = typeof MIGRATION_STEPS[number]
export async function getStepsForMigration (
projectRoot: string,
config: OldCypressConfig,
configFileExists: boolean,
): Promise<Step[]> {
const steps: Step[] = []
@@ -178,7 +174,7 @@ export async function getStepsForMigration (
steps.push(step)
}
if (step === 'configFile' && shouldShowConfigFileStep(config)) {
if (step === 'configFile' && configFileExists) {
steps.push(step)
}
@@ -71,7 +71,7 @@ describe('getStepsForMigration', () => {
const cwd = await scaffoldMigrationProject('migration-e2e-fully-custom')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getStepsForMigration(cwd, config)
const actual = await getStepsForMigration(cwd, config, true)
const expected: Step[] = ['configFile']
expect(actual).to.eql(expected)
@@ -81,7 +81,7 @@ describe('getStepsForMigration', () => {
const cwd = await scaffoldMigrationProject('migration-e2e-defaults')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getStepsForMigration(cwd, config)
const actual = await getStepsForMigration(cwd, config, true)
const expected: Step[] = ['renameAuto', 'renameSupport', 'configFile']
expect(actual).to.eql(expected)
@@ -91,7 +91,7 @@ describe('getStepsForMigration', () => {
const cwd = await scaffoldMigrationProject('migration-e2e-custom-test-files')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getStepsForMigration(cwd, config)
const actual = await getStepsForMigration(cwd, config, true)
const expected: Step[] = ['renameAuto', 'renameSupport', 'configFile']
expect(actual).to.eql(expected)
@@ -101,7 +101,7 @@ describe('getStepsForMigration', () => {
const cwd = await scaffoldMigrationProject('migration')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getStepsForMigration(cwd, config)
const actual = await getStepsForMigration(cwd, config, true)
const expected: Step[] = ['renameAuto', 'renameSupport', 'configFile', 'setupComponent']
expect(actual).to.eql(expected)
@@ -111,7 +111,7 @@ describe('getStepsForMigration', () => {
const cwd = await scaffoldMigrationProject('migration-component-testing-defaults')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getStepsForMigration(cwd, config)
const actual = await getStepsForMigration(cwd, config, true)
const expected: Step[] = ['renameAuto', 'renameManual', 'configFile', 'setupComponent']
expect(actual).to.eql(expected)
@@ -121,7 +121,7 @@ describe('getStepsForMigration', () => {
const cwd = await scaffoldMigrationProject('migration-component-testing-customized')
const config = fs.readJsonSync(path.join(cwd, 'cypress.json'))
const actual = await getStepsForMigration(cwd, config)
const actual = await getStepsForMigration(cwd, config, true)
const expected: Step[] = ['configFile', 'setupComponent']
expect(actual).to.eql(expected)
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
<style>
body {
font-family: "Courier Prime", Courier, monospace;
padding: 0 1em;
line-height: 1.4;
color: #eee;
background-color: #111;
}
pre {
padding: 0 0;
margin: 0 0;
font-family: "Courier Prime", Courier, monospace;
}
body {
margin: 5px;
padding: 0;
overflow: hidden;
}
pre {
white-space: pre-wrap;
word-break: break-word;
-webkit-font-smoothing: antialiased;
}
</style>
</head>
<body><pre><span style="color:#e05561">You are attempting to use Cypress with an older config file: <span style="color:#e5e510">custom.json<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">When you upgraded to Cypress v10.0 the config file was updated and moved to a new location: <span style="color:#e5e510">custom.config.js<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">You may need to update any CLI scripts to ensure that they are referring the new version. This would typically look something like:<span style="color:#e6e6e6">
<span style="color:#e05561">&quot;<span style="color:#e5e510">cypress open --config-file=custom.config.js<span style="color:#e05561">&quot;<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</pre></body></html>
+15 -3
View File
@@ -1166,13 +1166,13 @@ export const AllCypressErrors = {
`
},
LEGACY_CONFIG_FILE: (baseFileName: string, projectRoot: string) => {
LEGACY_CONFIG_FILE: (baseFileName: string, projectRoot: string, legacyConfigFile: string = 'cypress.json') => {
return errTemplate`
There is both a ${fmt.highlight(baseFileName)} and a ${fmt.highlight(`cypress.json`)} file at the location below:
There is both a ${fmt.highlight(baseFileName)} and a ${fmt.highlight(legacyConfigFile)} file at the location below:
${fmt.path(projectRoot)}
Cypress no longer supports cypress.json, please remove it from your project.
Cypress no longer supports ${fmt.off(legacyConfigFile)}, please remove it from your project.
`
},
@@ -1309,6 +1309,18 @@ 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
`
},
} as const
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -1096,5 +1096,10 @@ describe('visual error templates', () => {
default: [makeErr()],
}
},
MIGRATION_ALREADY_OCURRED: () => {
return {
default: ['custom.config.js', 'custom.json'],
}
},
})
})
@@ -40,6 +40,11 @@ export const e2eProjectDirs = [
'migration-component-testing',
'migration-component-testing-customized',
'migration-component-testing-defaults',
'migration-custom-config-file-migration-already-ocurred',
'migration-custom-config-file-respect-dirname',
'migration-custom-config-file-respect-pathname',
'migration-custom-config-file-root-level',
'migration-custom-config-file-with-existing-v10-config-file',
'migration-e2e-coffeescript',
'migration-e2e-component-default-everything',
'migration-e2e-component-default-test-files',
@@ -152,4 +152,6 @@ export const stubMigration: MaybeResolver<Migration> = {
hasCustomComponentTestFiles: false,
hasCustomIntegrationFolder: false,
hasCustomIntegrationTestFiles: false,
configFileNameAfter: 'cypress.config.js',
configFileNameBefore: 'cypress.json',
}
@@ -40,4 +40,7 @@ export const stubQuery: MaybeResolver<Query> = {
scaffoldedFiles () {
return null
},
projectRootFromCI () {
return false
},
}
@@ -16,10 +16,10 @@
src="../assets/logos/cypress-dark.png"
>
<a
class="font-medium mr-2px hocus-link-default"
:class="props.gql?.currentProject ? 'text-indigo-500' :
class="font-medium mr-2px"
:class="props.gql?.currentProject && !props.gql?.projectRootFromCI ? 'text-indigo-500 hocus-link-default' :
'text-gray-700'"
:href="props.gql?.currentProject ? 'global-mode' : undefined"
:href="props.gql?.currentProject && !props.gql?.projectRootFromCI ? 'global-mode' : undefined"
@click.prevent="clearCurrentProject"
>
{{ t('topNav.global.projects') }}
@@ -136,6 +136,7 @@ fragment HeaderBar_HeaderBarContent on Query {
config
savedState
}
projectRootFromCI
...TopNav
...Auth
}
@@ -157,7 +158,7 @@ const openLogin = () => {
}
const clearCurrentProject = () => {
if (props.gql.currentProject) {
if (props.gql.currentProject && !props.gql.projectRootFromCI) {
clearCurrentProjectMutation.executeMutation({})
}
}
+10
View File
@@ -551,6 +551,7 @@ enum ErrorTypeEnum {
INVALID_REPORTER_NAME
INVOKED_BINARY_OUTSIDE_NPM_MODULE
LEGACY_CONFIG_FILE
MIGRATION_ALREADY_OCURRED
MULTIPLE_SUPPORT_FILES_FOUND
NODE_VERSION_DEPRECATION_BUNDLED
NODE_VERSION_DEPRECATION_SYSTEM
@@ -781,6 +782,12 @@ type Migration {
"""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!]!
@@ -1144,6 +1151,9 @@ type Query {
"""Metadata about the migration, null if we aren't showing it"""
migration: Migration
"""Whether the project was specified from the --project flag"""
projectRootFromCI: Boolean!
"""All known projects for the app"""
projects: [ProjectLike!]!
@@ -103,7 +103,7 @@ export const CurrentProject = objectType({
t.boolean('needsLegacyConfigMigration', {
description: 'Whether the project needs to be migrated before proceeding',
resolve (source, args, ctx) {
return ctx.lifecycleManager.metaState.needsCypressJsonMigration
return ctx.lifecycleManager.needsCypressJsonMigration()
},
})
@@ -199,6 +199,20 @@ export const Migration = objectType({
},
})
t.nonNull.string('configFileNameBefore', {
description: 'the name of the config file to be migrated',
resolve: (source, args, ctx) => {
return ctx.lifecycleManager.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) => {
@@ -75,6 +75,11 @@ export const Query = objectType({
resolve: (source, args, ctx) => !ctx.currentProject,
})
t.nonNull.boolean('projectRootFromCI', {
description: 'Whether the project was specified from the --project flag',
resolve: (source, args, ctx) => Boolean(ctx.modeOptions.projectRoot),
})
t.nonNull.field('authState', {
type: AuthState,
description: 'The latest state of the auth process',
+186 -10
View File
@@ -18,9 +18,9 @@ Cypress.Commands.add('waitForWizard', () => {
return cy.get('[data-cy="migration-wizard"]')
})
function startMigrationFor (project: typeof e2eProjectDirs[number]) {
function startMigrationFor (project: typeof e2eProjectDirs[number], argv?: string[]) {
cy.scaffoldProject(project)
cy.openProject(project)
cy.openProject(project, argv)
cy.visitLaunchpad()
cy.waitForWizard()
}
@@ -29,20 +29,20 @@ function skipCTMigration () {
cy.contains(`I'll do this later`).click()
}
function migrateAndVerifyConfig (configExtension: 'js' | 'ts' | 'coffee' = 'js') {
function migrateAndVerifyConfig (migratedConfigFile: string = 'cypress.config.js') {
cy.contains('Migrate the configuration for me').click()
cy.withCtx(async (ctx, o) => {
const configStats = await ctx.actions.file.checkIfFileExists(`cypress.config.${o.configExtension}`)
const configStats = await ctx.actions.file.checkIfFileExists(o.migratedConfigFile)
expect(configStats).to.not.be.null.and.not.be.undefined
const oldConfigStats = await ctx.actions.file.checkIfFileExists('cypress.json')
const oldConfigStats = await ctx.lifecycleManager.checkIfLegacyConfigFileExist()
expect(oldConfigStats).to.be.null
expect(oldConfigStats).to.be.false
await ctx.actions.migration.assertSuccessfulConfigMigration(o.configExtension)
}, { configExtension })
await ctx.actions.migration.assertSuccessfulConfigMigration(o.migratedConfigFile)
}, { migratedConfigFile })
}
function finishMigrationAndContinue () {
@@ -656,7 +656,7 @@ describe('Full migration flow for each project', { retries: { openMode: 2, runMo
runAutoRename()
renameSupport('ts')
migrateAndVerifyConfig('ts')
migrateAndVerifyConfig('cypress.config.ts')
checkOutcome()
})
@@ -700,7 +700,7 @@ describe('Full migration flow for each project', { retries: { openMode: 2, runMo
})
renameSupport()
migrateAndVerifyConfig('ts')
migrateAndVerifyConfig('cypress.config.ts')
checkOutcome()
})
@@ -936,3 +936,179 @@ describe('Migration', { viewportWidth: 1200, retries: { openMode: 2, runMode: 2
cy.get('h2').should('not.contain', 'Change the existing spec file extension')
})
})
describe('Migrate custom config files', () => {
it('completes journey for migration-custom-config-file-root-level', () => {
startMigrationFor('migration-custom-config-file-root-level', ['--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.wait(100)
cy.withCtx(async (ctx) => {
const specs = ['cypress/e2e/foo.cy.js']
for (const spec of specs) {
const stats = await ctx.actions.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.wait(100)
cy.withCtx(async (ctx) => {
const specs = ['cypress/e2e/foo.cy.js']
for (const spec of specs) {
const stats = await ctx.actions.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.wait(100)
cy.withCtx(async (ctx) => {
const specs = ['cypress/e2e/foo.cy.js']
for (const spec of specs) {
const stats = await ctx.actions.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', () => {
cy.scaffoldProject('migration-custom-config-file-migration-already-ocurred')
cy.openProject('migration-custom-config-file-migration-already-ocurred', ['--config-file', 'customConfig.json'])
cy.visitLaunchpad()
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', () => {
cy.scaffoldProject('migration-custom-config-file-with-existing-v10-config-file')
cy.openProject('migration-custom-config-file-with-existing-v10-config-file', ['--config-file', 'customConfig.json'])
cy.visitLaunchpad()
cy.contains('There is both a customConfig.config.js and a customConfig.json file at the location below:')
cy.contains('ypress no longer supports customConfig.json, please remove it from your project.')
})
})
@@ -81,12 +81,12 @@
>
<template #jsonFile>
<CodeTag class="text-red-500">
cypress.json
{{ props.gql.configFileNameBefore }}
</CodeTag>
</template>
<template #jsFile>
<CodeTag class="text-jade-500">
{{ fileName }}
{{ props.gql.configFileNameAfter }}
</CodeTag>
</template>
</i18n-t>
@@ -99,7 +99,7 @@
bg
class="text-red-600 bg-red-100"
>
cypress.json
{{ props.gql.configFileNameBefore }}
</CodeTag>
</template>
<template #afterHeader>
@@ -108,7 +108,7 @@
bg
class="bg-jade-100 text-jade-600"
>
{{ fileName }}
{{ props.gql.configFileNameAfter }}
</CodeTag>
</template>
<template #before>
@@ -147,6 +147,8 @@ const { t } = useI18n()
gql`
fragment ConvertConfigFile on Migration {
configFileNameBefore
configFileNameAfter
configBeforeCode
configAfterCode
hasCustomIntegrationFolder
@@ -173,6 +175,7 @@ const codeAfter = computed(() => {
})
const fileName = computed(() => props.gql.hasTypescript ? 'cypress.config.ts' : 'cypress.config.js')
</script>
<style lang="scss" scoped>
@@ -197,7 +197,9 @@ query MigrationWizardQuery {
}
`
const query = useQuery({ query: MigrationWizardQueryDocument, pause: true })
// The requestPolicy needs to be cache-and-network because otherwise
// if a user visits 2 projects with migration data - is gonna show only the first data
const query = useQuery({ query: MigrationWizardQueryDocument, requestPolicy: 'cache-and-network', pause: true })
const migration = computed(() => query.data.value?.migration)
const steps = computed(() => migration.value?.filteredSteps || [])
@@ -0,0 +1,14 @@
## Migration Custom Config File Migration Already Ocurred
This is the "kitchen sink" of migrations. It has E2E and a custom config file which
has been already migrated, so when running with the json file it shows the correct
file to be used
The following migration steps will be used during this migration:
- [x] automatic folder rename of cypress/integration to cypress/e2e
- [ ] manual file rename
- [x] rename support
- [x] update config file
- [] setup component testing
@@ -0,0 +1,13 @@
## Migration Custom Config File Respect Dirname
This is the "kitchen sink" of migrations. It has E2E and a custom config file which is inside
a folder - the migration should respect the dirname when creating the new config file
The following migration steps will be used during this migration:
- [x] automatic folder rename of cypress/integration to cypress/e2e
- [ ] manual file rename
- [x] rename support
- [x] update config file
- [] setup component testing
@@ -0,0 +1,11 @@
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)
},
},
})
@@ -0,0 +1,13 @@
## Migration Custom Config File Respect Pathname
This is the "kitchen sink" of migrations. It has E2E and a custom config file which the pathname
includes extra values - the migration should respect the pathname when creating the new config file
The following migration steps will be used during this migration:
- [x] automatic folder rename of cypress/integration to cypress/e2e
- [ ] manual file rename
- [x] rename support
- [x] update config file
- [] setup component testing
@@ -0,0 +1,11 @@
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)
},
},
})
@@ -0,0 +1,13 @@
## Migration Custom Config File Root Level
This is the "kitchen sink" of migrations. It has E2E and a custom config file which
lives in the root of the project
The following migration steps will be used during this migration:
- [x] automatic folder rename of cypress/integration to cypress/e2e
- [ ] manual file rename
- [x] rename support
- [x] update config file
- [] setup component testing
@@ -0,0 +1,11 @@
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)
},
},
})
@@ -0,0 +1,14 @@
## Migration Custom Config File Root Level
This is the "kitchen sink" of migrations. It has E2E and a custom config file which
lives in the root of the project and also there's a v10 config file, creating legacy
config conflicts
The following migration steps will be used during this migration:
- [x] automatic folder rename of cypress/integration to cypress/e2e
- [ ] manual file rename
- [x] rename support
- [x] update config file
- [] setup component testing
@@ -0,0 +1,11 @@
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)
},
},
})