babel-plugin-react-data-testid-generator
v0.3.2
Published
Enhanced babel plugin for automatic React data-testid generation with unique IDs and class component support
Downloads
90
Maintainers
Readme
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
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:
{
"plugins": ["babel-plugin-react-data-testid-generator"]
}Framework-Specific Setup
Create .babelrc.json in your project root:
{
"presets": ["next/babel"],
"plugins": [
[
"babel-plugin-react-data-testid-generator",
{
"attributes": ["data-testid"]
}
]
]
}Configure in vite.config.js:
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"],
},
],
],
},
}),
],
});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:
module.exports = {
presets: ["react-app"],
plugins: [
[
"babel-plugin-react-data-testid-generator",
{
attributes: ["data-testid"],
},
],
],
};With Custom Attributes
{
"plugins": [
[
"babel-plugin-react-data-testid-generator",
{
"attributes": ["data-testid", "data-cy"]
}
]
]
}🔄 Transformations
Functional Components
Before:
function UserCard({ name }) {
return (
<div>
<h3>{name}</h3>
<button>Follow</button>
<button>Message</button>
</div>
);
}After:
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:
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
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
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
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:
{
"plugins": [
[
"babel-plugin-react-data-testid-generator",
{
"attributes": ["data-testid", "data-cy", "data-test-id"]
}
]
]
}Cypress only:
{
"plugins": [
[
"babel-plugin-react-data-testid-generator",
{
"attributes": ["data-cy"]
}
]
]
}Disable plugin:
{
"plugins": [
[
"babel-plugin-react-data-testid-generator",
{
"attributes": []
}
]
]
}🧪 Testing Integration
Jest + React Testing Library
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
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
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
cd example/babelrc
npm install
npm run devFeatures:
- Next.js 13+ with App Router
- Beautiful modern UI with animations
- Interactive components demonstrating test ID generation
- Open http://localhost:3000
⚡ Vite Example
cd example/vite
npm install
npm run devFeatures:
- Vite with React 18
- Lightning-fast HMR
- Same UI as Next.js example
- Open 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
- 🏷️ Use PascalCase for component names to get predictable test IDs
- 🔍 Inspect Generated IDs in development to understand the structure
- 🌍 Environment-Specific - consider disabling in production:
const isProd = process.env.NODE_ENV === "production";
module.exports = {
plugins: [...(!isProd ? [["babel-plugin-react-data-testid-generator"]] : [])],
};- 🧪 Test ID Patterns - use consistent patterns in tests:
ComponentName.elementTypefor first occurrenceComponentName.elementType2for second occurrenceComponentName.MemberExpressionfor JSX member expressions
🔧 Development
Setup
git clone https://github.com/Kazaz-Or/babel-plugin-react-data-testid-generator.git
cd babel-plugin-react-data-testid-generator
yarn installCommands
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 codeTesting
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:
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
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes and add tests
- Ensure all tests pass (
yarn test) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
If you contribute to this project, you can add yourself to the contributors list:
- Install the all-contributors CLI:
npm install -g all-contributors-cli - Add yourself:
all-contributors add <your-username> <contribution-type> - Update the README:
all-contributors generate
- 💻
code- Code contributions - 📖
doc- Documentation - 🐛
bug- Bug reports - 💡
ideas- Ideas and suggestions - 🧪
test- Tests - 🎨
design- Design - 🚧
maintenance- Maintenance - 📦
platform- Packaging/platform support
📄 License
MIT © Or Kazaz
🙏 Acknowledgments
- Inspired by babel-plugin-react-data-testid
Made with 💙 for better testing
