Skip to content

Commit c87a638

Browse files
committed
internal: Add basic cursor rules
1 parent 155b2c1 commit c87a638

File tree

3 files changed

+677
-0
lines changed

3 files changed

+677
-0
lines changed

.cursor/rules/test-files.mdc

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
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

Comments
 (0)