mirror of
https://github.com/cypress-io/cypress.git
synced 2026-05-03 21:40:28 -05:00
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:
committed by
GitHub
parent
59ba5c764e
commit
7636287eee
@@ -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">"<span style="color:#e5e510">cypress open --config-file=custom.config.js<span style="color:#e05561">"<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6">
|
||||
<span style="color:#e05561">https://on.cypress.io/migration-guide<span style="color:#e6e6e6">
|
||||
<span style="color:#e05561"><span style="color:#e6e6e6"></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
|
||||
</pre></body></html>
|
||||
@@ -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({})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 || [])
|
||||
|
||||
+14
@@ -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
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
{}
|
||||
+11
@@ -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
@@ -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
@@ -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)
|
||||
},
|
||||
},
|
||||
})
|
||||
+14
@@ -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
|
||||
|
||||
+11
@@ -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)
|
||||
},
|
||||
},
|
||||
})
|
||||
+1
@@ -0,0 +1 @@
|
||||
{}
|
||||
Reference in New Issue
Block a user