diff --git a/packages/data-context/src/sources/MigrationDataSource.ts b/packages/data-context/src/sources/MigrationDataSource.ts index 77bb1f6022..11654628f7 100644 --- a/packages/data-context/src/sources/MigrationDataSource.ts +++ b/packages/data-context/src/sources/MigrationDataSource.ts @@ -14,6 +14,7 @@ import { getComponentTestFilesGlobs, getComponentFolder, } from './migration' +import _ from 'lodash' import type { FilePart } from './migration/format' import Debug from 'debug' @@ -152,7 +153,7 @@ export class MigrationDataSource { canBeAutomaticallyMigrated.push(...specs.component.map(applyMigrationTransform).filter((spec) => spec.before.relative !== spec.after.relative)) } - return canBeAutomaticallyMigrated + return this.checkAndUpdateDuplicatedSpecs(canBeAutomaticallyMigrated) } async createConfigString () { @@ -189,4 +190,65 @@ export class MigrationDataSource { get configFileNameAfterMigration () { return this.ctx.lifecycleManager.legacyConfigFile.replace('.json', `.config.${this.ctx.lifecycleManager.fileExtensionToUse}`) } + + private checkAndUpdateDuplicatedSpecs (specs: MigrationFile[]) { + const updatedSpecs: MigrationFile[] = [] + + const sortedSpecs = this.sortSpecsByExtension(specs) + + sortedSpecs.forEach((spec) => { + const specExist = _.find(updatedSpecs, (x) => x.after.relative === spec.after.relative) + + if (specExist) { + const beforeParts: FilePart[] = JSON.parse(JSON.stringify(spec.before.parts)) + const preExtensionBefore = beforeParts.find((part) => part.group === 'preExtension') + + if (preExtensionBefore) { + preExtensionBefore.highlight = false + } + + const afterParts: FilePart[] = JSON.parse(JSON.stringify(spec.after.parts)) + const fileNameAfter = afterParts.find((part) => part.group === 'fileName') + + if (fileNameAfter && preExtensionBefore) { + const beforePreExtension = preExtensionBefore?.text?.replace('.', '') + + fileNameAfter.text = `${fileNameAfter.text}${beforePreExtension}` + } + + spec.before.parts = beforeParts + spec.after.parts = afterParts + spec.after.relative = afterParts.map((x) => x.text).join('') + } + + updatedSpecs.push(spec) + }) + + return updatedSpecs + } + + private sortSpecsByExtension (specs: MigrationFile[]) { + const sortedExtensions = ['.spec.', '.Spec.', '_spec.', '_Spec.', '-spec.', '-Spec.', '.test.', '.Test.', '_test.', '_Test.', '-test.', '-Test.'] + + return specs.sort(function (a, b) { + function getExtIndex (spec: string) { + let index = -1 + + // Sort the specs based on the extension, giving priority to .spec + sortedExtensions.some((c, i) => { + if (~spec.indexOf(c)) { + index = i + + return true + } + + return false + }) + + return index + } + + return getExtIndex(a.before.relative) - getExtIndex(b.before.relative) + }) + } } diff --git a/packages/data-context/src/sources/migration/format.ts b/packages/data-context/src/sources/migration/format.ts index 94b1ebc5bb..64efa0562f 100644 --- a/packages/data-context/src/sources/migration/format.ts +++ b/packages/data-context/src/sources/migration/format.ts @@ -1,18 +1,11 @@ import dedent from 'dedent' -export interface FilePartNoHighlight { +export type FilePart = { text: string - highlight: false + group?: 'folder' | 'preExtension' | 'supportFileName' | 'fileName' + highlight: boolean } -export interface FilePartHighlight { - text: string - group: 'folder' | 'preExtension' | 'supportFileName' - highlight: true -} - -export type FilePart = FilePartNoHighlight | FilePartHighlight - export function formatMigrationFile (file: string, regexp: RegExp): FilePart[] { const match = regexp.exec(file) @@ -55,6 +48,7 @@ export function formatMigrationFile (file: string, regexp: RegExp): FilePart[] { { text: (folder ? '/' : '') + fileName, // user/cypress/integration/file.spec.tsx -> /file highlight: false, + group: 'fileName', }, { text: preExtension || '', // user/cypress/integration/file.spec.tsx -> .spec. diff --git a/packages/data-context/test/unit/sources/migration/autoRename.spec.ts b/packages/data-context/test/unit/sources/migration/autoRename.spec.ts index 5432d41aba..0569f98c85 100644 --- a/packages/data-context/test/unit/sources/migration/autoRename.spec.ts +++ b/packages/data-context/test/unit/sources/migration/autoRename.spec.ts @@ -167,6 +167,7 @@ describe('applyMigrationTransform', () => { { 'highlight': false, 'text': '/button', + 'group': 'fileName', }, { 'highlight': true, @@ -194,6 +195,7 @@ describe('applyMigrationTransform', () => { { 'highlight': false, 'text': '/button', + 'group': 'fileName', }, { 'highlight': true, @@ -230,6 +232,7 @@ describe('applyMigrationTransform', () => { { 'highlight': false, 'text': 'custom-folder/button', + 'group': 'fileName', }, { 'highlight': true, @@ -248,6 +251,7 @@ describe('applyMigrationTransform', () => { { 'highlight': false, 'text': 'custom-folder/button', + 'group': 'fileName', }, { 'highlight': true, @@ -293,6 +297,7 @@ describe('applyMigrationTransform', () => { { 'highlight': false, 'text': '/foo.bar', + 'group': 'fileName', }, ], }, @@ -311,6 +316,7 @@ describe('applyMigrationTransform', () => { { 'highlight': false, 'text': '/foo.bar', + 'group': 'fileName', }, ], }, @@ -347,6 +353,7 @@ describe('applyMigrationTransform', () => { { 'highlight': false, 'text': '/spec', + 'group': 'fileName', }, { 'highlight': true, @@ -374,6 +381,7 @@ describe('applyMigrationTransform', () => { { 'highlight': false, 'text': '/spec', + 'group': 'fileName', }, { 'highlight': true, @@ -412,6 +420,7 @@ describe('applyMigrationTransform', () => { { 'highlight': false, 'text': 'cypress/tests/api-bankaccounts', + 'group': 'fileName', }, { 'highlight': true, @@ -430,6 +439,7 @@ describe('applyMigrationTransform', () => { { 'highlight': false, 'text': 'cypress/tests/api-bankaccounts', + 'group': 'fileName', }, { 'highlight': true, @@ -466,6 +476,7 @@ describe('applyMigrationTransform', () => { { 'highlight': false, 'text': 'cypress/component/button', + 'group': 'fileName', }, { 'highlight': true, @@ -484,6 +495,7 @@ describe('applyMigrationTransform', () => { { 'highlight': false, 'text': 'cypress/component/button', + 'group': 'fileName', }, { 'highlight': true, @@ -522,6 +534,7 @@ describe('applyMigrationTransform', () => { { 'text': 'cypress/custom-component/button', 'highlight': false, + 'group': 'fileName', }, { 'text': '.spec.', @@ -540,6 +553,7 @@ describe('applyMigrationTransform', () => { { 'text': 'cypress/custom-component/button', 'highlight': false, + 'group': 'fileName', }, { 'text': '.cy.', diff --git a/packages/data-context/test/unit/sources/migration/format.spec.ts b/packages/data-context/test/unit/sources/migration/format.spec.ts index 8d9cad67bc..98b1c85a19 100644 --- a/packages/data-context/test/unit/sources/migration/format.spec.ts +++ b/packages/data-context/test/unit/sources/migration/format.spec.ts @@ -14,7 +14,7 @@ describe('formatMigrationFile', () => { expect(actual).to.eql([ { text: 'cypress/', highlight: false }, { text: 'integration', highlight: true, group: 'folder' }, - { text: '/app', highlight: false }, + { text: '/app', highlight: false, group: 'fileName' }, { text: '.spec.', highlight: true, group: 'preExtension' }, { text: 'js', highlight: false }, ]) diff --git a/packages/frontend-shared/cypress/e2e/support/e2eProjectDirs.ts b/packages/frontend-shared/cypress/e2e/support/e2eProjectDirs.ts index 893ea670f6..5f6ff0ed7d 100644 --- a/packages/frontend-shared/cypress/e2e/support/e2eProjectDirs.ts +++ b/packages/frontend-shared/cypress/e2e/support/e2eProjectDirs.ts @@ -57,6 +57,7 @@ export const e2eProjectDirs = [ 'migration-e2e-custom-test-files', 'migration-e2e-defaults', 'migration-e2e-defaults-no-specs', + 'migration-e2e-duplicated-spec-names', 'migration-e2e-export-default', 'migration-e2e-false-plugins-support-file', 'migration-e2e-fully-custom', diff --git a/packages/launchpad/cypress/e2e/migration.cy.ts b/packages/launchpad/cypress/e2e/migration.cy.ts index 2cf85894ba..43e24da287 100644 --- a/packages/launchpad/cypress/e2e/migration.cy.ts +++ b/packages/launchpad/cypress/e2e/migration.cy.ts @@ -743,6 +743,62 @@ describe('Full migration flow for each project', { retries: { openMode: 2, runMo migrateAndVerifyConfig() }) + it('completes journey for migration-e2e-duplicated-spec-names', () => { + startMigrationFor('migration-e2e-duplicated-spec-names') + // default testFiles - auto + cy.get(renameAutoStep).should('exist') + cy.get(configFileStep).should('exist') + + cy.get('[data-cy="migrate-before"]').within(() => { + cy.get('code').eq(0).should('contain', 'cypress/integration/app-spec2.js') + cy.get('code').eq(1).should('contain', 'cypress/integration/app_spec2.js') + cy.get('code').eq(2).should('contain', 'cypress/integration/app.spec.js') + cy.get('code').eq(3).should('contain', 'cypress/integration/app2_spec.js') + cy.get('code').eq(4).should('contain', 'cypress/integration/app_spec.js') + cy.get('code').eq(5).should('contain', 'cypress/integration/app-spec.js') + }) + + cy.get('[data-cy="migrate-after"]').within(() => { + cy.get('code').eq(0).should('contain', 'cypress/e2e/app-spec2.cy.js') + cy.get('code').eq(1).should('contain', 'cypress/e2e/app_spec2.cy.js') + cy.get('code').eq(2).should('contain', 'cypress/e2e/app.cy.js') + cy.get('code').eq(3).should('contain', 'cypress/e2e/app2.cy.js') + cy.get('code').eq(4).should('contain', 'cypress/e2e/app_spec.cy.js') + cy.get('code').eq(5).should('contain', 'cypress/e2e/app-spec.cy.js') + }) + + runAutoRename() + + cy.wait(100) + + cy.withCtx(async (ctx) => { + const specs = [ + 'cypress/e2e/app-spec2.cy.js', + 'cypress/e2e/app_spec2.cy.js', + 'cypress/e2e/app.cy.js', + 'cypress/e2e/app2.cy.js', + 'cypress/e2e/app_spec.cy.js', + 'cypress/e2e/app-spec.cy.js', + ] + + for (const spec of specs) { + const stats = await ctx.actions.file.checkIfFileExists(ctx.path.join(spec)) + + expect(stats).to.not.be.null + } + }) + + migrateAndVerifyConfig() + + cy.withCtx(async (ctx) => { + const integrationFolderStats = await ctx.actions.file.checkIfFileExists(ctx.path.join('cypress', 'integration')) + + expect(integrationFolderStats).to.be.null + }) + + checkOutcome() + }) + context('migration-e2e-component-default-test-files', () => { it('completes journey', () => { startMigrationFor('migration-e2e-component-default-test-files') diff --git a/system-tests/projects/migration-e2e-duplicated-spec-names/README.md b/system-tests/projects/migration-e2e-duplicated-spec-names/README.md new file mode 100644 index 0000000000..724928bad1 --- /dev/null +++ b/system-tests/projects/migration-e2e-duplicated-spec-names/README.md @@ -0,0 +1,13 @@ +## Migration E2E Defaults + +An e2e project with different types of spec names, that can create a collision while renaming. +We rename the `integrationFolder` and spec extension. + +The following migration steps will be used during this migration: + +- [x] automatic file rename +- [ ] manual file rename +- [ ] rename support +- [x] update config file +- [ ] setup component testing + diff --git a/system-tests/projects/migration-e2e-duplicated-spec-names/cypress.json b/system-tests/projects/migration-e2e-duplicated-spec-names/cypress.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/system-tests/projects/migration-e2e-duplicated-spec-names/cypress.json @@ -0,0 +1 @@ +{} diff --git a/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/integration/app-spec.js b/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/integration/app-spec.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/integration/app-spec2.js b/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/integration/app-spec2.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/integration/app.spec.js b/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/integration/app.spec.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/integration/app2_spec.js b/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/integration/app2_spec.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/integration/app_spec.js b/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/integration/app_spec.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/integration/app_spec2.js b/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/integration/app_spec2.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/plugins/index.js b/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/plugins/index.js new file mode 100644 index 0000000000..a61659e3ba --- /dev/null +++ b/system-tests/projects/migration-e2e-duplicated-spec-names/cypress/plugins/index.js @@ -0,0 +1,3 @@ +module.exports = (on, config) => { + +} diff --git a/system-tests/projects/migration-e2e-duplicated-spec-names/expected-cypress.config.js b/system-tests/projects/migration-e2e-duplicated-spec-names/expected-cypress.config.js new file mode 100644 index 0000000000..294e951392 --- /dev/null +++ b/system-tests/projects/migration-e2e-duplicated-spec-names/expected-cypress.config.js @@ -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) + }, + }, +})