Skip to main content

gen-rtl-test

Generate React Testing Library tests following OCP Console best practices

معلومات المصدر

المستودع
openshift/console
آخر نشاط في المصدر
٢٦ مايو ٢٠٢٦ في ١٣:٤٣
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٤٦٠
التفرعات
٧٦٦

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
gen-rtl-test
description
Generate React Testing Library tests following OCP Console best practices
argument-hint
[path/to/Component.tsx] or use @file for autocomplete
# OCP Console React Component Unit Testing Best Practices **Usage:** - `/gen-rtl-test` - **Default**: Automatically checks `git diff` for component changes and generates tests - `/gen-rtl-test path/to/Component.tsx` - Generate tests for a specific component - `/gen-rtl-test @Component.tsx` - Use `@` for file autocomplete, then select the file ## Smart Component Detection Workflow When invoked without arguments, the slash command follows this intelligent workflow: 1. **Check git diff**: Automatically run `git diff --name-only` to find modified files 2. **Filter for components**: Identify `.tsx` and `.jsx` component files (exclude test files, type files, utils) 3. **Validate components**: Ensure files contain React components (not just types or utilities) 4. **Present options**: Show user the detected components and ask which to generate tests for 5. **Fallback**: If no valid components found, prompt user for component path This workflow ensures you automatically generate tests for components you're actively working on. You are helping generate comprehensive React Testing Library (RTL) test cases following the established OCP Console unit testing standards. **Before writing imports:** Inspect the component under test (and its hooks). Use **`renderWithProviders`** only if it depends on the **Redux store** and/or **React Router**. Otherwise use **`render`** from `@testing-library/react`. (See **Rule 0** for the full table, including **PluginStore** when relevant.) ## Introduction & Objectives This guide establishes a consistent, project-wide standard for all React component tests in the OCP Console. **Core Philosophy:** Test component behavior from a user's perspective, not internal implementation details. ### Objectives - Establish consistent project-wide testing standards - Promote user-centric testing that focuses on behavior over implementation - Provide practical, rules-based guidance for common scenarios - Improve test quality, resilience, and maintainability --- ### Rule 0: Use `renderWithProviders` Only When the Component Needs Redux and/or Router Pick the render helper from what the **component under test** actually uses: | Use | When the component (or non-mocked hooks it calls) … | |-----|------------------------------------------------------| | **`renderWithProviders`** from `@console/shared/src/test-utils/unit-test-utils` | Needs the **Redux store** (e.g. `useSelector`, `useDispatch`, k8s/resource hooks backed by the console store) **and/or** **React Router** (e.g. `useNavigate`, `useParams`, `useLocation`, `Link`, `NavLink`). The helper also wraps **PluginStore** — use it when the tree touches **dynamic plugin** APIs that expect that context. | | **`render`** from `@testing-library/react` | Has **no** Redux or Router dependency (pure presentational UI, local `useState` only, or all store/router hooks are mocked so the real provider is unnecessary). | ```typescript // Standalone / presentational — no Redux or Router in the component under test import { render, screen } from '@testing-library/react'; import { BadgeLabel } from './BadgeLabel'; it('renders the label', () => { render(<BadgeLabel text="Ready" />); expect(screen.getByText('Ready')).toBeVisible(); }); // Connected to store and/or routes — use the console test wrapper import { screen } from '@testing-library/react'; import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils'; import { DeploymentListRow } from './DeploymentListRow'; it('shows the deployment name', () => { renderWithProviders(<DeploymentListRow deployment={mockDeployment} />); expect(screen.getByRole('cell', { name: /nginx/i })).toBeVisible(); }); ``` **Why `renderWithProviders` exists:** it supplies **Redux `Provider`**, **`MemoryRouter`**, and **PluginStore** so typical console components do not throw when mounting. **Optional clarity for reviewers:** If the file uses `render` (not `renderWithProviders`), a one-line comment at the top of the file or above the first test can help reviewers, e.g. `// Unit tests: component has no Redux or Router dependencies.` **Do not** use `renderWithProviders` “by default” for every console file — that hides missing providers in tests that should be asserting integration with real store/router behavior, and it adds cost where `render` is enough. ### Rule 0.1: Use userEvent (Not fireEvent) **ALWAYS** use `userEvent` from `@testing-library/user-event` for user interactions. ```typescript // FORBIDDEN — ESLint / project rules (e.g. testing-library) when enabled import { fireEvent } from '@testing-library/react'; fireEvent.click(button); fireEvent.change(input, { target: { value: 'test' } }); // REQUIRED import userEvent from '@testing-library/user-event'; const user = userEvent.setup(); await user.click(button); await user.type(input, 'test'); ``` **Why:** - `userEvent` simulates real user behavior (focus, blur, keyboard events) - `fireEvent` dispatches raw DOM events (not realistic) - `userEvent` catches more bugs related to event handling - Better async handling with `await` ### Rule 0.2: Use screen Queries (Not Destructured) **ALWAYS** use `screen` from RTL instead of destructuring queries from `render`. ```typescript // FORBIDDEN — ESLint (e.g. testing-library/prefer-screen-queries) when enabled const { getByRole, getByText } = render(<MyComponent />); const button = getByRole('button'); // REQUIRED — use whichever render helper matches Rule 0, then always query via screen import { render, screen } from '@testing-library/react'; // or: import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils'; render(<MyComponent />); // or renderWithProviders(<MyComponent />) when Redux/Router are needed const button = screen.getByRole('button'); ``` **Why:** - Consistent query access across all tests - Better debugging with `screen.debug()` - Cleaner test code - ESLint rule `testing-library/prefer-screen-queries` enforces this --- ## Section 1: React Testing Library Overview ### The RTL Approach RTL emphasizes testing components as users interact with them. Users find buttons by visible text (e.g., "Submit"), not by CSS classes, IDs, or test IDs. Therefore, test selectors should prioritize what users see and interact with. ### Core Principles 1. **User-Centric Testing** - Test what users see and interact with. **DO NOT test:** - Internal component state - Private component methods - Props passed to child components - CSS class names or styles - Component structure (e.g., `expect(container.firstChild).toBe...`) 2. **Accessibility-First** - Queries match how screen readers and users interact with the UI 3. **Semantic Over Generic** - Always prefer role-based queries (e.g., `getByRole`) over generic selectors 4. **DRY Helpers** - Use reusable function in frontend/packages/console-shared/src/test-utils directoty and sub-directory if exists else extract repetitive setup into reusable functions 5. **Async-Aware** - Handle asynchronous updates with `findBy*` and `waitFor` 6. **TypeScript Safety** - Use proper types for props, state, and mock data 7. **Arrange-Act-Assert (AAA) Pattern** - Structure tests logically: - **Arrange:** Render component with mocks - **Act:** Perform user actions - **Assert:** Verify expected state --- ## Section 2: Console RTL Rules ## ⚠️ CRITICAL RULE - READ FIRST ### **ALWAYS Use ES6 Imports - NEVER Use require()** **This is the #1 most critical rule for test generation.** ## 🚫 ZERO TOLERANCE: NO require() ANYWHERE **NEVER use `require()` in test files. NO EXCEPTIONS.** ❌ **FORBIDDEN - In test bodies:** ```typescript it('should work', () => { const { k8sCreate } = require('@console/internal/module/k8s'); // ❌ NEVER }); ``` ❌ **FORBIDDEN - In mock factories:** ```typescript jest.mock('../Component', () => { const React = require('react'); // ❌ NEVER - even here! return () => React.createElement('div', null, 'Mock'); }); ``` ✅ **REQUIRED - ES6 imports only:** ```typescript // Import at file top import { k8sCreate } from '@console/internal/module/k8s'; // Simple mocks - no React.createElement needed jest.mock('../Component', () => () => null); // ✅ Return null jest.mock('../LoadingSpinner', () => () => 'Loading...'); // ✅ Return string // Use in tests it('should work', () => { (k8sCreate as jest.Mock).mockResolvedValue({}); }); ``` **Why ZERO tolerance:** - `require()` breaks Jest's mock hoisting mechanism - Causes test isolation failures and flaky tests - Violates OCP Console testing standards - **NO exceptions - even in mock factories** --- ### Rule 1: Test File Co-location and Naming Convention **File Structure:** ``` MyComponentDirectory/ ├── __tests__/ │ └── MyComponent.spec.tsx └── MyComponent.tsx ``` - Test file must be in `__tests__/` directory within component directory - Test file must have same name as implementation file - Use `.spec.tsx` extension ### Rule 2: Mocking Strategies #### Check for Global Mocks First Before manually mocking, check `__mocks__/` directory for existing global mocks (e.g., `react-i18next`, `localStorage`, `k8sResourcesMocks`). These are applied automatically. #### Keep Component Mocks Simple (No JSX) Mock functions must NOT return JSX to avoid Jest hoisting errors: ```typescript // ✅ CORRECT: jest.mock('../MyComponent', () => () => null); // ✅ CORRECT: jest.mock('../LoadingSpinner', () => () => 'Loading...'); // ✅ CORRECT: Return children directly jest.mock('../utils/firehose', () => ({ Firehose: (props) => props.children, })); // ✅ CORRECT: Use jest.fn for tracking calls jest.mock('../utils/firehose', () => ({ Firehose: jest.fn((props) => props.children), })); // ❌ INCORRECT (causes hoisting errors): jest.mock('../MyComponent', () => () => <div>My Mock</div>); ``` #### Mock Custom Hooks with jest.fn() ```typescript jest.mock('../useCustomHook', () => ({ useCustomHook: jest.fn(() => [/* mock data */]), })); ``` #### Use Static Partial Mocking for Module-Wide Control ```typescript jest.mock('@console/internal/module/k8s', () => ({ ...jest.requireActual('@console/internal/module/k8s'), k8sCreate: jest.fn(), k8sPatch: jest.fn(), })); ``` #### Use jest.spyOn for Granular, Test-Level Control (Preferred) ```typescript import * as k8sModule from '@console/internal/module/k8s'; it('should do something when k8sGet succeeds', () => { jest.spyOn(k8sModule, 'k8sGet').mockResolvedValue(data); // ... rest of the test ... }); ``` #### Controlling Redux State **DO NOT** mock the `useReduxStore` hook. Instead, pass `initialState` to `renderWithProviders`: ```typescript import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils'; it('should render with mock Redux data', () => { const mockK8sState = { /* ... */ }; renderWithProviders( <MyComponent />, { initialState: { k8s: mockK8sState } } ); expect(screen.getByText('My Mock Data')).toBeVisible(); }); ``` #### ⚠️ CRITICAL: Always Use ES6 Import (Never require()) **STRICTLY ENFORCED - ZERO EXCEPTIONS** ## 🚫 NO require() ANYWHERE IN TEST FILES Always use ES6 `import/export` syntax in test files. **NEVER** use `require()` - not in test bodies, not in mock factories, **NOWHERE**. **✅ CORRECT - ES6 Imports:** ```typescript // Import at the top of the file import { k8sCreate } from '@console/internal/module/k8s'; import { history } from '@console/internal/components/utils'; import * as pdbModels from '../pdb-models'; // Simple mocks - return null or strings, NO React.createElement
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub