mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-20 22:21:23 -06:00
feat: convert cypress.json to cypress.config.js (#19748)
Co-authored-by: Barthélémy Ledoux <bart@cypress.io> Co-authored-by: ElevateBart <ledouxb@gmail.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
exports['migration utils should create a string when passed only a global option 1'] = `
|
||||
const { defineConfig } = require('cypress')
|
||||
|
||||
module.export = defineConfig({
|
||||
visualViewport: 300,
|
||||
})
|
||||
`
|
||||
|
||||
exports['migration utils should create a string when passed an empty object 1'] = `
|
||||
const { defineConfig } = require('cypress')
|
||||
|
||||
module.export = defineConfig({
|
||||
|
||||
})
|
||||
`
|
||||
|
||||
exports['migration utils should create a string when passed only a e2e options 1'] = `
|
||||
const { defineConfig } = require('cypress')
|
||||
|
||||
module.export = defineConfig({
|
||||
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
return require('/cypress/plugins/index.js')
|
||||
},
|
||||
baseUrl: 'localhost:3000'
|
||||
},
|
||||
})
|
||||
`
|
||||
|
||||
exports['migration utils should create a string for a config with global, component, and e2e options 1'] = `
|
||||
const { defineConfig } = require('cypress')
|
||||
|
||||
module.export = defineConfig({
|
||||
visualViewport: 300,
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
return require('/cypress/plugins/index.js')
|
||||
},
|
||||
baseUrl: 'localhost:300',
|
||||
retries: 2
|
||||
},
|
||||
component: {
|
||||
setupNodeEvents(on, config) {
|
||||
return require('/cypress/plugins/index.js')
|
||||
},
|
||||
retries: 1
|
||||
},
|
||||
})
|
||||
`
|
||||
|
||||
exports['migration utils should create a string when passed only a component options 1'] = `
|
||||
const { defineConfig } = require('cypress')
|
||||
|
||||
module.export = defineConfig({
|
||||
|
||||
component: {
|
||||
setupNodeEvents(on, config) {
|
||||
return require('/cypress/plugins/index.js')
|
||||
},
|
||||
retries: 2
|
||||
},
|
||||
})
|
||||
`
|
||||
|
||||
exports['migration utils should exclude fields that are no longer valid 1'] = `
|
||||
const { defineConfig } = require('cypress')
|
||||
|
||||
module.export = defineConfig({
|
||||
|
||||
})
|
||||
`
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
FileActions,
|
||||
ProjectActions,
|
||||
WizardActions,
|
||||
MigrationActions,
|
||||
} from './actions'
|
||||
import { AuthActions } from './actions/AuthActions'
|
||||
import { DevActions } from './actions/DevActions'
|
||||
@@ -53,4 +54,9 @@ export class DataActions {
|
||||
get electron () {
|
||||
return new ElectronActions(this.ctx)
|
||||
}
|
||||
|
||||
@cached
|
||||
get migration () {
|
||||
return new MigrationActions(this.ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
HtmlDataSource,
|
||||
UtilDataSource,
|
||||
BrowserApiShape,
|
||||
MigrationDataSource,
|
||||
} from './sources/'
|
||||
import { cached } from './util/cached'
|
||||
import type { GraphQLSchema } from 'graphql'
|
||||
@@ -225,6 +226,11 @@ export class DataContext {
|
||||
return new UtilDataSource(this)
|
||||
}
|
||||
|
||||
@cached
|
||||
get migration () {
|
||||
return new MigrationDataSource(this)
|
||||
}
|
||||
|
||||
get projectsList () {
|
||||
return this.coreData.app.projects
|
||||
}
|
||||
|
||||
19
packages/data-context/src/actions/MigrationActions.ts
Normal file
19
packages/data-context/src/actions/MigrationActions.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { DataContext } from '..'
|
||||
|
||||
export class MigrationActions {
|
||||
constructor (private ctx: DataContext) { }
|
||||
|
||||
async createConfigFile () {
|
||||
const config = await this.ctx.migration.createConfigString()
|
||||
|
||||
await this.ctx.actions.file.writeFileInProject('cypress.config.js', config).catch((error) => {
|
||||
throw error
|
||||
})
|
||||
|
||||
this.ctx.lifecycleManager.refreshMetaState()
|
||||
|
||||
await this.ctx.actions.file.removeFileInProject('cypress.json').catch((error) => {
|
||||
throw error
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -8,5 +8,6 @@ export * from './DevActions'
|
||||
export * from './ElectronActions'
|
||||
export * from './FileActions'
|
||||
export * from './LocalSettingsActions'
|
||||
export * from './MigrationActions'
|
||||
export * from './ProjectActions'
|
||||
export * from './WizardActions'
|
||||
|
||||
@@ -74,6 +74,16 @@ export class FileDataSource {
|
||||
}
|
||||
}
|
||||
|
||||
isValidJsFile (absolutePath: string) {
|
||||
try {
|
||||
require(absolutePath)
|
||||
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private trackFile () {
|
||||
// this.watchedFilePaths.clear()
|
||||
// this.fileLoader.clear()
|
||||
|
||||
64
packages/data-context/src/sources/MigrationDataSource.ts
Normal file
64
packages/data-context/src/sources/MigrationDataSource.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import path from 'path'
|
||||
import type { DataContext } from '..'
|
||||
import { createConfigString } from '../util/migration'
|
||||
|
||||
export class MigrationDataSource {
|
||||
private _config: Cypress.ConfigOptions | null = null
|
||||
constructor (private ctx: DataContext) { }
|
||||
|
||||
async getConfig () {
|
||||
const config = await this.parseCypressConfig()
|
||||
|
||||
return JSON.stringify(config, null, 2)
|
||||
}
|
||||
|
||||
async createConfigString () {
|
||||
const config = await this.parseCypressConfig()
|
||||
|
||||
return createConfigString(config)
|
||||
}
|
||||
|
||||
async getIntegrationFolder () {
|
||||
const config = await this.parseCypressConfig()
|
||||
|
||||
if (config.e2e?.integrationFolder) {
|
||||
return config.e2e.integrationFolder
|
||||
}
|
||||
|
||||
if (config.integrationFolder) {
|
||||
return config.integrationFolder
|
||||
}
|
||||
|
||||
return 'cypress/integration'
|
||||
}
|
||||
|
||||
async getComponentFolder () {
|
||||
const config = await this.parseCypressConfig()
|
||||
|
||||
if (config.component?.componentFolder) {
|
||||
return config.component.componentFolder
|
||||
}
|
||||
|
||||
if (config.componentFolder) {
|
||||
return config.componentFolder
|
||||
}
|
||||
|
||||
return 'cypress/component'
|
||||
}
|
||||
|
||||
private async parseCypressConfig (): Promise<Cypress.ConfigOptions> {
|
||||
if (this._config) {
|
||||
return this._config
|
||||
}
|
||||
|
||||
if (this.ctx.lifecycleManager.metaState.hasLegacyCypressJson) {
|
||||
const cfgPath = path.join(this.ctx.lifecycleManager?.projectRoot, 'cypress.json')
|
||||
|
||||
this._config = this.ctx.file.readJsonFile(cfgPath) as Cypress.ConfigOptions
|
||||
|
||||
return this._config
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export * from './FileDataSource'
|
||||
export * from './GitDataSource'
|
||||
export * from './GraphQLDataSource'
|
||||
export * from './HtmlDataSource'
|
||||
export * from './MigrationDataSource'
|
||||
export * from './ProjectDataSource'
|
||||
export * from './SettingsDataSource'
|
||||
export * from './StorybookDataSource'
|
||||
|
||||
@@ -6,4 +6,5 @@ export * from './cached'
|
||||
export * from './config-file-updater'
|
||||
export * from './config-options'
|
||||
export * from './file'
|
||||
export * from './migration'
|
||||
export * from './urqlCacheKeys'
|
||||
|
||||
80
packages/data-context/src/util/migration.ts
Normal file
80
packages/data-context/src/util/migration.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import path from 'path'
|
||||
|
||||
type ConfigOptions = {
|
||||
global: Record<string, unknown>
|
||||
e2e: Record<string, unknown>
|
||||
component: Record<string, unknown>
|
||||
}
|
||||
|
||||
export async function createConfigString (cfg: Partial<Cypress.ConfigOptions>) {
|
||||
return createCypressConfigJs(reduceConfig(cfg), getPluginRelativePath(cfg))
|
||||
}
|
||||
|
||||
function getPluginRelativePath (cfg: Partial<Cypress.ConfigOptions>) {
|
||||
const DEFAULT_PLUGIN_PATH = path.normalize('/cypress/plugins/index.js')
|
||||
|
||||
return cfg.pluginsFile ? cfg.pluginsFile : DEFAULT_PLUGIN_PATH
|
||||
}
|
||||
|
||||
function reduceConfig (cfg: Partial<Cypress.ConfigOptions>) {
|
||||
const excludedFields = ['pluginsFile', '$schema', 'componentFolder']
|
||||
|
||||
return Object.entries(cfg).reduce((acc, [key, val]) => {
|
||||
if (excludedFields.includes(key)) {
|
||||
return acc
|
||||
}
|
||||
|
||||
if (key === 'e2e' || key === 'component') {
|
||||
const value = val as Record<string, unknown>
|
||||
|
||||
return { ...acc, [key]: { ...acc[key], ...value } }
|
||||
}
|
||||
|
||||
if (key === 'testFiles') {
|
||||
return {
|
||||
...acc,
|
||||
e2e: { ...acc.e2e, specPattern: val },
|
||||
component: { ...acc.component, specPattern: val },
|
||||
}
|
||||
}
|
||||
|
||||
if (key === 'baseUrl') {
|
||||
return {
|
||||
...acc,
|
||||
e2e: { ...acc.e2e, [key]: val },
|
||||
}
|
||||
}
|
||||
|
||||
return { ...acc, global: { ...acc.global, [key]: val } }
|
||||
}, { global: {}, e2e: {}, component: {} })
|
||||
}
|
||||
|
||||
function createCypressConfigJs (config: ConfigOptions, pluginPath: string) {
|
||||
const globalString = Object.keys(config.global).length > 0 ? `${formatObjectForConfig(config.global, 2)},` : ''
|
||||
const componentString = Object.keys(config.component).length > 0 ? createTestingTypeTemplate('component', pluginPath, config.component) : ''
|
||||
const e2eString = Object.keys(config.e2e).length > 0 ? createTestingTypeTemplate('e2e', pluginPath, config.e2e) : ''
|
||||
|
||||
return `const { defineConfig } = require('cypress')
|
||||
|
||||
module.export = defineConfig({
|
||||
${globalString}${e2eString}${componentString}
|
||||
})`
|
||||
}
|
||||
|
||||
function formatObjectForConfig (obj: Record<string, unknown>, spaces: number) {
|
||||
return JSON.stringify(obj, null, spaces)
|
||||
.replace(/"([^"]+)":/g, '$1:') // remove quotes from fields
|
||||
.replace(/^[{]|[}]$/g, '') // remove opening and closing {}
|
||||
.replace(/"/g, '\'') // single quotes
|
||||
.trim()
|
||||
}
|
||||
|
||||
function createTestingTypeTemplate (testingType: 'e2e' | 'component', pluginPath: string, options: Record<string, unknown>) {
|
||||
return `
|
||||
${testingType}: {
|
||||
setupNodeEvents(on, config) {
|
||||
return require('${pluginPath}')
|
||||
},
|
||||
${formatObjectForConfig(options, 4)}
|
||||
},`
|
||||
}
|
||||
75
packages/data-context/test/unit/config-generator.spec.ts
Normal file
75
packages/data-context/test/unit/config-generator.spec.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import snapshot from 'snap-shot-it'
|
||||
import { createConfigString } from '../../src/util/migration'
|
||||
|
||||
describe('migration utils', () => {
|
||||
it('should create a string when passed only a global option', async () => {
|
||||
const config = {
|
||||
visualViewport: 300,
|
||||
}
|
||||
|
||||
const generatedConfig = await createConfigString(config)
|
||||
|
||||
snapshot(generatedConfig)
|
||||
})
|
||||
|
||||
it('should create a string when passed only a e2e options', async () => {
|
||||
const config = {
|
||||
e2e: {
|
||||
baseUrl: 'localhost:3000',
|
||||
},
|
||||
}
|
||||
|
||||
const generatedConfig = await createConfigString(config)
|
||||
|
||||
snapshot(generatedConfig)
|
||||
})
|
||||
|
||||
it('should create a string when passed only a component options', async () => {
|
||||
const config = {
|
||||
component: {
|
||||
retries: 2,
|
||||
},
|
||||
}
|
||||
|
||||
const generatedConfig = await createConfigString(config)
|
||||
|
||||
snapshot(generatedConfig)
|
||||
})
|
||||
|
||||
it('should create a string for a config with global, component, and e2e options', async () => {
|
||||
const config = {
|
||||
visualViewport: 300,
|
||||
baseUrl: 'localhost:300',
|
||||
e2e: {
|
||||
retries: 2,
|
||||
},
|
||||
component: {
|
||||
retries: 1,
|
||||
},
|
||||
}
|
||||
|
||||
const generatedConfig = await createConfigString(config)
|
||||
|
||||
snapshot(generatedConfig)
|
||||
})
|
||||
|
||||
it('should create a string when passed an empty object', async () => {
|
||||
const config = {}
|
||||
|
||||
const generatedConfig = await createConfigString(config)
|
||||
|
||||
snapshot(generatedConfig)
|
||||
})
|
||||
|
||||
it('should exclude fields that are no longer valid', async () => {
|
||||
const config = {
|
||||
'$schema': 'http://someschema.com',
|
||||
pluginsFile: 'path/to/plugin/file',
|
||||
componentFolder: 'path/to/component/folder',
|
||||
}
|
||||
|
||||
const generatedConfig = await createConfigString(config)
|
||||
|
||||
snapshot(generatedConfig)
|
||||
})
|
||||
})
|
||||
@@ -29,6 +29,7 @@ export const e2eProjectDirs = [
|
||||
'kill-child-process',
|
||||
'launchpad',
|
||||
'max-listeners',
|
||||
'migration',
|
||||
'multiple-task-registrations',
|
||||
'multiples-config-files-with-json',
|
||||
'no-scaffolding',
|
||||
|
||||
@@ -47,4 +47,6 @@ export const stubMigration: MaybeResolver<Migration> = {
|
||||
}
|
||||
},
|
||||
})`,
|
||||
integrationFolder: 'cypress/integration',
|
||||
componentFolder: 'cypress/component',
|
||||
}
|
||||
|
||||
@@ -543,12 +543,18 @@ type LocalSettingsPreferences {
|
||||
|
||||
"""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 integration folder path used to store e2e tests"""
|
||||
integrationFolder: String!
|
||||
|
||||
"""List of files needing manual conversion"""
|
||||
manualFiles: [String!]!
|
||||
|
||||
@@ -619,6 +625,9 @@ type Mutation {
|
||||
"""Check if a give spec file will match the project spec pattern"""
|
||||
matchesSpecPattern(specFile: String!): Boolean!
|
||||
|
||||
"""Transforms cypress.json file into cypress.config.js file"""
|
||||
migrateConfigFile: Boolean
|
||||
|
||||
"""Open a path in preferred IDE"""
|
||||
openDirectoryInIDE(path: String!): Boolean
|
||||
openExternal(url: String!): Boolean
|
||||
|
||||
@@ -14,7 +14,7 @@ export const Migration = objectType({
|
||||
t.nonNull.field('step', {
|
||||
type: MigrationStepEnum,
|
||||
description: 'Step where the migration is right now',
|
||||
resolve: () => 'renameManual',
|
||||
resolve: () => 'configFile',
|
||||
})
|
||||
|
||||
t.nonNull.list.nonNull.string('specFilesBefore', {
|
||||
@@ -40,15 +40,29 @@ export const Migration = objectType({
|
||||
|
||||
t.nonNull.string('configBeforeCode', {
|
||||
description: 'contents of the cypress.json file before conversion',
|
||||
resolve: () => {
|
||||
return ``
|
||||
resolve: (source, args, ctx) => {
|
||||
return ctx.migration.getConfig()
|
||||
},
|
||||
})
|
||||
|
||||
t.nonNull.string('configAfterCode', {
|
||||
description: 'contents of the cypress.json file after conversion',
|
||||
resolve: () => {
|
||||
return ``
|
||||
resolve: (source, args, ctx) => {
|
||||
return ctx.migration.createConfigString()
|
||||
},
|
||||
})
|
||||
|
||||
t.nonNull.string('integrationFolder', {
|
||||
description: 'the integration folder path used to store e2e tests',
|
||||
resolve: (source, args, ctx) => {
|
||||
return ctx.migration.getIntegrationFolder()
|
||||
},
|
||||
})
|
||||
|
||||
t.nonNull.string('componentFolder', {
|
||||
description: 'the component folder path used to store components tests',
|
||||
resolve: (source, args, ctx) => {
|
||||
return ctx.migration.getComponentFolder()
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
@@ -419,6 +419,20 @@ export const mutation = mutationType({
|
||||
},
|
||||
})
|
||||
|
||||
t.field('migrateConfigFile', {
|
||||
description: 'Transforms cypress.json file into cypress.config.js file',
|
||||
type: 'Boolean',
|
||||
resolve: async (_, args, ctx) => {
|
||||
try {
|
||||
await ctx.actions.migration.createConfigFile()
|
||||
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
t.field('setProjectIdInConfigFile', {
|
||||
description: 'Set the projectId field in the config file of the current project',
|
||||
type: Query,
|
||||
|
||||
41
packages/launchpad/cypress/e2e/migration.cy.ts
Normal file
41
packages/launchpad/cypress/e2e/migration.cy.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
describe('Migration', () => {
|
||||
beforeEach(() => {
|
||||
cy.scaffoldProject('migration')
|
||||
cy.openProject('migration')
|
||||
cy.visitLaunchpad()
|
||||
})
|
||||
|
||||
describe('Configuration', () => {
|
||||
it('should create the cypress.config.js file and delete old config', () => {
|
||||
cy.get('[data-cy="convertConfigButton"]').click()
|
||||
|
||||
cy.withCtx(async (ctx) => {
|
||||
const stats = await ctx.actions.file.checkIfFileExists('cypress.config.js')
|
||||
|
||||
expect(stats).to.not.be.null.and.not.be.undefined
|
||||
|
||||
let doesFileExist = true
|
||||
|
||||
try {
|
||||
await ctx.actions.file.checkIfFileExists('cypress.json')
|
||||
} catch (error) {
|
||||
doesFileExist = false
|
||||
}
|
||||
|
||||
expect(doesFileExist).to.be.false
|
||||
})
|
||||
})
|
||||
|
||||
it('should create a valid js file', () => {
|
||||
cy.get('[data-cy="convertConfigButton"]').click()
|
||||
|
||||
cy.withCtx(async (ctx) => {
|
||||
const configPath = ctx.path.join(ctx.lifecycleManager.projectRoot, 'cypress.config.js')
|
||||
|
||||
const isValidJsFile = ctx.file.isValidJsFile(configPath)
|
||||
|
||||
expect(isValidJsFile).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -13,8 +13,7 @@
|
||||
:gql="query.data.value"
|
||||
/>
|
||||
<MigrationWizard
|
||||
v-else-if="currentProject?.needsLegacyConfigMigration && query.data.value.migration"
|
||||
:gql="query.data.value.migration"
|
||||
v-else-if="currentProject?.needsLegacyConfigMigration"
|
||||
/>
|
||||
<template v-else>
|
||||
<ScaffoldedFiles
|
||||
@@ -121,9 +120,6 @@ fragment MainLaunchpadQueryData on Query {
|
||||
isInGlobalMode
|
||||
...GlobalPage
|
||||
...ScaffoldedFiles
|
||||
migration {
|
||||
...MigrationWizard
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
@@ -1,41 +1,66 @@
|
||||
import { MigrationWizardFragmentDoc } from '../generated/graphql-test'
|
||||
import { MigrationWizardDataFragmentDoc } from '../generated/graphql-test'
|
||||
import MigrationWizard from './MigrationWizard.vue'
|
||||
|
||||
describe('<MigrationWizard/>', { viewportWidth: 1280, viewportHeight: 1100 }, () => {
|
||||
it('renders Automatic rename', () => {
|
||||
cy.mountFragment(MigrationWizardFragmentDoc, {
|
||||
cy.mountFragment(MigrationWizardDataFragmentDoc, {
|
||||
onResult (res) {
|
||||
res.step = 'renameAuto'
|
||||
res.migration = {
|
||||
__typename: 'Migration',
|
||||
step: 'renameAuto',
|
||||
specFilesAfter: ['test.cy.tsx'],
|
||||
specFilesBefore: ['test.spec.tsx'],
|
||||
manualFiles: ['test.cy.tsx'],
|
||||
configAfterCode: '{}',
|
||||
configBeforeCode: '{}',
|
||||
}
|
||||
},
|
||||
render (gql) {
|
||||
render () {
|
||||
return (<div class="p-16px">
|
||||
<MigrationWizard gql={gql} />
|
||||
<MigrationWizard />
|
||||
</div>)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('renders Manual rename', () => {
|
||||
cy.mountFragment(MigrationWizardFragmentDoc, {
|
||||
cy.mountFragment(MigrationWizardDataFragmentDoc, {
|
||||
onResult (res) {
|
||||
res.step = 'renameManual'
|
||||
res.migration = {
|
||||
__typename: 'Migration',
|
||||
step: 'renameManual',
|
||||
specFilesAfter: ['test.cy.tsx'],
|
||||
specFilesBefore: ['test.spec.tsx'],
|
||||
manualFiles: ['test.cy.tsx'],
|
||||
configAfterCode: '{}',
|
||||
configBeforeCode: '{}',
|
||||
}
|
||||
},
|
||||
render (gql) {
|
||||
render () {
|
||||
return (<div class="p-16px">
|
||||
<MigrationWizard gql={gql} />
|
||||
<MigrationWizard />
|
||||
</div>)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('renders Config File migration', () => {
|
||||
cy.mountFragment(MigrationWizardFragmentDoc, {
|
||||
cy.mountFragment(MigrationWizardDataFragmentDoc, {
|
||||
onResult (res) {
|
||||
res.step = 'configFile'
|
||||
res.migration = {
|
||||
...res.migration,
|
||||
__typename: 'Migration',
|
||||
step: 'configFile',
|
||||
specFilesAfter: ['test.cy.tsx'],
|
||||
specFilesBefore: ['test.spec.tsx'],
|
||||
manualFiles: ['test.cy.tsx'],
|
||||
configAfterCode: '{}',
|
||||
configBeforeCode: '{}',
|
||||
}
|
||||
},
|
||||
render (gql) {
|
||||
render () {
|
||||
return (<div class="p-16px">
|
||||
<MigrationWizard gql={gql} />
|
||||
<MigrationWizard />
|
||||
</div>)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -7,69 +7,71 @@
|
||||
>
|
||||
{{ t('migration.wizard.description') }}
|
||||
</p>
|
||||
|
||||
<MigrationStep
|
||||
:open="props.gql.step === 'renameAuto'"
|
||||
:checked="props.gql.step !== 'renameAuto'"
|
||||
:step="1"
|
||||
:title="t('migration.wizard.step1.title')"
|
||||
:description="t('migration.wizard.step1.description')"
|
||||
>
|
||||
<RenameSpecsAuto :gql="props.gql" />
|
||||
<template #footer>
|
||||
<Button
|
||||
@click="renameSpecs"
|
||||
>
|
||||
{{ t('migration.wizard.step1.button') }}
|
||||
</Button>
|
||||
</template>
|
||||
</MigrationStep>
|
||||
<MigrationStep
|
||||
:open="props.gql.step === 'renameManual'"
|
||||
:checked="props.gql.step === 'configFile'"
|
||||
:step="2"
|
||||
:title="t('migration.wizard.step2.title')"
|
||||
:description="t('migration.wizard.step2.description')"
|
||||
>
|
||||
<RenameSpecsManual :gql="props.gql" />
|
||||
<template #footer>
|
||||
<div class="flex gap-16px">
|
||||
<template v-if="query.data.value?.migration">
|
||||
<MigrationStep
|
||||
:open="migration.step === 'renameAuto'"
|
||||
:checked="migration.step !== 'renameAuto'"
|
||||
:step="1"
|
||||
:title="t('migration.wizard.step1.title')"
|
||||
:description="t('migration.wizard.step1.description')"
|
||||
>
|
||||
<RenameSpecsAuto :gql="query.data.value?.migration" />
|
||||
<template #footer>
|
||||
<Button
|
||||
disabled
|
||||
variant="pending"
|
||||
@click="renameSpecs"
|
||||
>
|
||||
<template #prefix>
|
||||
<i-cy-loading_x16
|
||||
{{ t('migration.wizard.step1.button') }}
|
||||
</Button>
|
||||
</template>
|
||||
</MigrationStep>
|
||||
<MigrationStep
|
||||
:open="migration.step === 'renameManual'"
|
||||
:checked="migration.step === 'configFile'"
|
||||
:step="2"
|
||||
:title="t('migration.wizard.step2.title')"
|
||||
:description="t('migration.wizard.step2.description')"
|
||||
>
|
||||
<RenameSpecsManual :gql="query.data.value?.migration" />
|
||||
<template #footer>
|
||||
<div class="flex gap-16px">
|
||||
<Button
|
||||
disabled
|
||||
variant="pending"
|
||||
>
|
||||
<template #prefix>
|
||||
<i-cy-loading_x16
|
||||
|
||||
class="animate-spin icon-dark-white icon-light-gray-400"
|
||||
/>
|
||||
</template>
|
||||
{{ t('migration.wizard.step2.buttonWait') }}
|
||||
</Button>
|
||||
class="animate-spin icon-dark-white icon-light-gray-400"
|
||||
/>
|
||||
</template>
|
||||
{{ t('migration.wizard.step2.buttonWait') }}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
@click="skipStep2"
|
||||
>
|
||||
{{ t('migration.wizard.step2.button') }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</MigrationStep>
|
||||
<MigrationStep
|
||||
:open="migration.step === 'configFile'"
|
||||
:step="3"
|
||||
:title="t('migration.wizard.step3.title')"
|
||||
:description="t('migration.wizard.step3.description')"
|
||||
>
|
||||
<ConvertConfigFile :gql="query.data.value?.migration" />
|
||||
<template #footer>
|
||||
<Button
|
||||
variant="outline"
|
||||
@click="skipStep2"
|
||||
data-cy="convertConfigButton"
|
||||
@click="convertConfig"
|
||||
>
|
||||
{{ t('migration.wizard.step2.button') }}
|
||||
{{ t('migration.wizard.step3.button') }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</MigrationStep>
|
||||
<MigrationStep
|
||||
:open="props.gql.step === 'configFile'"
|
||||
:step="3"
|
||||
:title="t('migration.wizard.step3.title')"
|
||||
:description="t('migration.wizard.step3.description')"
|
||||
>
|
||||
<ConvertConfigFile :gql="props.gql" />
|
||||
<template #footer>
|
||||
<Button
|
||||
@click="convertConfig"
|
||||
>
|
||||
{{ t('migration.wizard.step3.button') }}
|
||||
</Button>
|
||||
</template>
|
||||
</MigrationStep>
|
||||
</template>
|
||||
</MigrationStep>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -79,22 +81,39 @@ import RenameSpecsAuto from './RenameSpecsAuto.vue'
|
||||
import RenameSpecsManual from './RenameSpecsManual.vue'
|
||||
import ConvertConfigFile from './ConvertConfigFile.vue'
|
||||
import { useI18n } from '@cy/i18n'
|
||||
import { gql } from '@urql/vue'
|
||||
import type { MigrationWizardFragment } from '../generated/graphql'
|
||||
import { gql, useMutation, useQuery } from '@urql/vue'
|
||||
import { MigrationWizardQueryDocument } from '../generated/graphql'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
gql`
|
||||
fragment MigrationWizard on Migration {
|
||||
step
|
||||
...RenameSpecsAuto
|
||||
...RenameSpecsManual
|
||||
...ConvertConfigFile
|
||||
fragment MigrationWizardData on Query {
|
||||
migration {
|
||||
step
|
||||
...RenameSpecsAuto
|
||||
...RenameSpecsManual
|
||||
...ConvertConfigFile
|
||||
}
|
||||
}`
|
||||
|
||||
const props = defineProps<{
|
||||
gql: MigrationWizardFragment
|
||||
}>()
|
||||
gql`
|
||||
query MigrationWizardQuery {
|
||||
...MigrationWizardData
|
||||
}
|
||||
`
|
||||
|
||||
const convertConfigMutation = gql`
|
||||
mutation MigrationWizard_ConvertFile {
|
||||
migrateConfigFile
|
||||
}
|
||||
`
|
||||
|
||||
const query = useQuery({ query: MigrationWizardQueryDocument })
|
||||
|
||||
const configMutation = useMutation(convertConfigMutation)
|
||||
|
||||
const migration = computed(() => query.data.value?.migration ?? { step: 'renameAuto' })
|
||||
|
||||
function renameSpecs () {
|
||||
|
||||
@@ -105,6 +124,6 @@ function skipStep2 () {
|
||||
}
|
||||
|
||||
function convertConfig () {
|
||||
|
||||
configMutation.executeMutation({})
|
||||
}
|
||||
</script>
|
||||
|
||||
18
system-tests/projects/migration/cypress.json
Normal file
18
system-tests/projects/migration/cypress.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "https://on.cypress.io/cypress.schema.json",
|
||||
"baseUrl": "http://localhost:3000",
|
||||
"retries": 2,
|
||||
"defaultCommandTimeout": 5000,
|
||||
"fixturesFolder": false,
|
||||
"componentFolder": "src",
|
||||
"testFiles": "**/*.spec.{tsx,js}",
|
||||
"pluginsFile": "cypress/plugins/index.ts",
|
||||
"e2e": {
|
||||
"defaultCommandTimeout": 10000,
|
||||
"slowTestThreshold": 5000
|
||||
},
|
||||
"component": {
|
||||
"slowTestThreshold": 5000,
|
||||
"retries": 1
|
||||
}
|
||||
}
|
||||
5
system-tests/projects/migration/cypress/plugins/index.js
Normal file
5
system-tests/projects/migration/cypress/plugins/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = (on, config) => {
|
||||
return {
|
||||
test: 'value',
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user