mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 07:26:20 +00:00
* feat: update context link modal form init * feat: add double way sync on urls and param * feat: minor refactor * feat: minor refactor * feat: change contextlinks data structure * feat: context menu changes init * feat: context menu hook refactor * feat: context links processors * feat: context variables hook added * feat: add support for field variables * feat: minor refactor * feat: minor refactor * feat: minor refactor * feat: handle on save * feat: minor refactor * feat: snapshot update * feat: revert qbv5 * feat: aggregation header val * feat: fix header color * feat: minor refactor * feat: minor refactor * feat: fix breaking changes from qb v5 * feat: change api for breakout opitons * feat: minor refactor * feat: minor refactor * fix: added fix for extractquerypararms when value is string in multivalue operator * feat: minor refactor * feat: add back in breakout * feat: minor refactor * feat: add substitute var api call to decode vars * feat: minor fix * feat: optimize query value comparison in QueryBuilderV2 * feat: minor fix * feat: minor fix * feat: test fix * feat: added dynamic variables creation flow (#7541) * feat: added dynamic variables creation flow * feat: added keys and value apis and hooks * feat: added api and select component changes * feat: added keys fetching and preview values * feat: added dynamic variable to variable items * feat: handled value persistence and tab switches * feat: added default value and formed a schema for dyn-variables * feat: added client and server side searches * feat: corrected the initial load getfieldKey api * feat: removed fetch on mount restriction * feat: added dynamic variable to the dashboard details (#7755) * feat: added dynamic variable to the dashboard details * feat: added new component to existing variables * feat: added enhancement to multiselect and select for dyn-variables * feat: added refetch method between all dynamic-variables * feat: correct error handling * feat: correct error handling * feat: enforced non-empty selectedvalues and default value * feat: added client and server side searches * feat: retry on error * feat: correct error handling * feat: handle defautl value in existing variables * feat: lowercase the source for payload * feat: fixed the incorrect assignment of active indices * feat: improved handling of all option * feat: improved the ALL option visuals * feat: handled default value enforcement in existing variables * feat: added unix time to values call * feat: added incomplete data message and info to search * feat: changed dashboard panel call handling with existing variables * feat: adjusted the response type and data with the new API schema for values * feat: code refactor * feat: made dyn-variable option as the default * feat: added test cases for dyn variable creation and completion * feat: updated test cases * feat: added variable in url and made dashboard sync around that and sharable (#7944) * feat: added dynamic variable to the dashboard details * feat: added new component to existing variables * feat: added enhancement to multiselect and select for dyn-variables * feat: added refetch method between all dynamic-variables * feat: correct error handling * feat: correct error handling * feat: enforced non-empty selectedvalues and default value * feat: added client and server side searches * feat: retry on error * feat: correct error handling * feat: handle defautl value in existing variables * feat: lowercase the source for payload * feat: fixed the incorrect assignment of active indices * feat: improved handling of all option * feat: improved the ALL option visuals * feat: handled default value enforcement in existing variables * feat: added unix time to values call * feat: added incomplete data message and info to search * feat: changed dashboard panel call handling with existing variables * feat: adjusted the response type and data with the new API schema for values * feat: code refactor * feat: made dyn-variable option as the default * feat: added test cases for dyn variable creation and completion * feat: updated test cases * feat: added variable in url and made dashboard sync around that and sharable * feat: added test cases * feat: added safety check * feat: enabled url setting on first load itself * feat: code refactor * feat: cleared options query param when on dashboard list page * feat: resolved conflicts * feat: added dynamic variable suggestion in where clause * feat: added test cases for hooks and api call functions * feat: added test case for querybuildersearchv2 suggestion changes * feat: code refactor * feat: updated test case * feat: corrected the regex matcher for resolved titles * feat: added ability to add/remove variable filter to one or more existing panels * feat: added widgetselector on variable creation * feat: show labels in widget selector * feat: added apply to all and variable removal logical * feat: refectch only related and affected panels in case of dynamic variables * feat: added button loader for apply-all * feat: light-mode styles * feat: minor refactor * feat: added test cases * feat: refactor * feat: remove consoles * feat: pass panel types to substitutevars * feat: cross filtering init * fix: added fix for query builder filters * feat: cross filtering add set/unset/create functionality * feat: test update * fix: added migration to filter expression for crud operations of variable * feat: format legend name according to existing format * feat: breakout test init * feat: breakout test match query * feat: context links tests * feat: minor refactor * feat: show edit only if user has access * feat: added dynamic variables creation flow (#7541) * feat: added dynamic variables creation flow * feat: added keys and value apis and hooks * feat: added api and select component changes * feat: added keys fetching and preview values * feat: added dynamic variable to variable items * feat: handled value persistence and tab switches * feat: added default value and formed a schema for dyn-variables * feat: added client and server side searches * feat: corrected the initial load getfieldKey api * feat: removed fetch on mount restriction * feat: added dynamic variable to the dashboard details (#7755) * feat: added dynamic variable to the dashboard details * feat: added new component to existing variables * feat: added enhancement to multiselect and select for dyn-variables * feat: added refetch method between all dynamic-variables * feat: correct error handling * feat: correct error handling * feat: enforced non-empty selectedvalues and default value * feat: added client and server side searches * feat: retry on error * feat: correct error handling * feat: handle defautl value in existing variables * feat: lowercase the source for payload * feat: fixed the incorrect assignment of active indices * feat: improved handling of all option * feat: improved the ALL option visuals * feat: handled default value enforcement in existing variables * feat: added unix time to values call * feat: added incomplete data message and info to search * feat: changed dashboard panel call handling with existing variables * feat: adjusted the response type and data with the new API schema for values * feat: code refactor * feat: made dyn-variable option as the default * feat: added test cases for dyn variable creation and completion * feat: updated test cases * feat: added dynamic variable suggestion in where clause * feat: added test cases for hooks and api call functions * feat: added test case for querybuildersearchv2 suggestion changes * feat: code refactor * feat: updated test case * feat: corrected the regex matcher for resolved titles * feat: added ability to add/remove variable filter to one or more existing panels * feat: added widgetselector on variable creation * feat: show labels in widget selector * feat: added apply to all and variable removal logical * feat: refectch only related and affected panels in case of dynamic variables * feat: added button loader for apply-all * feat: light-mode styles * fix: added migration to filter expression for crud operations of variable * feat: reverted dynamic variable url config changes (#8877) * Revert "feat: changed query param name" This reverts commit 62bee5f003bf74b0da1c5951f1b5d0f2c250905d. * Revert "feat: added user-friendly format to dashboard variable url" This reverts commit 6de8b1c2e8c6a838941014ea4929e9f5c908d975. * feat: reverted url var changes * feat: reverted url changed from usedashboardvarupdate hook * feat: send empty array for widgetId * feat: added type in the variables in query_range payload for dynamic * feat: minor fixes * fix: added fix for multivalue operator without brackets * feat: minor fix * feat: fix failing test * feat: change revert * test: added tests for querycontextUtils + querybuilderv2 utils * fix: added fix for replacing filter with the new value * fix: added fix for replacing filters + datetimepicker composite query * test: fixed querybuilderv2 utils test * feat: handle number dataType in filters * feat: correct the variable addition to panel format for new qb expression * feat: remove other queries in breakout * feat: add metric to traces mapping * feat: pass proper time range * feat: update time range logic * feat: value panel drilldown init * feat: value panel drilldown init * feat: enable context links in value panel * feat: minor fix * feat: update snapshot * feat: hide breakout in value panel * feat: add panel type to view mode * feat: add support to change panel in breakouts * feat: panel change for breakout logic added * chore: fix style * chore: show variables suggestion while creating context links * chore: add timestamp to graphs * chore: add timestamp to table panel * chore: fix failing tests * chore: fix infinite re-rendering due to queryRange * chore: send appropriate time range when signal is metrics * chore: show variables suggestion while creating context links * chore: minor refactor * chore: show trace details link if filter has trace_id * chore: fix infinite render of table component * chore: added tests for v2 * fix: context links set from dropdown * chore: minor refactor * chore: minor refactor * chore: fix test * chore: fix timerange for apm metrics * fix: get correct timestamp for clicked data * chore: comment out change to histogram on breakout by number * chore: change panel type on panel type change in url * chore: remove consoles * feat: added dynamic variables creation flow (#7541) * feat: added dynamic variables creation flow * feat: added keys and value apis and hooks * feat: added api and select component changes * feat: added keys fetching and preview values * feat: added dynamic variable to variable items * feat: handled value persistence and tab switches * feat: added default value and formed a schema for dyn-variables * feat: added client and server side searches * feat: corrected the initial load getfieldKey api * feat: removed fetch on mount restriction * feat: added dynamic variable to the dashboard details (#7755) * feat: added dynamic variable to the dashboard details * feat: added new component to existing variables * feat: added enhancement to multiselect and select for dyn-variables * feat: added refetch method between all dynamic-variables * feat: correct error handling * feat: correct error handling * feat: enforced non-empty selectedvalues and default value * feat: added client and server side searches * feat: retry on error * feat: correct error handling * feat: handle defautl value in existing variables * feat: lowercase the source for payload * feat: fixed the incorrect assignment of active indices * feat: improved handling of all option * feat: improved the ALL option visuals * feat: handled default value enforcement in existing variables * feat: added unix time to values call * feat: added incomplete data message and info to search * feat: changed dashboard panel call handling with existing variables * feat: adjusted the response type and data with the new API schema for values * feat: code refactor * feat: made dyn-variable option as the default * feat: added test cases for dyn variable creation and completion * feat: updated test cases * feat: fix lint and test cases * feat: fix typo * feat: fixed test case * feat: added dynamic variable suggestion in where clause * feat: added test cases for hooks and api call functions * feat: added test case for querybuildersearchv2 suggestion changes * feat: code refactor * feat: corrected the regex matcher for resolved titles * feat: fixed test cases * feat: added ability to add/remove variable filter to one or more existing panels * feat: added widgetselector on variable creation * feat: show labels in widget selector * feat: added apply to all and variable removal logical * feat: refectch only related and affected panels in case of dynamic variables * feat: added button loader for apply-all * feat: light-mode styles * fix: added migration to filter expression for crud operations of variable * feat: added type in the variables in query_range payload for dynamic * feat: correct the variable addition to panel format for new qb expression * feat: added test cases for dynamic variable and add/remove panel feat * feat: implemented where clause suggestion in new qb v5 * feat: added retries for dyn variable and fixed on-enter selection issue * feat: added relatedValues and existing query in param related changes * feat: sanitized data storage and removed duplicates * fix: fixed typechecks * feat: updated panel wait and refetch logic and ALL option selection * feat: fixed variable tabel reordering issue * feat: added empty name validation in variable creation * feat: change value to searchtext in values API * feat: added option for regex in the component, disabled for now * feat: added beta and not rec. tag in variable tabs * feat: added check to prevent api and updates calls with same payload * feat: optimized localstorage for all selection in dynamic variable and updated __all__ case * feat: resolved variable tables infinite loop update error * feat: aded variable name auto-update based on attribute name entered for dynamic variables * feat: modified only/all click behaviour and set all selection always true for dynamic variable * feat: fix dropdown closing doesn't reset us back to our all available values when we have a search * feat: handled all state distinction and carry forward in existing variables * feat: trucate + n more tooltip content to 10 * feat: fixed infinite loop because of dependency of frequently changing object ref in var table * feat: fixed inconsist search implementations * feat: reverted only - all updated area implementation * feat: added more space for search in multiselect component * feat: checked for variable id instead of variable key for refetch * feat: improved performance around multiselect component and added confirm modal for apply to all * feat: rewrite functionality around add and remove panels * feat: changed color for apply to all modal * feat: added changes under flag to handle variable specific removal for removeKeysFromExpression func * feat: added validation in variable edit panel * chore: fix dynamic variable update in context menu to latest logic * chore: minor fix * chore: type fix * fix: remove unwanted code * fix: remove unwanted code * fix: resolved pr comments * fix: minor fix * fix: fix tests * fix: style fix * fix: hide drilldown options in view mode for non-builder panels * chore: add global uplot mock * chore: query builder context update to all provider * chore: add cursor rules init * chore: useSafeNavigate mock added * chore: more cleanups * chore: remove react-router-v5 mock from setup * chore: update cursorrules * chore: add tests readme init * chore: minor refactor --------- Co-authored-by: Aditya Singh <adityasingh@Adityas-MacBook-Pro.local> Co-authored-by: Abhi Kumar <ahrefabhi@gmail.com> Co-authored-by: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Co-authored-by: SagarRajput-7 <sagar@signoz.io>
484 lines
13 KiB
Plaintext
484 lines
13 KiB
Plaintext
# Persona
|
||
You are an expert developer with deep knowledge of Jest, React Testing Library, MSW, and TypeScript, tasked with creating unit tests for this repository.
|
||
|
||
# Auto-detect TypeScript Usage
|
||
Check for TypeScript in the project through tsconfig.json or package.json dependencies.
|
||
Adjust syntax based on this detection.
|
||
|
||
# TypeScript Type Safety for Jest Tests
|
||
**CRITICAL**: All Jest tests MUST be fully type-safe with proper TypeScript types.
|
||
|
||
**Type Safety Requirements:**
|
||
- Use proper TypeScript interfaces for all mock data
|
||
- Type all Jest mock functions with `jest.MockedFunction<T>`
|
||
- Use generic types for React components and hooks
|
||
- Define proper return types for mock functions
|
||
- Use `as const` for literal types when needed
|
||
- Avoid `any` type – use proper typing instead
|
||
|
||
# Unit Testing Focus
|
||
Focus on critical functionality (business logic, utility functions, component behavior)
|
||
Mock dependencies (API calls, external modules) before imports
|
||
Test multiple data scenarios (valid inputs, invalid inputs, edge cases)
|
||
Write maintainable tests with descriptive names grouped in describe blocks
|
||
|
||
# Global vs Local Mocks
|
||
**Use Global Mocks for:**
|
||
- High-frequency dependencies (20+ test files)
|
||
- Core infrastructure (react-router-dom, react-query, antd)
|
||
- Standard implementations across the app
|
||
- Browser APIs (ResizeObserver, matchMedia, localStorage)
|
||
- Utility libraries (date-fns, lodash)
|
||
|
||
**Use Local Mocks for:**
|
||
- Business logic dependencies (5-15 test files)
|
||
- Test-specific behavior (different data per test)
|
||
- API endpoints with specific responses
|
||
- Domain-specific components
|
||
- Error scenarios and edge cases
|
||
|
||
**Global Mock Files Available (from jest.config.ts):**
|
||
- `uplot` → `__mocks__/uplotMock.ts`
|
||
|
||
# Repo-specific Testing Conventions
|
||
|
||
## Imports
|
||
Always import from our harness:
|
||
```ts
|
||
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
|
||
```
|
||
For API mocks:
|
||
```ts
|
||
import { server, rest } from 'mocks-server/server';
|
||
```
|
||
Do not import directly from `@testing-library/react`.
|
||
|
||
## Router
|
||
Use the router built into render:
|
||
```ts
|
||
render(<Page />, undefined, { initialRoute: '/traces-explorer' });
|
||
```
|
||
Only mock `useLocation` / `useParams` if the test depends on them.
|
||
|
||
## Hook Mocks
|
||
Pattern:
|
||
```ts
|
||
import useFoo from 'hooks/useFoo';
|
||
jest.mock('hooks/useFoo');
|
||
const mockUseFoo = jest.mocked(useFoo);
|
||
mockUseFoo.mockReturnValue(/* minimal shape */ as any);
|
||
```
|
||
Prefer helpers (`rqSuccess`, `rqLoading`, `rqError`) for React Query results.
|
||
|
||
## MSW
|
||
Global MSW server runs automatically.
|
||
Override per-test:
|
||
```ts
|
||
server.use(
|
||
rest.get('*/api/v1/foo', (_req, res, ctx) => res(ctx.status(200), ctx.json({ ok: true })))
|
||
);
|
||
```
|
||
Keep large responses in `mocks-server/__mockdata_`.
|
||
|
||
## Interactions
|
||
- Prefer `userEvent` for real user interactions (click, type, select, tab).
|
||
- Use `fireEvent` only for low-level/programmatic events not covered by `userEvent` (e.g., scroll, resize, setting `element.scrollTop` for virtualization). Wrap in `act(...)` if needed.
|
||
- Always await interactions:
|
||
```ts
|
||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||
await user.click(screen.getByRole('button', { name: /save/i }));
|
||
```
|
||
|
||
```ts
|
||
// Example: virtualized list scroll (no userEvent helper)
|
||
const scroller = container.querySelector('[data-test-id="virtuoso-scroller"]') as HTMLElement;
|
||
scroller.scrollTop = targetScrollTop;
|
||
act(() => { fireEvent.scroll(scroller); });
|
||
```
|
||
|
||
## Timers
|
||
❌ No global fake timers.
|
||
✅ Per-test only, for debounce/throttle:
|
||
```ts
|
||
jest.useFakeTimers();
|
||
const user = userEvent.setup({ advanceTimers: (ms) => jest.advanceTimersByTime(ms) });
|
||
await user.type(screen.getByRole('textbox'), 'query');
|
||
jest.advanceTimersByTime(400);
|
||
jest.useRealTimers();
|
||
```
|
||
|
||
## Queries
|
||
Prefer accessible queries (`getByRole`, `findByRole`, `getByLabelText`).
|
||
Fallback: visible text.
|
||
Last resort: `data-testid`.
|
||
|
||
# Example Test (using only configured global mocks)
|
||
```ts
|
||
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
|
||
import { server, rest } from 'mocks-server/server';
|
||
import MyComponent from '../MyComponent';
|
||
|
||
describe('MyComponent', () => {
|
||
it('renders and interacts', async () => {
|
||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||
|
||
server.use(
|
||
rest.get('*/api/v1/example', (_req, res, ctx) => res(ctx.status(200), ctx.json({ value: 42 })))
|
||
);
|
||
|
||
render(<MyComponent />, undefined, { initialRoute: '/foo' });
|
||
|
||
expect(await screen.findByText(/value: 42/i)).toBeInTheDocument();
|
||
await user.click(screen.getByRole('button', { name: /refresh/i }));
|
||
await waitFor(() => expect(screen.getByText(/loading/i)).toBeInTheDocument());
|
||
});
|
||
});
|
||
```
|
||
|
||
# Anti-patterns
|
||
❌ Importing RTL directly
|
||
❌ Using global fake timers
|
||
❌ Wrapping render in `act(...)`
|
||
❌ Mocking infra dependencies locally (router, react-query)
|
||
✅ Use our harness (`tests/test-utils`)
|
||
✅ Use MSW for API overrides
|
||
✅ Use userEvent + await
|
||
✅ Pin time only in tests that assert relative dates
|
||
|
||
# Best Practices
|
||
- **Critical Functionality**: Prioritize testing business logic and utilities
|
||
- **Dependency Mocking**: Global mocks for infra, local mocks for business logic
|
||
- **Data Scenarios**: Always test valid, invalid, and edge cases
|
||
- **Descriptive Names**: Make test intent clear
|
||
- **Organization**: Group related tests in describe
|
||
- **Consistency**: Match repo conventions
|
||
- **Edge Cases**: Test null, undefined, unexpected values
|
||
- **Limit Scope**: 3–5 focused tests per file
|
||
- **Use Helpers**: `rqSuccess`, `makeUser`, etc.
|
||
- **No Any**: Enforce type safety
|
||
|
||
# Example Test
|
||
```ts
|
||
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
|
||
import { server, rest } from 'mocks-server/server';
|
||
import MyComponent from '../MyComponent';
|
||
|
||
describe('MyComponent', () => {
|
||
it('renders and interacts', async () => {
|
||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||
|
||
server.use(
|
||
rest.get('*/api/v1/example', (_req, res, ctx) => res(ctx.status(200), ctx.json({ value: 42 })))
|
||
);
|
||
|
||
render(<MyComponent />, undefined, { initialRoute: '/foo' });
|
||
|
||
expect(await screen.findByText(/value: 42/i)).toBeInTheDocument();
|
||
await user.click(screen.getByRole('button', { name: /refresh/i }));
|
||
await waitFor(() => expect(screen.getByText(/loading/i)).toBeInTheDocument());
|
||
});
|
||
});
|
||
```
|
||
|
||
# Anti-patterns
|
||
❌ Importing RTL directly
|
||
❌ Using global fake timers
|
||
❌ Wrapping render in `act(...)`
|
||
❌ Mocking infra dependencies locally (router, react-query)
|
||
✅ Use our harness (`tests/test-utils`)
|
||
✅ Use MSW for API overrides
|
||
✅ Use userEvent + await
|
||
✅ Pin time only in tests that assert relative dates
|
||
|
||
# TypeScript Type Safety Examples
|
||
|
||
## Proper Mock Typing
|
||
```ts
|
||
// ✅ GOOD - Properly typed mocks
|
||
interface User {
|
||
id: number;
|
||
name: string;
|
||
email: string;
|
||
}
|
||
|
||
interface ApiResponse<T> {
|
||
data: T;
|
||
status: number;
|
||
message: string;
|
||
}
|
||
|
||
// Type the mock functions
|
||
const mockFetchUser = jest.fn() as jest.MockedFunction<(id: number) => Promise<ApiResponse<User>>>;
|
||
const mockUpdateUser = jest.fn() as jest.MockedFunction<(user: User) => Promise<ApiResponse<User>>>;
|
||
|
||
// Mock implementation with proper typing
|
||
mockFetchUser.mockResolvedValue({
|
||
data: { id: 1, name: 'John Doe', email: 'john@example.com' },
|
||
status: 200,
|
||
message: 'Success'
|
||
});
|
||
|
||
// ❌ BAD - Using any type
|
||
const mockFetchUser = jest.fn() as any; // Don't do this
|
||
```
|
||
|
||
## React Component Testing with Types
|
||
```ts
|
||
// ✅ GOOD - Properly typed component testing
|
||
interface ComponentProps {
|
||
title: string;
|
||
data: User[];
|
||
onUserSelect: (user: User) => void;
|
||
isLoading?: boolean;
|
||
}
|
||
|
||
const TestComponent: React.FC<ComponentProps> = ({ title, data, onUserSelect, isLoading = false }) => {
|
||
// Component implementation
|
||
};
|
||
|
||
describe('TestComponent', () => {
|
||
it('should render with proper props', () => {
|
||
// Arrange - Type the props properly
|
||
const mockProps: ComponentProps = {
|
||
title: 'Test Title',
|
||
data: [{ id: 1, name: 'John', email: 'john@example.com' }],
|
||
onUserSelect: jest.fn() as jest.MockedFunction<(user: User) => void>,
|
||
isLoading: false
|
||
};
|
||
|
||
// Act
|
||
render(<TestComponent {...mockProps} />);
|
||
|
||
// Assert
|
||
expect(screen.getByText('Test Title')).toBeInTheDocument();
|
||
});
|
||
});
|
||
```
|
||
|
||
## Hook Testing with Types
|
||
```ts
|
||
// ✅ GOOD - Properly typed hook testing
|
||
interface UseUserDataReturn {
|
||
user: User | null;
|
||
loading: boolean;
|
||
error: string | null;
|
||
refetch: () => void;
|
||
}
|
||
|
||
const useUserData = (id: number): UseUserDataReturn => {
|
||
// Hook implementation
|
||
};
|
||
|
||
describe('useUserData', () => {
|
||
it('should return user data with proper typing', () => {
|
||
// Arrange
|
||
const mockUser: User = { id: 1, name: 'John', email: 'john@example.com' };
|
||
mockFetchUser.mockResolvedValue({
|
||
data: mockUser,
|
||
status: 200,
|
||
message: 'Success'
|
||
});
|
||
|
||
// Act
|
||
const { result } = renderHook(() => useUserData(1));
|
||
|
||
// Assert
|
||
expect(result.current.user).toEqual(mockUser);
|
||
expect(result.current.loading).toBe(false);
|
||
expect(result.current.error).toBeNull();
|
||
});
|
||
});
|
||
```
|
||
|
||
## Global Mock Type Safety
|
||
```ts
|
||
// ✅ GOOD - Type-safe global mocks
|
||
// In __mocks__/routerMock.ts
|
||
export const mockUseLocation = (overrides: Partial<Location> = {}): Location => ({
|
||
pathname: '/traces',
|
||
search: '',
|
||
hash: '',
|
||
state: null,
|
||
key: 'test-key',
|
||
...overrides,
|
||
});
|
||
|
||
// In test files
|
||
const location = useLocation(); // Properly typed from global mock
|
||
expect(location.pathname).toBe('/traces');
|
||
```
|
||
|
||
# TypeScript Configuration for Jest
|
||
|
||
## Required Jest Configuration
|
||
```json
|
||
// jest.config.ts
|
||
{
|
||
"preset": "ts-jest/presets/js-with-ts-esm",
|
||
"globals": {
|
||
"ts-jest": {
|
||
"useESM": true,
|
||
"isolatedModules": true,
|
||
"tsconfig": "<rootDir>/tsconfig.jest.json"
|
||
}
|
||
},
|
||
"extensionsToTreatAsEsm": [".ts", ".tsx"],
|
||
"moduleFileExtensions": ["ts", "tsx", "js", "json"]
|
||
}
|
||
```
|
||
|
||
## TypeScript Jest Configuration
|
||
```json
|
||
// tsconfig.jest.json
|
||
{
|
||
"extends": "./tsconfig.json",
|
||
"compilerOptions": {
|
||
"types": ["jest", "@testing-library/jest-dom"],
|
||
"esModuleInterop": true,
|
||
"allowSyntheticDefaultImports": true,
|
||
"moduleResolution": "node"
|
||
},
|
||
"include": [
|
||
"src/**/*",
|
||
"**/*.test.ts",
|
||
"**/*.test.tsx",
|
||
"__mocks__/**/*"
|
||
]
|
||
}
|
||
```
|
||
|
||
## Common Type Safety Patterns
|
||
|
||
### Mock Function Typing
|
||
```ts
|
||
// ✅ GOOD - Proper mock function typing
|
||
const mockApiCall = jest.fn() as jest.MockedFunction<typeof apiCall>;
|
||
const mockEventHandler = jest.fn() as jest.MockedFunction<(event: Event) => void>;
|
||
|
||
// ❌ BAD - Using any
|
||
const mockApiCall = jest.fn() as any;
|
||
```
|
||
|
||
### Generic Mock Typing
|
||
```ts
|
||
// ✅ GOOD - Generic mock typing
|
||
interface MockApiResponse<T> {
|
||
data: T;
|
||
status: number;
|
||
}
|
||
|
||
const mockFetchData = jest.fn() as jest.MockedFunction<
|
||
<T>(endpoint: string) => Promise<MockApiResponse<T>>
|
||
>;
|
||
|
||
// Usage
|
||
mockFetchData<User>('/users').mockResolvedValue({
|
||
data: { id: 1, name: 'John' },
|
||
status: 200
|
||
});
|
||
```
|
||
|
||
### React Testing Library with Types
|
||
```ts
|
||
// ✅ GOOD - Typed testing utilities
|
||
import { render, screen, RenderResult } from '@testing-library/react';
|
||
import { ComponentProps } from 'react';
|
||
|
||
type TestComponentProps = ComponentProps<typeof TestComponent>;
|
||
|
||
const renderTestComponent = (props: Partial<TestComponentProps> = {}): RenderResult => {
|
||
const defaultProps: TestComponentProps = {
|
||
title: 'Test',
|
||
data: [],
|
||
onSelect: jest.fn(),
|
||
...props
|
||
};
|
||
|
||
return render(<TestComponent {...defaultProps} />);
|
||
};
|
||
```
|
||
|
||
### Error Handling with Types
|
||
```ts
|
||
// ✅ GOOD - Typed error handling
|
||
interface ApiError {
|
||
message: string;
|
||
code: number;
|
||
details?: Record<string, unknown>;
|
||
}
|
||
|
||
const mockApiError: ApiError = {
|
||
message: 'API Error',
|
||
code: 500,
|
||
details: { endpoint: '/users' }
|
||
};
|
||
|
||
mockFetchUser.mockRejectedValue(new Error(JSON.stringify(mockApiError)));
|
||
```
|
||
|
||
## Type Safety Checklist
|
||
- [ ] All mock functions use `jest.MockedFunction<T>`
|
||
- [ ] All mock data has proper interfaces
|
||
- [ ] No `any` types in test files
|
||
- [ ] Generic types are used where appropriate
|
||
- [ ] Error types are properly defined
|
||
- [ ] Component props are typed
|
||
- [ ] Hook return types are defined
|
||
- [ ] API response types are defined
|
||
- [ ] Global mocks are type-safe
|
||
- [ ] Test utilities are properly typed
|
||
|
||
# Mock Decision Tree
|
||
```
|
||
Is it used in 20+ test files?
|
||
├─ YES → Use Global Mock
|
||
│ ├─ react-router-dom
|
||
│ ├─ react-query
|
||
│ ├─ antd components
|
||
│ └─ browser APIs
|
||
│
|
||
└─ NO → Is it business logic?
|
||
├─ YES → Use Local Mock
|
||
│ ├─ API endpoints
|
||
│ ├─ Custom hooks
|
||
│ └─ Domain components
|
||
│
|
||
└─ NO → Is it test-specific?
|
||
├─ YES → Use Local Mock
|
||
│ ├─ Error scenarios
|
||
│ ├─ Loading states
|
||
│ └─ Specific data
|
||
│
|
||
└─ NO → Consider Global Mock
|
||
└─ If it becomes frequently used
|
||
```
|
||
|
||
# Common Anti-Patterns to Avoid
|
||
|
||
❌ **Don't mock global dependencies locally:**
|
||
```js
|
||
// BAD - This is already globally mocked
|
||
jest.mock('react-router-dom', () => ({ ... }));
|
||
```
|
||
|
||
❌ **Don't create global mocks for test-specific data:**
|
||
```js
|
||
// BAD - This should be local
|
||
jest.mock('../api/tracesService', () => ({
|
||
getTraces: jest.fn(() => specificTestData)
|
||
}));
|
||
```
|
||
|
||
✅ **Do use global mocks for infrastructure:**
|
||
```js
|
||
// GOOD - Use global mock
|
||
import { useLocation } from 'react-router-dom';
|
||
```
|
||
|
||
✅ **Do create local mocks for business logic:**
|
||
```js
|
||
// GOOD - Local mock for specific test needs
|
||
jest.mock('../api/tracesService', () => ({
|
||
getTraces: jest.fn(() => mockTracesData)
|
||
}));
|
||
``` |