fix: various minor migration bugs (#21195)

* correct indentation

* update formatting

* add comments

* update tsconfig.json

* Update packages/data-context/src/sources/migration/codegen.ts

Co-authored-by: Blue F <blue@cypress.io>

* Update packages/data-context/src/sources/migration/codegen.ts

Co-authored-by: Blue F <blue@cypress.io>

Co-authored-by: Blue F <blue@cypress.io>
This commit is contained in:
Lachlan Miller
2022-05-03 20:21:47 +10:00
committed by GitHub
parent 6179f7d067
commit d13db9073c
11 changed files with 131 additions and 19 deletions

View File

@@ -180,3 +180,16 @@ export default defineConfig({
})
`
exports['cypress.config.js generation generates correct config for component testing migration with custom testFiles glob 1'] = `
const { defineConfig } = require('cypress')
module.exports = defineConfig({
component: {
setupNodeEvents(on, config) {},
componentFolder: '.',
specPattern: './**/*.spec.cy.{js,ts,jsx,tsx}',
},
})
`

View File

@@ -15,6 +15,7 @@
},
"dependencies": {
"@babel/code-frame": "7.8.3",
"@babel/generator": "7.17.9",
"@babel/parser": "7.13.0",
"@urql/core": "2.3.1",
"@urql/exchange-execute": "1.1.0",

View File

@@ -184,9 +184,12 @@ export class MigrationActions {
const hasCustomComponentFolder = componentFolder !== 'cypress/component'
const hasCustomComponentTestFiles = !isDefaultTestFiles(legacyConfigForMigration, 'component')
const hasComponentTesting = componentFolder
? await hasSpecFile(this.ctx.currentProject, componentFolder, componentTestFiles)
: false
// A user is considered to "have" component testing if either
// 1. they have a default component folder (cypress/component) with at least 1 spec file
// OR
// 2. they have configured a non-default componentFolder (even if it doesn't have any specs.)
const hasSpecInDefaultComponentFolder = await hasSpecFile(this.ctx.currentProject, componentFolder, componentTestFiles)
const hasComponentTesting = (hasCustomComponentFolder || hasSpecInDefaultComponentFolder) ?? false
this.ctx.update((coreData) => {
coreData.migration.flags = {

View File

@@ -12,6 +12,8 @@ import Debug from 'debug'
import dedent from 'dedent'
import { hasDefaultExport } from './parserUtils'
import type { LegacyCypressConfigJson } from '..'
import { parse } from '@babel/parser'
import generate from '@babel/generator'
const debug = Debug('cypress:data-context:sources:migration:codegen')
@@ -168,18 +170,24 @@ function createCypressConfig (config: ConfigOptions, pluginPath: string | undefi
if (defineConfigAvailable(options.projectRoot)) {
if (options.hasTypescript) {
return formatConfig(
`import { defineConfig } from 'cypress'
return formatConfig(dedent`
import { defineConfig } from 'cypress'
export default defineConfig({${globalString}${e2eString}${componentString}})`,
)
export default defineConfig({
${globalString}
${e2eString}
${componentString}
})`)
}
return formatConfig(
`const { defineConfig } = require('cypress')
return formatConfig(dedent`
const { defineConfig } = require('cypress')
module.exports = defineConfig({${globalString}${e2eString}${componentString}})`,
)
module.exports = defineConfig({
${globalString}
${e2eString}
${componentString}
})`)
}
if (options.hasTypescript) {
@@ -245,7 +253,11 @@ export interface RelativeSpec {
*
* NOTE: this is what we use to see if CT/E2E is set up
*/
export async function hasSpecFile (projectRoot: string, folder: string, glob: string | string[]): Promise<boolean> {
export async function hasSpecFile (projectRoot: string, folder: string | false, glob: string | string[]): Promise<boolean> {
if (!folder) {
return false
}
return (await globby(glob, {
cwd: path.join(projectRoot, folder),
onlyFiles: true,
@@ -447,6 +459,65 @@ export function getSpecPattern (cfg: LegacyCypressConfigJson, testType: TestingT
return specPattern
}
function formatWithBundledBabel (config: string) {
const ast = parse(config)
let { code } = generate(ast, {}, config)
// By default babel generates imports like this:
// const {
// defineConfig
// } = require('cypress');
// So we replace them with a one-liner, since we know this will never
// be more than one import.
//
// Babel also adds empty lines, for example:
//
// export default defineConfig({
// component: {
// },
// <===== empty line
// e2e: {
//
// }
// })
// Which we don't want, so we change those to single carriage returns.
const replacers = [
{
from: dedent`
const {
defineConfig
} = require('cypress');
`,
to: dedent`
const { defineConfig } = require('cypress');
`,
},
{
from: dedent`
import {
defineConfig
} from 'cypress';
`,
to: dedent`
import { defineConfig } from 'cypress';
`,
},
{
from: `,\n\n`,
to: `,\n`,
},
]
for (const rep of replacers) {
if (code.includes(rep.from)) {
code = code.replaceAll(rep.from, rep.to)
}
}
return code
}
export function formatConfig (config: string): string {
try {
const prettier = require('prettier') as typeof import('prettier')
@@ -458,6 +529,11 @@ export function formatConfig (config: string): string {
parser: 'babel',
})
} catch (e) {
return config
// If they do not have prettier
// We do a basic format using babel, which we
// bundle as part of the binary.
// We don't ship a fully fledged formatter like
// prettier, since it's massively bloats the bundle.
return formatWithBundledBabel(config)
}
}

View File

@@ -48,7 +48,7 @@ export function getIntegrationFolder (config: LegacyCypressConfigJson) {
return config.e2e?.integrationFolder ?? config.integrationFolder ?? 'cypress/integration'
}
export function getComponentFolder (config: LegacyCypressConfigJson) {
export function getComponentFolder (config: LegacyCypressConfigJson): false | string {
if (config.component?.componentFolder === false || config.componentFolder === false) {
return false
}

View File

@@ -17,6 +17,25 @@ import { scaffoldMigrationProject, getSystemTestProject } from '../../helper'
const projectRoot = getSystemTestProject('migration-e2e-defaults')
describe('cypress.config.js generation', () => {
it('generates correct config for component testing migration with custom testFiles glob', async () => {
const config = {
component: {
testFiles: '**/*.spec.cy.{js,ts,jsx,tsx}',
componentFolder: '.',
},
}
const generatedConfig = await createConfigString(config, {
hasE2ESpec: false,
hasComponentTesting: true,
hasPluginsFile: false,
projectRoot,
hasTypescript: false,
})
snapshot(generatedConfig)
})
it('should create a string when passed only a global option', async () => {
const config: Partial<Cypress.Config> = {
viewportWidth: 300,

View File

@@ -8,7 +8,7 @@
"./../ts/index.d.ts"
],
"compilerOptions": {
"lib": ["es2020"],
"lib": ["esnext"],
"strict": true,
"allowJs": false,
"noImplicitAny": true,

View File

@@ -9,7 +9,7 @@
"script"
],
"compilerOptions": {
"lib": ["es2020"],
"lib": ["esnext"],
"strict": true,
"allowJs": false,
"noImplicitAny": true,

View File

@@ -9,7 +9,7 @@
"./../ts/index.d.ts"
],
"compilerOptions": {
"lib": ["es2020"],
"lib": ["esnext"],
"strict": true,
"allowJs": false,
"noImplicitAny": true,

View File

@@ -8,7 +8,7 @@
"./../ts/index.d.ts"
],
"compilerOptions": {
"lib": ["es2020"],
"lib": ["esnext"],
"types": [
"mocha",
"node"

View File

@@ -1302,7 +1302,7 @@
jsesc "^2.5.1"
source-map "^0.5.0"
"@babel/generator@^7.12.10", "@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.15.4", "@babel/generator@^7.15.8", "@babel/generator@^7.17.9", "@babel/generator@^7.4.0", "@babel/generator@^7.4.4", "@babel/generator@^7.5.0", "@babel/generator@^7.6.0", "@babel/generator@^7.8.3", "@babel/generator@^7.9.0":
"@babel/generator@7.17.9", "@babel/generator@^7.12.10", "@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.15.4", "@babel/generator@^7.15.8", "@babel/generator@^7.17.9", "@babel/generator@^7.4.0", "@babel/generator@^7.4.4", "@babel/generator@^7.5.0", "@babel/generator@^7.6.0", "@babel/generator@^7.8.3", "@babel/generator@^7.9.0":
version "7.17.9"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.9.tgz#f4af9fd38fa8de143c29fce3f71852406fc1e2fc"
integrity sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==