1+ ---
2+ description: Testing patterns and conventions for @data-client/test
3+ globs: '**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx', '**/__tests__/**/*.ts', '**/__tests__/**/*.tsx'
4+ alwaysApply: false
5+ ---
6+
7+ # Testing Patterns (@data-client/test)
8+
9+ ## Hook Testing with renderDataHook()
10+
11+ ```typescript
12+ import { renderDataHook } from '@data-client/test';
13+
14+ it('useSuspense() should render the response', async () => {
15+ const { result, waitFor } = renderDataHook(
16+ () => useSuspense(ArticleResource.get, { id: 5 }),
17+ {
18+ initialFixtures: [
19+ {
20+ endpoint: ArticleResource.get,
21+ args: [{ id: 5 }],
22+ response: { id: 5, title: 'hi ho', content: 'whatever' },
23+ },
24+ ],
25+ },
26+ );
27+ expect(result.current.title).toBe('hi ho');
28+ });
29+ ```
30+
31+ **Options:**
32+ - `initialFixtures` - Set up initial state of the store
33+ - `resolverFixtures` - Add interceptors for subsequent requests
34+ - `getInitialInterceptorData` - Simulate changing server state
35+
36+ **Return values:**
37+ - Inherits all `renderHook()` return values from `@testing-library/react`
38+ - `controller` - Controller instance for manual actions
39+ - `cleanup()` - Cleanup function
40+ - `allSettled()` - Wait for all async operations to complete
41+
42+ ## Fixtures and Interceptors
43+
44+ **Success Fixture:**
45+ ```typescript
46+ interface SuccessFixture {
47+ endpoint;
48+ args;
49+ response;
50+ error?;
51+ delay?;
52+ }
53+ ```
54+
55+ **Response Interceptor:**
56+ ```typescript
57+ interface ResponseInterceptor {
58+ endpoint;
59+ response(...args);
60+ delay?;
61+ delayCollapse?;
62+ }
63+ ```
64+
65+ ## Testing Mutations
66+
67+ **Create operations:**
68+ ```typescript
69+ it('should create a new todo', async () => {
70+ const { result } = renderDataHook(
71+ () => useController(),
72+ {
73+ initialFixtures: [
74+ {
75+ endpoint: TodoResource.getList,
76+ args: [],
77+ response: [],
78+ },
79+ ],
80+ resolverFixtures: [
81+ {
82+ endpoint: TodoResource.getList.push,
83+ response: (newTodo) => ({ ...newTodo, id: 1 }),
84+ },
85+ ],
86+ },
87+ );
88+
89+ const newTodo = { title: 'Test Todo', completed: false };
90+ const createdTodo = await result.current.fetch(TodoResource.getList.push, newTodo);
91+
92+ expect(createdTodo.id).toBe(1);
93+ });
94+ ```
95+
96+ ## Testing Error States
97+
98+ ```typescript
99+ it('should handle fetch errors', async () => {
100+ const { result, waitFor } = renderDataHook(
101+ () => useSuspense(TodoResource.get, { id: 1 }),
102+ {
103+ initialFixtures: [
104+ {
105+ endpoint: TodoResource.get,
106+ args: [{ id: 1 }],
107+ response: null,
108+ error: new Error('Not found'),
109+ },
110+ ],
111+ },
112+ );
113+
114+ await waitFor(() => {
115+ expect(result.current).toBeUndefined();
116+ });
117+ });
118+ ```
119+
120+ ## Testing Components
121+
122+ ```typescript
123+ import { render } from '@testing-library/react';
124+ import { DataProvider } from '@data-client/react';
125+
126+ const renderWithProvider = (component, options = {}) => {
127+ return render(
128+ <DataProvider {...options}>
129+ {component}
130+ </DataProvider>
131+ );
132+ };
133+
134+ it('should render todo list', async () => {
135+ const { getByText } = renderWithProvider(
136+ <TodoList />,
137+ {
138+ initialFixtures: [
139+ {
140+ endpoint: TodoResource.getList,
141+ args: [],
142+ response: [{ id: 1, title: 'Test Todo', completed: false }],
143+ },
144+ ],
145+ },
146+ );
147+
148+ expect(getByText('Test Todo')).toBeInTheDocument();
149+ });
150+ ```
151+
152+ ## Testing with nock (HTTP Endpoint Testing)
153+
154+ ```typescript
155+ import nock from 'nock';
156+
157+ it('should fetch data from API', async () => {
158+ const scope = nock('https://jsonplaceholder.typicode.com')
159+ .get('/todos/1')
160+ .reply(200, { id: 1, title: 'Test', completed: false });
161+
162+ const result = await TodoResource.get({ id: 1 });
163+
164+ expect(result.title).toBe('Test');
165+ scope.done();
166+ });
167+ ```
168+
169+ ## Testing Managers
170+
171+ ```typescript
172+ it('should handle manager middleware', async () => {
173+ const mockManager = {
174+ middleware: (controller) => (next) => async (action) => {
175+ if (action.type === 'FETCH') {
176+ console.log('Fetch action:', action);
177+ }
178+ return next(action);
179+ },
180+ cleanup: jest.fn(),
181+ };
182+
183+ const { controller } = renderDataHook(
184+ () => useController(),
185+ { managers: [mockManager] },
186+ );
187+
188+ await controller.fetch(TodoResource.get, { id: 1 });
189+ expect(mockManager.cleanup).not.toHaveBeenCalled();
190+ });
191+ ```
192+
193+ ## Test File Organization
194+
195+ **Keep tests under `packages/*/src/**/__tests__`:**
196+ ```
197+ packages/react/src/hooks/__tests__/useSuspense.test.ts
198+ packages/react/src/components/__tests__/DataProvider.test.tsx
199+ ```
200+
201+ **Test naming:**
202+ - Node-only: `*.node.test.ts[x]`
203+ - React Native: `*.native.test.ts[x]`
204+ - Regular: `*.test.ts[x]`
205+
206+ ## Best Practices
207+
208+ - Use `renderDataHook()` for testing hooks that use @data-client/react hooks
209+ - Use fixtures or interceptors when testing hooks or components
210+ - Use `nock` when testing networking definitions
211+ - Test both success and error scenarios
212+ - Test mutations and their side effects
213+ - Don't mock @data-client internals directly
214+ - Don't use raw fetch in tests when fixtures are available
0 commit comments