diff --git a/.prettierrc.json b/.prettierrc.json
index 41987a37..bdef9447 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -1,5 +1,6 @@
{
"printWidth": 100,
+ "tabWidth": 2,
"useTabs": false,
"singleQuote": true,
"trailingComma": "none",
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 607bd23b..72fc8492 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 1624aa6b..5933ed36 100644
--- a/README.md
+++ b/README.md
@@ -8,11 +8,11 @@ A [Cypress](https://www.cypress.io/) plugin which automatically adds and enables
## 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?
@@ -37,23 +37,23 @@ import { cypressCodegen } from 'cypress-codegen/dist/plugin';
import { defineConfig } from 'cypress';
export default defineConfig({
- e2e: {
- 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: 'create-react-app',
- bundler: 'webpack'
- }
+ component: {
+ setupNodeEvents(on, config) {
+ cypressCodegen(on, config);
+ return config;
+ },
+ devServer: {
+ framework: 'create-react-app',
+ bundler: 'webpack'
}
+ }
});
```
@@ -66,8 +66,8 @@ import 'cypress-codegen';
3. Put all of your custom commands in `cypress/commands` as regular functions. It is recommended to separate each command into its own file of the same name.
4. Run any Cypress test, and `cypress-codegen` will:
- - load these functions as valid custom commands
- - generate a special Cypress type definition for each function which will enable IntelliSense and "go to definition" shortcuts in your Cypress test code
+ - load these functions as valid custom commands
+ - generate a special Cypress type definition for each function which will enable IntelliSense and "go to definition" shortcuts in your Cypress test code
## Example
@@ -102,7 +102,7 @@ In cypress.config.ts:
```ts
env: {
- PROJECT: 'my-project';
+ PROJECT: 'my-project';
}
```
@@ -120,6 +120,6 @@ or in `cypress.config.js`:
```ts
env: {
- CODEGEN: false;
+ CODEGEN: false;
}
```
diff --git a/cypress/commands/custom-mount.tsx b/cypress/commands/custom-mount.tsx
index 36caf447..a9c38708 100644
--- a/cypress/commands/custom-mount.tsx
+++ b/cypress/commands/custom-mount.tsx
@@ -1,17 +1,17 @@
import * as React from 'react';
-export const customMount = (Component: React.ReactElement) => {
- cy.mount(
-
-
-
- );
+export const customMount = (Component: React.FC) => {
+ cy.mount(
+
+
+
+ );
};
declare global {
- namespace Cypress {
- interface Chainable {
- customMount(Component: React.ReactElement): Chainable;
- }
+ namespace Cypress {
+ interface Chainable {
+ customMount(Component: React.FC): Chainable;
}
+ }
}
diff --git a/cypress/component/component-example.cy.tsx b/cypress/component/component-example.cy.tsx
index 65805f2d..f51e77f6 100644
--- a/cypress/component/component-example.cy.tsx
+++ b/cypress/component/component-example.cy.tsx
@@ -16,36 +16,36 @@ 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 support scoped methods', () => {
- cy.contains(componentText).functionExampleScoped(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');
- });
+ 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 support scoped methods', () => {
+ cy.contains(componentText).functionExampleScoped(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/support/component-index.html b/cypress/support/component-index.html
index 3d489995..e39ba429 100644
--- a/cypress/support/component-index.html
+++ b/cypress/support/component-index.html
@@ -1,12 +1,12 @@
-
-
-
-
- Components App
-
-
-
-
+
+
+
+
+ Components App
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
index a97414df..dee4e981 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,7 @@
"devDependencies": {
"@types/glob": "7.2.0",
"@types/jest": "28.1.6",
+ "@types/react": "18.0.17",
"cypress": "10.4.0",
"eslint": "8.21.0",
"eslint-plugin-cypress": "2.12.1",
@@ -4051,6 +4052,12 @@
"integrity": "sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg==",
"dev": true
},
+ "node_modules/@types/prop-types": {
+ "version": "15.7.5",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
+ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
+ "dev": true
+ },
"node_modules/@types/q": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz",
@@ -4069,6 +4076,17 @@
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
"dev": true
},
+ "node_modules/@types/react": {
+ "version": "18.0.17",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.17.tgz",
+ "integrity": "sha512-38ETy4tL+rn4uQQi7mB81G7V1g0u2ryquNmsVIOKUAEIDK+3CUjZ6rSRpdvS99dNBnkLFL83qfmtLacGOTIhwQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ },
"node_modules/@types/resolve": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
@@ -4084,6 +4102,12 @@
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
"dev": true
},
+ "node_modules/@types/scheduler": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
+ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
+ "dev": true
+ },
"node_modules/@types/serve-index": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz",
@@ -6930,6 +6954,12 @@
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
"dev": true
},
+ "node_modules/csstype": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+ "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
+ "dev": true
+ },
"node_modules/cypress": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-10.4.0.tgz",
@@ -26234,6 +26264,12 @@
"integrity": "sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg==",
"dev": true
},
+ "@types/prop-types": {
+ "version": "15.7.5",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
+ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
+ "dev": true
+ },
"@types/q": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz",
@@ -26252,6 +26288,17 @@
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
"dev": true
},
+ "@types/react": {
+ "version": "18.0.17",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.17.tgz",
+ "integrity": "sha512-38ETy4tL+rn4uQQi7mB81G7V1g0u2ryquNmsVIOKUAEIDK+3CUjZ6rSRpdvS99dNBnkLFL83qfmtLacGOTIhwQ==",
+ "dev": true,
+ "requires": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ },
"@types/resolve": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
@@ -26267,6 +26314,12 @@
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
"dev": true
},
+ "@types/scheduler": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
+ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
+ "dev": true
+ },
"@types/serve-index": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz",
@@ -28304,6 +28357,12 @@
}
}
},
+ "csstype": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+ "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
+ "dev": true
+ },
"cypress": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-10.4.0.tgz",
diff --git a/package.json b/package.json
index 887b0784..ebecc212 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"devDependencies": {
"@types/glob": "7.2.0",
"@types/jest": "28.1.6",
+ "@types/react": "18.0.17",
"cypress": "10.4.0",
"eslint": "8.21.0",
"eslint-plugin-cypress": "2.12.1",
diff --git a/src/common.ts b/src/common.ts
index e938459e..a87d0b68 100644
--- a/src/common.ts
+++ b/src/common.ts
@@ -13,4 +13,4 @@ limitations under the License.
export const COMMANDS_DIRECTORY = 'cypress/commands';
-export const isScopedMethod = (methodName: string) => methodName.endsWith('Scoped');
+export const isScopedMethod = (methodName?: string) => methodName?.endsWith('Scoped');
diff --git a/src/generate-types-from-abstract-syntax-tree.ts b/src/generate-types-from-abstract-syntax-tree.ts
index 34397fb7..7bf8327b 100644
--- a/src/generate-types-from-abstract-syntax-tree.ts
+++ b/src/generate-types-from-abstract-syntax-tree.ts
@@ -28,7 +28,7 @@ import { resolve } from 'path';
import { format, Options } from 'prettier';
import { isScopedMethod } from './common';
-export const generateTypesFromAbstractSyntaxTree = (filePath: string, prettierConfig?: Options) => {
+export const generateTypesFromAbstractSyntaxTree = (filePath: string, prettierConfig: Options) => {
const contents = readFileSync(resolve(filePath)).toString();
const ast = parse(contents, { sourceType: 'module', plugins: ['typescript', 'jsx'] });
const currentNodes = ast.program.body;
@@ -38,8 +38,11 @@ export const generateTypesFromAbstractSyntaxTree = (filePath: string, prettierCo
t.isExportNamedDeclaration(node) &&
(t.isFunctionDeclaration(node.declaration) || t.isVariableDeclaration(node.declaration))
)
- .map((node: ExportNamedDeclaration) => {
- const declaration = node.declaration as FunctionDeclaration | VariableDeclaration;
+ .map(node => {
+ const exportNamedDeclaration = node as ExportNamedDeclaration;
+ const declaration = exportNamedDeclaration.declaration as
+ | FunctionDeclaration
+ | VariableDeclaration;
const isVariableDeclaration = t.isVariableDeclaration(declaration);
const functionIdentifier = isVariableDeclaration
? (declaration.declarations[0].id as Identifier)
@@ -56,10 +59,10 @@ export const generateTypesFromAbstractSyntaxTree = (filePath: string, prettierCo
const firstParamOmitted = parameters.slice(1);
return {
functionIdentifier,
- parameters: isScopedMethod(functionIdentifier.name) ? firstParamOmitted : parameters
+ parameters: isScopedMethod(functionIdentifier?.name) ? firstParamOmitted : parameters
};
});
- const newInterface = generateInterface(customCommands);
+ const newInterface = generateInterface(customCommands as CustomCommand[]);
const lastNode = currentNodes[currentNodes.length - 1];
const interfaceExists = t.isTSModuleDeclaration(lastNode) && lastNode.global && lastNode.declare;
const newNodes = interfaceExists ? currentNodes.slice(0, currentNodes.length - 1) : currentNodes;
diff --git a/src/index.ts b/src/index.ts
index 0eec3830..4e36747b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -13,26 +13,34 @@ limitations under the License.
import { isScopedMethod } from './common';
+interface TaskResult {
+ filePaths: string[];
+ commandsDirectory: string;
+}
+
before('Import Custom Commands', () => {
- cy.task('importCustomCommands').then(
- ({ filePaths, commandsDirectory }: { filePaths: string[]; commandsDirectory: string }) => {
- filePaths.forEach(filePath => {
- const projectName = Cypress.env('PROJECT') ? `${Cypress.env('PROJECT')}/` : '';
- // This relative file path is extremely particular and for some unknown reason must be exactly this.
- const customCommandObject = require(`../../../${projectName}cypress/commands/${filePath.replace(
- commandsDirectory,
- ''
- )}`);
- const methodNames = Object.keys(customCommandObject);
- methodNames.forEach((methodName: keyof Cypress.Chainable) => {
- const method = customCommandObject[methodName];
- if (isScopedMethod(methodName)) {
- Cypress.Commands.add(methodName, { prevSubject: 'element' }, method);
- } else {
- Cypress.Commands.add(methodName, method);
- }
- });
+ cy.task('importCustomCommands').then((result: unknown) => {
+ const { filePaths, commandsDirectory } = result as TaskResult;
+ filePaths.forEach(filePath => {
+ const projectName = Cypress.env('PROJECT') ? `${Cypress.env('PROJECT')}/` : '';
+ // This relative file path is extremely particular and for some unknown reason must be exactly this.
+ const customCommandObject = require(`../../../${projectName}cypress/commands/${filePath.replace(
+ commandsDirectory,
+ ''
+ )}`);
+ const methodNames = Object.keys(customCommandObject);
+ methodNames.forEach(methodName => {
+ const method = customCommandObject[methodName];
+ if (isScopedMethod(methodName)) {
+ Cypress.Commands.add(
+ methodName as keyof Cypress.Chainable,
+ { prevSubject: 'element' },
+ method
+ );
+ } else {
+ Cypress.Commands.add(methodName as keyof Cypress.Chainable, method);
+ }
});
- }
- );
+ });
+ });
});
diff --git a/src/plugin.ts b/src/plugin.ts
index 9b0978dc..e5347037 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -32,7 +32,7 @@ export const cypressCodegen: CypressCodegen = (
if (config.env.CODEGEN !== false) {
on('before:browser:launch', (browser, launchOptions) => {
const filePaths = sync(`${COMMANDS_DIRECTORY}/**/*`, { nodir: true });
- const prettierConfig = prettierConfigOverride ?? resolveConfig.sync(process.cwd());
+ const prettierConfig = prettierConfigOverride ?? resolveConfig.sync(process.cwd()) ?? {};
filePaths.forEach(filePath => {
const fileContentsWithTypes = generateTypesFromAbstractSyntaxTree(filePath, prettierConfig);
diff --git a/tsconfig.json b/tsconfig.json
index 84a9ffac..4e05d8f8 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -5,6 +5,7 @@
"declaration": true,
"module": "CommonJS",
"sourceMap": true,
+ "strict": true,
"target": "ES6",
"lib": ["ES2019", "DOM"],
"jsx": "react"