diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c7063fa..d145434 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,18 +1,18 @@ version: 2 updates: - - package-ecosystem: 'npm' - directory: '/' + - package-ecosystem: "npm" + directory: "/" schedule: - interval: 'weekly' + interval: "weekly" groups: babel: patterns: - - '@babel/*' + - "@babel/*" reviewers: - - '@ExpediaGroup/cypress-codegen-admins' - - package-ecosystem: 'github-actions' - directory: '/' + - "@ExpediaGroup/cypress-codegen-admins" + - package-ecosystem: "github-actions" + directory: "/" schedule: - interval: 'weekly' + interval: "weekly" reviewers: - - '@ExpediaGroup/cypress-codegen-admins' + - "@ExpediaGroup/cypress-codegen-admins" diff --git a/.prettierignore b/.prettierignore index ee89780..3235ec8 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,3 @@ node_modules pnpm-lock.yaml +cypress diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index bdef944..0000000 --- a/.prettierrc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "printWidth": 100, - "tabWidth": 2, - "useTabs": false, - "singleQuote": true, - "trailingComma": "none", - "bracketSpacing": true, - "arrowParens": "avoid" -} diff --git a/.releaserc.yaml b/.releaserc.yaml index 6ee3a74..5725597 100644 --- a/.releaserc.yaml +++ b/.releaserc.yaml @@ -1,5 +1,5 @@ plugins: - - - '@semantic-release/commit-analyzer' + - - "@semantic-release/commit-analyzer" - preset: angular releaseRules: - breaking: true @@ -12,7 +12,7 @@ plugins: release: patch - scope: no-release release: false - - '@semantic-release/release-notes-generator' - - '@semantic-release/github' + - "@semantic-release/release-notes-generator" + - "@semantic-release/github" branches: - main diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 72fc849..607bd23 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,7 +48,7 @@ Push your changes to your branch and open a pull request against the parent repo Upon Pull Request submission, your code will be reviewed by the maintainers. They will confirm at least the following: -- Tests run successfully (unit, coverage, integration, style). -- Contribution policy has been followed. +- Tests run successfully (unit, coverage, integration, style). +- Contribution policy has been followed. One (human) reviewer will need to sign off on your Pull Request before it can be merged. diff --git a/README.md b/README.md index a9edf27..181ea41 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ A [Cypress](https://www.cypress.io/) plugin and CLI tool which automatically add ## Table of Contents -- [Why Do I Need This Plugin?](#why-do-i-need-this-plugin) -- [Installation](#installation) -- [Usage](#usage) -- [Example](#example) -- [Configuration](#configuration) +- [Why Do I Need This Plugin?](#why-do-i-need-this-plugin) +- [Installation](#installation) +- [Usage](#usage) +- [Example](#example) +- [Configuration](#configuration) ## Why Do I Need This Plugin? @@ -33,27 +33,27 @@ Model your Cypress project exactly like [the one in this repository](https://git 1. Add the required plugin code to `cypress.config.ts` like so: ```ts -import { cypressCodegen } from 'cypress-codegen'; -import { defineConfig } from 'cypress'; +import { cypressCodegen } from "cypress-codegen"; +import { defineConfig } from "cypress"; export default defineConfig({ - e2e: { - setupNodeEvents(on, config) { - cypressCodegen(on, config); - return config; - } - }, - - component: { - setupNodeEvents(on, config) { - cypressCodegen(on, config); - return config; + e2e: { + setupNodeEvents(on, config) { + cypressCodegen(on, config); + return config; + }, + }, + + component: { + setupNodeEvents(on, config) { + cypressCodegen(on, config); + return config; + }, + devServer: { + framework: "react", + bundler: "vite", + }, }, - devServer: { - framework: 'react', - bundler: 'vite' - } - } }); ``` diff --git a/cypress.config.ts b/cypress.config.ts index eeaa118..7054b27 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,26 +1,32 @@ -import { cypressCodegen } from 'cypress-codegen'; -import { defineConfig } from 'cypress'; +import { cypressCodegen } from "cypress-codegen"; +import { defineConfig } from "cypress"; export default defineConfig({ screenshotOnRunFailure: false, video: false, e2e: { - setupNodeEvents(on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) { + setupNodeEvents( + on: Cypress.PluginEvents, + config: Cypress.PluginConfigOptions, + ) { cypressCodegen(on, config); return config; - } + }, }, component: { devServer: { - framework: 'react', - bundler: 'vite', - viteConfig: {} + framework: "react", + bundler: "vite", + viteConfig: {}, }, - setupNodeEvents(on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) { + setupNodeEvents( + on: Cypress.PluginEvents, + config: Cypress.PluginConfigOptions, + ) { cypressCodegen(on, config); return config; - } - } + }, + }, }); diff --git a/cypress/commands/custom-mount.tsx b/cypress/commands/custom-mount.tsx index 5fc9046..4b71359 100644 --- a/cypress/commands/custom-mount.tsx +++ b/cypress/commands/custom-mount.tsx @@ -1,10 +1,10 @@ -import * as React from 'react'; +import * as React from "react"; export const customMount = (Component: React.FC) => { cy.mount(
-
+ , ); }; diff --git a/cypress/commands/example.ts b/cypress/commands/example.ts index 42e6e4d..c0a0690 100644 --- a/cypress/commands/example.ts +++ b/cypress/commands/example.ts @@ -12,14 +12,14 @@ limitations under the License. */ export function functionExample(input: string) { - cy.log('Here is a custom command!') - .log('And it preserves my code styling') - .log('When I chain commands on new lines!'); + cy.log("Here is a custom command!") + .log("And it preserves my code styling") + .log("When I chain commands on new lines!"); cy.contains(input); } export const arrowFunctionExample = (input: string) => { - cy.log('Here is a custom command from an arrow function!'); + cy.log("Here is a custom command from an arrow function!"); cy.contains(input); }; diff --git a/cypress/commands/index.ts b/cypress/commands/index.ts index 898b127..247b125 100644 --- a/cypress/commands/index.ts +++ b/cypress/commands/index.ts @@ -1,4 +1,4 @@ /** Generated by cypress-codegen **/ -export * from './example'; -export * from './custom-mount'; -export * from './nested/nested-example'; +export * from "./example"; +export * from "./custom-mount"; +export * from "./nested/nested-example"; diff --git a/cypress/commands/nested/nested-example.ts b/cypress/commands/nested/nested-example.ts index d97a899..a881e52 100644 --- a/cypress/commands/nested/nested-example.ts +++ b/cypress/commands/nested/nested-example.ts @@ -12,7 +12,7 @@ limitations under the License. */ export const nestedExample = (input: string) => { - cy.log('Here is a custom command in a nested directory!'); + cy.log("Here is a custom command in a nested directory!"); cy.contains(input); }; diff --git a/cypress/component/component-example.cy.tsx b/cypress/component/component-example.cy.tsx index 7ae45c2..0409b70 100644 --- a/cypress/component/component-example.cy.tsx +++ b/cypress/component/component-example.cy.tsx @@ -11,37 +11,37 @@ See the License for the specific language governing permissions and limitations under the License. */ -import * as React from 'react'; - -const componentText = 'Here is a component'; - -describe('Example Test', () => { - beforeEach(() => { - cy.mount(

{componentText}

); - }); - - it('should import custom commands in component tests', () => { - cy.functionExample(componentText); - }); - - it('should dynamically import custom commands from arrow functions', () => { - cy.arrowFunctionExample(componentText); - }); - - it('should dynamically import nested custom commands', () => { - cy.nestedExample(componentText); - }); - - it('should chain custom commands', () => { - cy.log(componentText) - .functionExample(componentText) - .arrowFunctionExample(componentText) - .nestedExample(componentText); - }); - - it('should support custom mount commands', () => { - const myComponent = () => <>{'Different text'}; - cy.customMount(myComponent); - cy.contains('Different text'); - }); +import * as React from "react"; + +const componentText = "Here is a component"; + +describe("Example Test", () => { + beforeEach(() => { + cy.mount(

{componentText}

); + }); + + it("should import custom commands in component tests", () => { + cy.functionExample(componentText); + }); + + it("should dynamically import custom commands from arrow functions", () => { + cy.arrowFunctionExample(componentText); + }); + + it("should dynamically import nested custom commands", () => { + cy.nestedExample(componentText); + }); + + it("should chain custom commands", () => { + cy.log(componentText) + .functionExample(componentText) + .arrowFunctionExample(componentText) + .nestedExample(componentText); + }); + + it("should support custom mount commands", () => { + const myComponent = () => <>{"Different text"}; + cy.customMount(myComponent); + cy.contains("Different text"); + }); }); diff --git a/cypress/e2e/e2e-example.cy.ts b/cypress/e2e/e2e-example.cy.ts index 5d902e2..37b3dfb 100644 --- a/cypress/e2e/e2e-example.cy.ts +++ b/cypress/e2e/e2e-example.cy.ts @@ -11,26 +11,26 @@ See the License for the specific language governing permissions and limitations under the License. */ -const expectedText = 'Kitchen Sink'; +const expectedText = "Kitchen Sink"; -describe('Example Test', () => { +describe("Example Test", () => { beforeEach(() => { - cy.visit('https://example.cypress.io/'); + cy.visit("https://example.cypress.io/"); }); - it('should dynamically import custom commands from functions', () => { + it("should dynamically import custom commands from functions", () => { cy.functionExample(expectedText); }); - it('should dynamically import custom commands from arrow functions', () => { + it("should dynamically import custom commands from arrow functions", () => { cy.arrowFunctionExample(expectedText); }); - it('should dynamically import nested custom commands', () => { + it("should dynamically import nested custom commands", () => { cy.nestedExample(expectedText); }); - it('should chain custom commands', () => { + it("should chain custom commands", () => { cy.log(expectedText) .functionExample(expectedText) .arrowFunctionExample(expectedText) diff --git a/cypress/support/component-index.html b/cypress/support/component-index.html index faf3b5f..54c03d5 100644 --- a/cypress/support/component-index.html +++ b/cypress/support/component-index.html @@ -1,12 +1,12 @@ - - - - - Components App - - -
- + + + + + Components App + + +
+ diff --git a/cypress/support/component.ts b/cypress/support/component.ts index 0967dfc..5153413 100644 --- a/cypress/support/component.ts +++ b/cypress/support/component.ts @@ -11,7 +11,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mount } from 'cypress/react18'; +import { mount } from "cypress/react18"; // Augment the Cypress namespace to include type definitions for // your custom command. @@ -25,7 +25,7 @@ declare global { } } -Cypress.Commands.add('mount', mount); +Cypress.Commands.add("mount", mount); /** Generated by cypress-codegen **/ -import('../commands').then(Cypress.Commands.addAll); +import("../commands").then(Cypress.Commands.addAll); diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts index ba357b5..06d1855 100644 --- a/cypress/support/e2e.ts +++ b/cypress/support/e2e.ts @@ -1,2 +1,2 @@ /** Generated by cypress-codegen **/ -import('../commands').then(Cypress.Commands.addAll); +import("../commands").then(Cypress.Commands.addAll); diff --git a/packages/cypress-codegen/.eslintrc.json b/packages/cypress-codegen/.eslintrc.json index 668607c..f680bf9 100644 --- a/packages/cypress-codegen/.eslintrc.json +++ b/packages/cypress-codegen/.eslintrc.json @@ -47,8 +47,7 @@ "github/no-then": "off", "i18n-text/no-en": "off", "import/no-unresolved": "off", - "filenames/match-regex": [2, "(^[a-z-]+$)|(^[a-z-]+\\.test$)"], - "quotes": ["error", "single"] + "filenames/match-regex": [2, "(^[a-z-]+$)|(^[a-z-]+\\.test$)"] }, "env": { "node": true, diff --git a/packages/cypress-codegen/package.json b/packages/cypress-codegen/package.json index 13bd053..bd29477 100644 --- a/packages/cypress-codegen/package.json +++ b/packages/cypress-codegen/package.json @@ -29,7 +29,7 @@ "chalk": "4.1.2", "commander": "11.1.0", "glob": "10.3.10", - "prettier": "3.0.3" + "prettier": "3.1.1" }, "peerDependencies": { "cypress": ">=12" diff --git a/packages/cypress-codegen/reset.d.ts b/packages/cypress-codegen/reset.d.ts index c5f48f7..0615bc9 100644 --- a/packages/cypress-codegen/reset.d.ts +++ b/packages/cypress-codegen/reset.d.ts @@ -1,2 +1,2 @@ // https://github.com/total-typescript/ts-reset -import '@total-typescript/ts-reset'; +import "@total-typescript/ts-reset"; diff --git a/packages/cypress-codegen/src/cli.ts b/packages/cypress-codegen/src/cli.ts index 24d6826..884e704 100755 --- a/packages/cypress-codegen/src/cli.ts +++ b/packages/cypress-codegen/src/cli.ts @@ -1,27 +1,34 @@ #!/usr/bin/env node /* eslint-disable no-console */ -import * as chalk from 'chalk'; -import { program, Option } from 'commander'; -import { codegen } from './codegen'; +import * as chalk from "chalk"; +import { program, Option } from "commander"; +import { codegen } from "./codegen"; program .addOption( - new Option('--testingType ', 'Overrides the default Cypress support file.') - .choices(['component', 'e2e']) - .default('e2e') + new Option( + "--testingType ", + "Overrides the default Cypress support file.", + ) + .choices(["component", "e2e"]) + .default("e2e"), ) .parse(process.argv); const { testingType } = program.opts(); -console.log(chalk.yellowBright(`Generating custom command types for ${testingType} tests...`)); +console.log( + chalk.yellowBright( + `Generating custom command types for ${testingType} tests...`, + ), +); codegen({ testingType }) .then(() => { - console.log(chalk.bgGreen('Codegen complete!')); + console.log(chalk.bgGreen("Codegen complete!")); }) - .catch(error => { - console.error(chalk.bgRed('Codegen failed!')); + .catch((error) => { + console.error(chalk.bgRed("Codegen failed!")); console.error(error); }); diff --git a/packages/cypress-codegen/src/codegen.ts b/packages/cypress-codegen/src/codegen.ts index db54dcc..9d641da 100644 --- a/packages/cypress-codegen/src/codegen.ts +++ b/packages/cypress-codegen/src/codegen.ts @@ -1,13 +1,16 @@ -import { globSync } from 'glob'; -import { resolveConfig } from 'prettier'; -import { generateContentWithExports } from './generate-content-with-exports'; -import { generateContentsWithInterface } from './generate-contents-with-interface'; -import { generateContentWithImports } from './generate-content-with-imports'; -import { writeFileSync } from 'fs'; -import { resolve } from 'path'; +import { globSync } from "glob"; +import { resolveConfig } from "prettier"; +import { generateContentWithExports } from "./generate-content-with-exports"; +import { generateContentsWithInterface } from "./generate-contents-with-interface"; +import { generateContentWithImports } from "./generate-content-with-imports"; +import { writeFileSync } from "fs"; +import { resolve } from "path"; -export const cypressCodegen = (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => { - on('before:browser:launch', async (browser, launchOptions) => { +export const cypressCodegen = ( + on: Cypress.PluginEvents, + config: Cypress.PluginConfigOptions, +) => { + on("before:browser:launch", async (browser, launchOptions) => { await codegen(config); return launchOptions; @@ -17,22 +20,35 @@ export const cypressCodegen = (on: Cypress.PluginEvents, config: Cypress.PluginC }; export const codegen = async (config: Partial) => { - const indexTsFile = 'cypress/commands/index.ts'; - const filePaths = globSync('cypress/commands/**/*', { nodir: true, ignore: indexTsFile }); + const indexTsFile = "cypress/commands/index.ts"; + const filePaths = globSync("cypress/commands/**/*", { + nodir: true, + ignore: indexTsFile, + }); const prettierConfig = (await resolveConfig(process.cwd())) ?? {}; - const commandsIndexPath = 'cypress/commands/index.ts'; - const exportFileContents = await generateContentWithExports(filePaths, prettierConfig); + const commandsIndexPath = "cypress/commands/index.ts"; + const exportFileContents = await generateContentWithExports( + filePaths, + prettierConfig, + ); writeFileSync(resolve(commandsIndexPath), exportFileContents); - const supportFile = config.supportFile || `cypress/support/${config.testingType}.ts`; - const fileContentsWithImports = await generateContentWithImports(supportFile, prettierConfig); + const supportFile = + config.supportFile || `cypress/support/${config.testingType}.ts`; + const fileContentsWithImports = await generateContentWithImports( + supportFile, + prettierConfig, + ); writeFileSync(resolve(supportFile), fileContentsWithImports); await Promise.all( - filePaths.map(async filePath => { - const fileContentsWithTypes = await generateContentsWithInterface(filePath, prettierConfig); + filePaths.map(async (filePath) => { + const fileContentsWithTypes = await generateContentsWithInterface( + filePath, + prettierConfig, + ); writeFileSync(resolve(filePath), fileContentsWithTypes); - }) + }), ); }; diff --git a/packages/cypress-codegen/src/generate-content-with-exports.ts b/packages/cypress-codegen/src/generate-content-with-exports.ts index d213764..19e0311 100644 --- a/packages/cypress-codegen/src/generate-content-with-exports.ts +++ b/packages/cypress-codegen/src/generate-content-with-exports.ts @@ -11,23 +11,33 @@ See the License for the specific language governing permissions and limitations under the License. */ -import * as t from '@babel/types'; -import generate from '@babel/generator'; -import { format, Options } from 'prettier'; -import { join, parse, relative } from 'path'; +import * as t from "@babel/types"; +import generate from "@babel/generator"; +import { format, Options } from "prettier"; +import { join, parse, relative } from "path"; -export const generateContentWithExports = async (filePaths: string[], prettierConfig: Options) => { - const exportStatements = filePaths.map(filePath => { +export const generateContentWithExports = async ( + filePaths: string[], + prettierConfig: Options, +) => { + const exportStatements = filePaths.map((filePath) => { const { dir, name } = parse(filePath); const pathWithoutExtension = join(dir, name); - const relativePath = `./${relative('cypress/commands', pathWithoutExtension)}`; + const relativePath = `./${relative( + "cypress/commands", + pathWithoutExtension, + )}`; return t.exportAllDeclaration(t.stringLiteral(relativePath)); }); - t.addComment(exportStatements[0], 'leading', '* Generated by cypress-codegen *'); + t.addComment( + exportStatements[0], + "leading", + "* Generated by cypress-codegen *", + ); const { code: newCode } = generate(t.program(exportStatements)); const resultingCode = `${newCode}\n`; return await format(resultingCode, { - parser: 'babel-ts', - ...prettierConfig + parser: "babel-ts", + ...prettierConfig, }); }; diff --git a/packages/cypress-codegen/src/generate-content-with-imports.ts b/packages/cypress-codegen/src/generate-content-with-imports.ts index 4a6d142..22622fb 100644 --- a/packages/cypress-codegen/src/generate-content-with-imports.ts +++ b/packages/cypress-codegen/src/generate-content-with-imports.ts @@ -11,47 +11,55 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { parse } from '@babel/parser'; -import * as t from '@babel/types'; -import generate from '@babel/generator'; -import { readFileSync } from 'fs'; -import { resolve } from 'path'; -import { format, Options } from 'prettier'; +import { parse } from "@babel/parser"; +import * as t from "@babel/types"; +import generate from "@babel/generator"; +import { readFileSync } from "fs"; +import { resolve } from "path"; +import { format, Options } from "prettier"; -export const generateContentWithImports = async (filePath: string, prettierConfig: Options) => { +export const generateContentWithImports = async ( + filePath: string, + prettierConfig: Options, +) => { const contents = readFileSync(resolve(filePath)).toString(); - const ast = parse(contents, { sourceType: 'module', plugins: ['typescript'] }); + const ast = parse(contents, { + sourceType: "module", + plugins: ["typescript"], + }); const currentNodes = ast.program.body; - const importsExist = contents.includes('* Generated by cypress-codegen *'); + const importsExist = contents.includes("* Generated by cypress-codegen *"); if (importsExist) { return contents; } - const { code: existingCode } = generate(t.program(currentNodes), { retainLines: true }); + const { code: existingCode } = generate(t.program(currentNodes), { + retainLines: true, + }); const newImportStatement = generateImportStatement(); const { code: newCode } = generate(t.program(newImportStatement)); const resultingCode = `${existingCode}\n\n${newCode}\n`; return await format(resultingCode, { - parser: 'babel-ts', - ...prettierConfig + parser: "babel-ts", + ...prettierConfig, }); }; const generateImportStatement = () => { - const importExpression = t.callExpression(t.identifier('import'), [ - t.stringLiteral('../commands') + const importExpression = t.callExpression(t.identifier("import"), [ + t.stringLiteral("../commands"), ]); const cypressExpression = t.memberExpression( - t.memberExpression(t.identifier('Cypress'), t.identifier('Commands')), - t.identifier('addAll') + t.memberExpression(t.identifier("Cypress"), t.identifier("Commands")), + t.identifier("addAll"), ); const fullExpression = t.callExpression( - t.memberExpression(importExpression, t.identifier('then')), - [cypressExpression] + t.memberExpression(importExpression, t.identifier("then")), + [cypressExpression], ); const importStatement = t.expressionStatement(fullExpression); - t.addComment(importStatement, 'leading', '* Generated by cypress-codegen *'); + t.addComment(importStatement, "leading", "* Generated by cypress-codegen *"); return [importStatement]; }; diff --git a/packages/cypress-codegen/src/generate-contents-with-interface.ts b/packages/cypress-codegen/src/generate-contents-with-interface.ts index f25adf7..2c300ca 100644 --- a/packages/cypress-codegen/src/generate-contents-with-interface.ts +++ b/packages/cypress-codegen/src/generate-contents-with-interface.ts @@ -11,32 +11,39 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { parse } from '@babel/parser'; +import { parse } from "@babel/parser"; import type { AssignmentPattern, ExportNamedDeclaration, FunctionDeclaration, FunctionExpression, Identifier, - VariableDeclaration -} from '@babel/types'; -import * as t from '@babel/types'; -import generate from '@babel/generator'; -import { readFileSync } from 'fs'; -import { resolve } from 'path'; -import { format, Options } from 'prettier'; + VariableDeclaration, +} from "@babel/types"; +import * as t from "@babel/types"; +import generate from "@babel/generator"; +import { readFileSync } from "fs"; +import { resolve } from "path"; +import { format, Options } from "prettier"; -export const generateContentsWithInterface = async (filePath: string, prettierConfig: Options) => { +export const generateContentsWithInterface = async ( + filePath: string, + prettierConfig: Options, +) => { const contents = readFileSync(resolve(filePath)).toString(); - const ast = parse(contents, { sourceType: 'module', plugins: ['typescript', 'jsx'] }); + const ast = parse(contents, { + sourceType: "module", + plugins: ["typescript", "jsx"], + }); const currentNodes = ast.program.body; const customCommands = currentNodes .filter( - node => + (node) => t.isExportNamedDeclaration(node) && - (t.isFunctionDeclaration(node.declaration) || t.isVariableDeclaration(node.declaration)) + (t.isFunctionDeclaration(node.declaration) || + t.isVariableDeclaration(node.declaration)), ) - .map(node => { + .map((node) => { const exportNamedDeclaration = node as ExportNamedDeclaration; const declaration = exportNamedDeclaration.declaration as | FunctionDeclaration @@ -48,7 +55,7 @@ export const generateContentsWithInterface = async (filePath: string, prettierCo const functionParameters = isVariableDeclaration ? (declaration.declarations[0]?.init as FunctionExpression).params : declaration.params; - const parameters = functionParameters.map(parameter => { + const parameters = functionParameters.map((parameter) => { if (t.isAssignmentPattern(parameter)) { return generateOptionalParameterFromInitializer(parameter); } @@ -56,23 +63,31 @@ export const generateContentsWithInterface = async (filePath: string, prettierCo }); return { functionIdentifier, - parameters + parameters, }; }); const lastNode = currentNodes[currentNodes.length - 1]; const interfaceExists = - lastNode?.leadingComments?.[0]?.value === '* Generated by cypress-codegen *'; - const newNodes = interfaceExists ? currentNodes.slice(0, currentNodes.length - 1) : currentNodes; - const { code: existingCode } = generate(t.program(newNodes), { retainLines: true }); - const newInterface = generateInterface(customCommands as CustomCommand[], interfaceExists); + lastNode?.leadingComments?.[0]?.value === + "* Generated by cypress-codegen *"; + const newNodes = interfaceExists + ? currentNodes.slice(0, currentNodes.length - 1) + : currentNodes; + const { code: existingCode } = generate(t.program(newNodes), { + retainLines: true, + }); + const newInterface = generateInterface( + customCommands as CustomCommand[], + interfaceExists, + ); const { code: newCode } = generate(t.program(newInterface)); - const separator = interfaceExists ? '\n' : '\n\n'; + const separator = interfaceExists ? "\n" : "\n\n"; const resultingCode = customCommands.length ? `${existingCode}${separator}${newCode}\n` : `${existingCode}\n`; return await format(resultingCode, { - parser: 'babel-ts', - ...prettierConfig + parser: "babel-ts", + ...prettierConfig, }); }; @@ -81,45 +96,56 @@ interface CustomCommand { parameters: Identifier[]; } -const generateOptionalParameterFromInitializer = ({ left }: AssignmentPattern): Identifier => ({ +const generateOptionalParameterFromInitializer = ({ + left, +}: AssignmentPattern): Identifier => ({ ...(left as Identifier), - type: 'Identifier', - optional: true + type: "Identifier", + optional: true, }); -const generateInterface = (customCommands: CustomCommand[], interfaceExists: boolean) => { +const generateInterface = ( + customCommands: CustomCommand[], + interfaceExists: boolean, +) => { const interfaceNode = t.tsModuleDeclaration( - t.identifier('global'), + t.identifier("global"), t.tsModuleBlock([ t.tsModuleDeclaration( - t.identifier('Cypress'), + t.identifier("Cypress"), t.tsModuleBlock([ t.tsInterfaceDeclaration( - t.identifier('Chainable'), + t.identifier("Chainable"), null, null, t.tsInterfaceBody( customCommands.map(({ functionIdentifier, parameters }) => { - if (parameters.some(parameter => t.isObjectPattern(parameter))) { - throw new Error('Object destructuring in function parameters is not supported.'); + if ( + parameters.some((parameter) => t.isObjectPattern(parameter)) + ) { + throw new Error( + "Object destructuring in function parameters is not supported.", + ); } return t.tsMethodSignature( functionIdentifier, null, parameters, - t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Chainable'))) + t.tsTypeAnnotation( + t.tsTypeReference(t.identifier("Chainable")), + ), ); - }) - ) - ) - ]) - ) - ]) + }), + ), + ), + ]), + ), + ]), ); interfaceNode.declare = true; interfaceNode.global = true; if (!interfaceExists) { - t.addComment(interfaceNode, 'leading', '* Generated by cypress-codegen *'); + t.addComment(interfaceNode, "leading", "* Generated by cypress-codegen *"); } return [interfaceNode]; diff --git a/packages/cypress-codegen/src/index.ts b/packages/cypress-codegen/src/index.ts index 45db24c..4ea006b 100644 --- a/packages/cypress-codegen/src/index.ts +++ b/packages/cypress-codegen/src/index.ts @@ -11,4 +11,4 @@ See the License for the specific language governing permissions and limitations under the License. */ -export { cypressCodegen } from './codegen'; +export { cypressCodegen } from "./codegen"; diff --git a/packages/cypress-codegen/test/generate-content-with-exports.test.ts b/packages/cypress-codegen/test/generate-content-with-exports.test.ts index ccf47e9..81d35b7 100644 --- a/packages/cypress-codegen/test/generate-content-with-exports.test.ts +++ b/packages/cypress-codegen/test/generate-content-with-exports.test.ts @@ -11,20 +11,20 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { generateContentWithExports } from '../src/generate-content-with-exports'; +import { generateContentWithExports } from "../src/generate-content-with-exports"; const filePaths = [ - 'cypress/commands/file/path/1.tsx', - 'cypress/commands/file/path/2.tsx', - 'cypress/commands/filePath/3.ts' + "cypress/commands/file/path/1.tsx", + "cypress/commands/file/path/2.tsx", + "cypress/commands/filePath/3.ts", ]; const prettierConfig = { singleQuote: true, - printWidth: 90 + printWidth: 90, }; -describe('generateContentWithExports', () => { - it('should generate exports file', async () => { +describe("generateContentWithExports", () => { + it("should generate exports file", async () => { const result = await generateContentWithExports(filePaths, prettierConfig); expect(result).toEqual(`/** Generated by cypress-codegen **/ export * from './file/path/1'; diff --git a/packages/cypress-codegen/test/generate-content-with-imports.test.ts b/packages/cypress-codegen/test/generate-content-with-imports.test.ts index 565a131..0428cce 100644 --- a/packages/cypress-codegen/test/generate-content-with-imports.test.ts +++ b/packages/cypress-codegen/test/generate-content-with-imports.test.ts @@ -11,19 +11,19 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { readFileSync } from 'fs'; -import { generateContentWithImports } from '../src/generate-content-with-imports'; +import { readFileSync } from "fs"; +import { generateContentWithImports } from "../src/generate-content-with-imports"; -jest.mock('fs'); +jest.mock("fs"); -const filePath = 'filePath'; +const filePath = "filePath"; const prettierConfig = { singleQuote: true, - printWidth: 90 + printWidth: 90, }; -describe('generateContentWithImports', () => { - it('should generate types when types do not exist', async () => { +describe("generateContentWithImports", () => { + it("should generate types when types do not exist", async () => { (readFileSync as jest.Mock).mockReturnValue(`// some comment import { mount } from 'cypress/react18'; @@ -66,7 +66,7 @@ import('../commands').then(Cypress.Commands.addAll); `); }); - it('should do nothing when types exist', async () => { + it("should do nothing when types exist", async () => { (readFileSync as jest.Mock).mockReturnValue(`// some comment import { mount } from 'cypress/react18'; diff --git a/packages/cypress-codegen/test/generate-contents-with-interface.ts b/packages/cypress-codegen/test/generate-contents-with-interface.ts index 13a7df5..5917eb7 100644 --- a/packages/cypress-codegen/test/generate-contents-with-interface.ts +++ b/packages/cypress-codegen/test/generate-contents-with-interface.ts @@ -11,20 +11,20 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { generateContentsWithInterface } from '../src/generate-contents-with-interface'; +import { generateContentsWithInterface } from "../src/generate-contents-with-interface"; -import { readFileSync } from 'fs'; +import { readFileSync } from "fs"; -jest.mock('fs'); +jest.mock("fs"); -const filePath = 'filePath'; +const filePath = "filePath"; const prettierConfig = { singleQuote: true, - printWidth: 90 + printWidth: 90, }; -describe('generateContentsWithInterface', () => { - it('should generate types when types do not exist', async () => { +describe("generateContentsWithInterface", () => { + it("should generate types when types do not exist", async () => { (readFileSync as jest.Mock).mockReturnValue(`// some comment export function functionExampleOneInput(input1: string) { @@ -47,7 +47,10 @@ export const arrowFunctionExample = (input1: string) => { cy.log('Here is a custom command from an arrow function!').log(input); }; `); - const result = await generateContentsWithInterface(filePath, prettierConfig); + const result = await generateContentsWithInterface( + filePath, + prettierConfig, + ); expect(result).toEqual(`// some comment export function functionExampleOneInput(input1: string) { @@ -85,7 +88,7 @@ declare global { `); }); - it('should generate types when types exist', async () => { + it("should generate types when types exist", async () => { (readFileSync as jest.Mock).mockReturnValue(`// some comment export function functionExampleOneInput(input1: string) { @@ -118,7 +121,10 @@ declare global { } } `); - const result = await generateContentsWithInterface(filePath, prettierConfig); + const result = await generateContentsWithInterface( + filePath, + prettierConfig, + ); expect(result).toEqual(`// some comment export function functionExampleOneInput(input1: string) { @@ -156,7 +162,7 @@ declare global { `); }); - it('should preserve formatting', async () => { + it("should preserve formatting", async () => { (readFileSync as jest.Mock).mockReturnValue(`// some comment export function functionExampleOneInput(input1: string) { @@ -181,7 +187,10 @@ export const arrowFunctionExample = (input1: string) => { cy.log('Here is a custom command from an arrow function!').log(input); }; `); - const result = await generateContentsWithInterface(filePath, prettierConfig); + const result = await generateContentsWithInterface( + filePath, + prettierConfig, + ); expect(result).toEqual(`// some comment export function functionExampleOneInput(input1: string) { @@ -221,14 +230,17 @@ declare global { `); }); - it('should handle generic types properly', async () => { + it("should handle generic types properly", async () => { (readFileSync as jest.Mock).mockReturnValue(`// some comment export function functionExampleGenericType(input1: string) { cy.log('Here is a generic type!'); } `); - const result = await generateContentsWithInterface(filePath, prettierConfig); + const result = await generateContentsWithInterface( + filePath, + prettierConfig, + ); expect(result).toEqual(`// some comment export function functionExampleGenericType(input1: string) { @@ -246,21 +258,26 @@ declare global { `); }); - it('should throw error for object-destructured input', async () => { + it("should throw error for object-destructured input", async () => { (readFileSync as jest.Mock).mockReturnValue(` export const objectDestructureExample = ({ input1, input2 }: { input1: string; input2: string }) => { cy.log(input1); cy.log(input2); }; `); - await expect(generateContentsWithInterface(filePath, prettierConfig)).rejects.toThrowError(); + await expect( + generateContentsWithInterface(filePath, prettierConfig), + ).rejects.toThrowError(); }); - it('should handle file with only exports', async () => { + it("should handle file with only exports", async () => { (readFileSync as jest.Mock).mockReturnValue(`export * from './some-file'; export * from './some-other-file'; `); - const result = await generateContentsWithInterface(filePath, prettierConfig); + const result = await generateContentsWithInterface( + filePath, + prettierConfig, + ); expect(result).toEqual(`export * from './some-file'; export * from './some-other-file'; `); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9b3887..0324f36 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,7 +46,7 @@ importers: version: 27.2.3(@typescript-eslint/eslint-plugin@6.13.2)(eslint@8.26.0)(typescript@5.3.3) eslint-plugin-prettier: specifier: 5.0.1 - version: 5.0.1(eslint-config-prettier@9.0.0)(eslint@8.26.0)(prettier@3.0.3) + version: 5.0.1(eslint-config-prettier@9.0.0)(eslint@8.26.0)(prettier@3.1.1) husky: specifier: 8.0.3 version: 8.0.3 @@ -96,8 +96,8 @@ importers: specifier: 10.3.10 version: 10.3.10 prettier: - specifier: 3.0.3 - version: 3.0.3 + specifier: 3.1.1 + version: 3.1.1 devDependencies: '@swc/jest': specifier: 0.2.29 @@ -3371,10 +3371,10 @@ packages: eslint-plugin-import: 2.29.0(@typescript-eslint/parser@6.4.0)(eslint@8.26.0) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.26.0) eslint-plugin-no-only-tests: 3.1.0 - eslint-plugin-prettier: 5.0.1(eslint-config-prettier@9.0.0)(eslint@8.26.0)(prettier@3.0.3) + eslint-plugin-prettier: 5.0.1(eslint-config-prettier@9.0.0)(eslint@8.26.0)(prettier@3.1.1) eslint-rule-documentation: 1.0.23 jsx-ast-utils: 3.3.5 - prettier: 3.0.3 + prettier: 3.1.1 svg-element-attributes: 1.3.1 transitivePeerDependencies: - '@types/eslint' @@ -3513,7 +3513,7 @@ packages: engines: {node: '>=5.0.0'} dev: true - /eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.0.0)(eslint@8.26.0)(prettier@3.0.3): + /eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.0.0)(eslint@8.26.0)(prettier@3.1.1): resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -3529,7 +3529,7 @@ packages: dependencies: eslint: 8.26.0 eslint-config-prettier: 9.0.0(eslint@8.26.0) - prettier: 3.0.3 + prettier: 3.1.1 prettier-linter-helpers: 1.0.0 synckit: 0.8.5 dev: true @@ -6040,8 +6040,8 @@ packages: fast-diff: 1.3.0 dev: true - /prettier@3.0.3: - resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} + /prettier@3.1.1: + resolution: {integrity: sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==} engines: {node: '>=14'} hasBin: true