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
Markdown
# babel-plugin-react-data-testid-generator
[](https://badge.fury.io/js/babel-plugin-react-data-testid-generator)
[](https://github.com/Kazaz-Or/babel-plugin-react-data-testid-generator/actions)
[](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**