From d13db9073c3fe36145ecdfd18c179fa8411b9cd9 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Tue, 3 May 2022 20:21:47 +1000 Subject: [PATCH] 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 * Update packages/data-context/src/sources/migration/codegen.ts Co-authored-by: Blue F Co-authored-by: Blue F --- .../__snapshots__/codegen.spec.ts.js | 13 +++ packages/data-context/package.json | 1 + .../src/actions/MigrationActions.ts | 9 +- .../src/sources/migration/codegen.ts | 96 +++++++++++++++++-- .../src/sources/migration/shouldShowSteps.ts | 2 +- .../unit/sources/migration/codegen.spec.ts | 19 ++++ packages/data-context/tsconfig.json | 2 +- packages/graphql/tsconfig.json | 2 +- packages/scaffold-config/tsconfig.json | 2 +- packages/server/tsconfig.json | 2 +- yarn.lock | 2 +- 11 files changed, 131 insertions(+), 19 deletions(-) diff --git a/packages/data-context/__snapshots__/codegen.spec.ts.js b/packages/data-context/__snapshots__/codegen.spec.ts.js index 07744beda7..621794bfc4 100644 --- a/packages/data-context/__snapshots__/codegen.spec.ts.js +++ b/packages/data-context/__snapshots__/codegen.spec.ts.js @@ -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}', + }, +}) + +` diff --git a/packages/data-context/package.json b/packages/data-context/package.json index 19d75af4fa..c1bf865b39 100644 --- a/packages/data-context/package.json +++ b/packages/data-context/package.json @@ -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", diff --git a/packages/data-context/src/actions/MigrationActions.ts b/packages/data-context/src/actions/MigrationActions.ts index 705c5ac51b..8643797ccb 100644 --- a/packages/data-context/src/actions/MigrationActions.ts +++ b/packages/data-context/src/actions/MigrationActions.ts @@ -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 = { diff --git a/packages/data-context/src/sources/migration/codegen.ts b/packages/data-context/src/sources/migration/codegen.ts index 5894db14bc..dd94f7c585 100644 --- a/packages/data-context/src/sources/migration/codegen.ts +++ b/packages/data-context/src/sources/migration/codegen.ts @@ -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 { +export async function hasSpecFile (projectRoot: string, folder: string | false, glob: string | string[]): Promise { + 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) } } diff --git a/packages/data-context/src/sources/migration/shouldShowSteps.ts b/packages/data-context/src/sources/migration/shouldShowSteps.ts index 0dd14f0aca..b882d21035 100644 --- a/packages/data-context/src/sources/migration/shouldShowSteps.ts +++ b/packages/data-context/src/sources/migration/shouldShowSteps.ts @@ -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 } diff --git a/packages/data-context/test/unit/sources/migration/codegen.spec.ts b/packages/data-context/test/unit/sources/migration/codegen.spec.ts index a8d6b61bfe..cedb07ce6f 100644 --- a/packages/data-context/test/unit/sources/migration/codegen.spec.ts +++ b/packages/data-context/test/unit/sources/migration/codegen.spec.ts @@ -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 = { viewportWidth: 300, diff --git a/packages/data-context/tsconfig.json b/packages/data-context/tsconfig.json index 416a5b29f7..5dfb0e8b80 100644 --- a/packages/data-context/tsconfig.json +++ b/packages/data-context/tsconfig.json @@ -8,7 +8,7 @@ "./../ts/index.d.ts" ], "compilerOptions": { - "lib": ["es2020"], + "lib": ["esnext"], "strict": true, "allowJs": false, "noImplicitAny": true, diff --git a/packages/graphql/tsconfig.json b/packages/graphql/tsconfig.json index 594d9b4f6c..f94e592856 100644 --- a/packages/graphql/tsconfig.json +++ b/packages/graphql/tsconfig.json @@ -9,7 +9,7 @@ "script" ], "compilerOptions": { - "lib": ["es2020"], + "lib": ["esnext"], "strict": true, "allowJs": false, "noImplicitAny": true, diff --git a/packages/scaffold-config/tsconfig.json b/packages/scaffold-config/tsconfig.json index f07e5594e5..584ed28691 100644 --- a/packages/scaffold-config/tsconfig.json +++ b/packages/scaffold-config/tsconfig.json @@ -9,7 +9,7 @@ "./../ts/index.d.ts" ], "compilerOptions": { - "lib": ["es2020"], + "lib": ["esnext"], "strict": true, "allowJs": false, "noImplicitAny": true, diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json index ca8643188c..5a24150420 100644 --- a/packages/server/tsconfig.json +++ b/packages/server/tsconfig.json @@ -8,7 +8,7 @@ "./../ts/index.d.ts" ], "compilerOptions": { - "lib": ["es2020"], + "lib": ["esnext"], "types": [ "mocha", "node" diff --git a/yarn.lock b/yarn.lock index 4de7aca1f2..d10f4c3ae6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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==