UNPKG

babel-plugin-react-data-testid-generator

Version:

Enhanced babel plugin for automatic React data-testid generation with unique IDs and class component support

531 lines (413 loc) โ€ข 13.5 kB
# babel-plugin-react-data-testid-generator [![npm version](https://badge.fury.io/js/babel-plugin-react-data-testid-generator.svg)](https://badge.fury.io/js/babel-plugin-react-data-testid-generator) [![Build Status](https://github.com/Kazaz-Or/babel-plugin-react-data-testid-generator/workflows/๐Ÿ”%20PR%20Validations/badge.svg)](https://github.com/Kazaz-Or/babel-plugin-react-data-testid-generator/actions) [![Coverage](https://img.shields.io/badge/coverage-96%25-brightgreen.svg)](https://github.com/Kazaz-Or/babel-plugin-react-data-testid-generator) > ๐Ÿงช **Enhanced Babel plugin for automatic React data-testid generation with unique IDs and comprehensive component support** Automatically adds `data-testid` attributes to your React components during the build process, making it easier to write reliable end-to-end tests. Features **component-scoped unique counters** for predictable, collision-free test IDs. ## โœจ Features - ๐ŸŽฏ **Automatic data-testid generation** for functional and class components - ๐Ÿ”ข **Component-scoped unique counters** to prevent ID conflicts - ๐Ÿ“Š **Predictable naming** like `ComponentName.element`, `ComponentName.element2` - ๐ŸŽญ **Full React support**: Functional components, arrow functions, class components - ๐Ÿ”ง **Customizable attributes** (data-testid, data-cy, data-test-id, etc.) - ๐Ÿš€ **Zero configuration** - works out of the box - ๐Ÿ“ฆ **TypeScript support** with full type definitions - ๐ŸŒ **Framework agnostic** - works with Next.js, Vite, CRA, and more - ๐Ÿ”„ **JSX member expressions** support (`Modal.Header` โ†’ `ComponentName.Header`) - ๐Ÿ›ก๏ธ **Never overrides existing attributes** ## ๐Ÿš€ Installation ```bash npm install --save-dev babel-plugin-react-data-testid-generator # or yarn add --dev babel-plugin-react-data-testid-generator ``` ## ๐Ÿ“– Usage ### Basic Setup Add the plugin to your `.babelrc.json` or `babel.config.js`: ```json { "plugins": ["babel-plugin-react-data-testid-generator"] } ``` ### Framework-Specific Setup <details> <summary><strong>๐Ÿš€ Next.js</strong></summary> Create `.babelrc.json` in your project root: ```json { "presets": ["next/babel"], "plugins": [ [ "babel-plugin-react-data-testid-generator", { "attributes": ["data-testid"] } ] ] } ``` </details> <details> <summary><strong>โšก Vite</strong></summary> Configure in `vite.config.js`: ```javascript import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; export default defineConfig({ plugins: [ react({ babel: { plugins: [ [ "babel-plugin-react-data-testid-generator", { attributes: ["data-testid"], }, ], ], }, }), ], }); ``` </details> <details> <summary><strong>๐Ÿ“ฆ Create React App</strong></summary> **Note**: CRA v5+ ignores custom babel configs. Use Next.js or Vite instead, or eject from CRA. For ejected CRA, add to `babel.config.js`: ```javascript module.exports = { presets: ["react-app"], plugins: [ [ "babel-plugin-react-data-testid-generator", { attributes: ["data-testid"], }, ], ], }; ``` </details> ### With Custom Attributes ```json { "plugins": [ [ "babel-plugin-react-data-testid-generator", { "attributes": ["data-testid", "data-cy"] } ] ] } ``` ## ๐Ÿ”„ Transformations ### Functional Components **Before:** ```jsx function UserCard({ name }) { return ( <div> <h3>{name}</h3> <button>Follow</button> <button>Message</button> </div> ); } ``` **After:** ```jsx function UserCard({ name }) { return ( <div data-testid="UserCard.div"> <h3 data-testid="UserCard.h3">{name}</h3> <button data-testid="UserCard.button">Follow</button> <button data-testid="UserCard.button2">Message</button> </div> ); } ``` ### Unique Counter System The plugin uses **component-scoped counters** to ensure uniqueness: ```jsx function FormComponent() { return ( <div> {" "} {/* FormComponent.div */} <div>First</div> {/* FormComponent.div2 */} <div>Second</div> {/* FormComponent.div3 */} <button>Save</button> {/* FormComponent.button */} <button>Cancel</button> {/* FormComponent.button2 */} </div> ); } ``` ### JSX Member Expressions ```jsx function ModalComponent() { return ( <Modal.Container> {" "} {/* ModalComponent.Container */} <Modal.Header>Title</Modal.Header> {/* ModalComponent.Header */} <Modal.Body>Content</Modal.Body> {/* ModalComponent.Body */} <Modal.Header>Second</Modal.Header> {/* ModalComponent.Header2 */} </Modal.Container> ); } ``` ### Class Components ```jsx class TodoList extends React.Component { render() { return ( <div> {" "} {/* TodoList.div */} <h2>My Todos</h2> {/* TodoList.h2 */} <ul> {" "} {/* TodoList.ul */} <li>Todo 1</li> {/* TodoList.li */} <li>Todo 2</li> {/* TodoList.li2 */} </ul> <button>Add Todo</button> {/* TodoList.button */} </div> ); } } ``` ### Conditional Rendering ```jsx function ConditionalComponent({ isLoggedIn }) { if (isLoggedIn) { return <div>Welcome</div>; // ConditionalComponent.div } return <div>Please login</div>; // ConditionalComponent.div2 } ``` ## โš™๏ธ Configuration Options | Option | Type | Default | Description | | ------------ | ---------- | ----------------- | ------------------------------------------- | | `attributes` | `string[]` | `["data-testid"]` | Array of attribute names to add to elements | ### Examples **Multiple testing frameworks:** ```json { "plugins": [ [ "babel-plugin-react-data-testid-generator", { "attributes": ["data-testid", "data-cy", "data-test-id"] } ] ] } ``` **Cypress only:** ```json { "plugins": [ [ "babel-plugin-react-data-testid-generator", { "attributes": ["data-cy"] } ] ] } ``` **Disable plugin:** ```json { "plugins": [ [ "babel-plugin-react-data-testid-generator", { "attributes": [] } ] ] } ``` ## ๐Ÿงช Testing Integration ### Jest + React Testing Library ```javascript import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import UserCard from "./UserCard"; test("should interact with generated test ids", async () => { render(<UserCard name="John" />); // Predictable test IDs const followButton = screen.getByTestId("UserCard.button"); const messageButton = screen.getByTestId("UserCard.button2"); await userEvent.click(followButton); expect(screen.getByTestId("UserCard.div")).toBeInTheDocument(); }); ``` ### Cypress ```javascript describe("UserCard Component", () => { it("should interact with elements", () => { cy.mount(<UserCard name="John" />); // Use generated data-cy attributes cy.get('[data-cy="UserCard.button"]').click(); cy.get('[data-cy="UserCard.button2"]').should("be.visible"); }); }); ``` ### Playwright ```javascript import { test, expect } from "@playwright/test"; test("user card interactions", async ({ page }) => { await page.goto("/user-profile"); // Reliable selectors with generated IDs await page.locator('[data-testid="UserCard.button"]').click(); await expect(page.locator('[data-testid="UserCard.div"]')).toBeVisible(); }); ``` ## ๐Ÿ“ Example Applications We provide two complete example applications demonstrating the plugin: ### ๐Ÿš€ Next.js Example ```bash cd example/babelrc npm install npm run dev ``` **Features:** - Next.js 13+ with App Router - Beautiful modern UI with animations - Interactive components demonstrating test ID generation - Open [http://localhost:3000](http://localhost:3000) ### โšก Vite Example ```bash cd example/vite npm install npm run dev ``` **Features:** - Vite with React 18 - Lightning-fast HMR - Same UI as Next.js example - Open [http://localhost:3001](http://localhost:3001) **Both examples include:** - โœ… Interactive forms and buttons - โœ… Modal components - โœ… Card layouts - โœ… Class and functional components - โœ… Conditional rendering - โœ… JSX member expressions **Inspect the DOM** to see the automatically generated `data-testid` attributes! ## ๐Ÿ› ๏ธ Supported React Patterns | Pattern | Supported | Example | | ---------------------- | --------- | ---------------------------------------------- | | Function Components | โœ… | `function MyComponent() {}` | | Arrow Functions | โœ… | `const MyComponent = () => {}` | | Class Components | โœ… | `class MyComponent extends React.Component {}` | | Anonymous Exports | โš ๏ธ | `export default () => {}` (skipped) | | JSX Member Expressions | โœ… | `<Modal.Header>` โ†’ `ComponentName.Header` | | Fragments | โœ… | `<>` and `<React.Fragment>` | | Conditional Rendering | โœ… | Multiple return statements | | Existing Attributes | โœ… | Never overrides existing `data-testid` | | Self-Closing Elements | โœ… | `<img />`, `<input />` | | Nested Components | โœ… | Deep nesting with unique counters | ## ๐ŸŽฏ Best Practices 1. **๐Ÿท๏ธ Use PascalCase** for component names to get predictable test IDs 2. **๐Ÿ” Inspect Generated IDs** in development to understand the structure 3. **๐ŸŒ Environment-Specific** - consider disabling in production: ```javascript const isProd = process.env.NODE_ENV === "production"; module.exports = { plugins: [...(!isProd ? [["babel-plugin-react-data-testid-generator"]] : [])], }; ``` 4. **๐Ÿงช Test ID Patterns** - use consistent patterns in tests: - `ComponentName.elementType` for first occurrence - `ComponentName.elementType2` for second occurrence - `ComponentName.MemberExpression` for JSX member expressions ## ๐Ÿ”ง Development ### Setup ```bash git clone https://github.com/Kazaz-Or/babel-plugin-react-data-testid-generator.git cd babel-plugin-react-data-testid-generator yarn install ``` ### Commands ```bash yarn test # Run tests yarn test:watch # Run tests in watch mode yarn test:coverage # Run tests with coverage yarn build # Build the plugin yarn lint # Lint code yarn fmt # Format code ``` ### Testing The plugin has comprehensive test coverage (96%+) including: - โœ… 62 test cases covering all React patterns - โœ… Snapshot testing for consistent output - โœ… Edge case handling - โœ… CI/CD with coverage thresholds ## ๐Ÿค Contributors Thanks to these wonderful people who have contributed to this project: <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --> <table> <tr> <td align="center"> <a href="https://github.com/Kazaz-Or"> <img src="https://github.com/Kazaz-Or.png?size=40" width="40px;" alt="Or Kazaz"/> <br /> <sub><b>Or Kazaz</b></sub> </a> <br /> <a href="#maintenance-Kazaz-Or" title="Maintenance">๐Ÿšง</a> <a href="https://github.com/Kazaz-Or/babel-plugin-react-data-testid-generator/commits?author=Kazaz-Or" title="Code">๐Ÿ’ป</a> <a href="#design-Kazaz-Or" title="Design">๐ŸŽจ</a> <a href="https://github.com/Kazaz-Or/babel-plugin-react-data-testid-generator/commits?author=Kazaz-Or" title="Documentation">๐Ÿ“–</a> <a href="#ideas-Kazaz-Or" title="Ideas, Planning, & Feedback">๐Ÿค”</a> </td> </tr> </table> <!-- ALL-CONTRIBUTORS-LIST:END --> ### How to Contribute We welcome contributions! Here are some ways you can help: - ๐Ÿ› **Report bugs** by opening an issue - ๐Ÿ’ก **Suggest features** or improvements - ๐Ÿ“– **Improve documentation** - ๐Ÿงช **Add test cases** - ๐Ÿ”ง **Submit pull requests** <details> <summary><strong>๐Ÿ“‹ Contribution Process</strong></summary> 1. Fork the repository 2. Create a feature branch (`git checkout -b feature/amazing-feature`) 3. Make your changes and add tests 4. Ensure all tests pass (`yarn test`) 5. Commit your changes (`git commit -m 'Add amazing feature'`) 6. Push to the branch (`git push origin feature/amazing-feature`) 7. Open a Pull Request </details> <details> <summary><strong>๐Ÿค Adding Yourself as a Contributor</strong></summary> If you contribute to this project, you can add yourself to the contributors list: 1. Install the all-contributors CLI: `npm install -g all-contributors-cli` 2. Add yourself: `all-contributors add <your-username> <contribution-type>` 3. Update the README: `all-contributors generate` </details> <details> <summary><strong>๐Ÿท๏ธ Contribution Types</strong></summary> - ๐Ÿ’ป `code` - Code contributions - ๐Ÿ“– `doc` - Documentation - ๐Ÿ› `bug` - Bug reports - ๐Ÿ’ก `ideas` - Ideas and suggestions - ๐Ÿงช `test` - Tests - ๐ŸŽจ `design` - Design - ๐Ÿšง `maintenance` - Maintenance - ๐Ÿ“ฆ `platform` - Packaging/platform support </details> ## ๐Ÿ“„ License MIT ยฉ [Or Kazaz](https://github.com/Kazaz-Or) --- ## ๐Ÿ™ Acknowledgments - Inspired by [babel-plugin-react-data-testid](https://github.com/akameco/babel-plugin-react-data-testid) **Made with ๐Ÿ’™ for better testing**