Skip to content

Commit 320eb51

Browse files
authored
[NA] [FE] Update cursor rules or FE (#3278)
* [OPIK-0001] [FE] Update cursor rules or FE * Added always true for all FE rules
1 parent 3c43e1d commit 320eb51

File tree

9 files changed

+682
-283
lines changed

9 files changed

+682
-283
lines changed

apps/opik-frontend/.cursor/rules/accessibility-testing.mdc

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
---
22
description: Frontend accessibility guidelines and testing patterns
33
globs: "**/*"
4-
alwaysApply: false
4+
alwaysApply: true
55
---
66

77
# Accessibility & Testing Guidelines
88

99
## Accessibility Guidelines
1010

1111
### Semantic HTML
12+
1213
```typescript
1314
// ✅ Use semantic HTML elements
1415
<header className="comet-header">
@@ -38,6 +39,7 @@ alwaysApply: false
3839
```
3940

4041
### ARIA Labels and Descriptions
42+
4143
```typescript
4244
// ✅ Always provide aria-label for icon buttons
4345
<Button variant="ghost" size="icon" aria-label="Delete item">
@@ -61,7 +63,7 @@ alwaysApply: false
6163
</div>
6264

6365
// ✅ Use aria-expanded for collapsible content
64-
<Button
66+
<Button
6567
aria-expanded={isOpen}
6668
aria-controls="dropdown-menu"
6769
onClick={() => setIsOpen(!isOpen)}
@@ -74,6 +76,7 @@ alwaysApply: false
7476
```
7577

7678
### Keyboard Navigation
79+
7780
```typescript
7881
// ✅ Ensure keyboard navigation works
7982
const handleKeyDown = useCallback((event: React.KeyboardEvent) => {
@@ -98,6 +101,7 @@ const handleKeyDown = useCallback((event: React.KeyboardEvent) => {
98101
```
99102

100103
### Focus Management
104+
101105
```typescript
102106
// ✅ Provide visible focus indicators
103107
.focus-visible:focus {
@@ -122,23 +126,24 @@ import { FocusTrap } from '@radix-ui/react-focus-trap';
122126
```
123127

124128
### Heading Hierarchy
129+
125130
```typescript
126131
// ✅ Use proper heading hierarchy
127132
<div className="page">
128133
<h1>Dashboard</h1>
129-
134+
130135
<section>
131136
<h2>Recent Projects</h2>
132-
137+
133138
<article>
134139
<h3>Project Name</h3>
135140
<p>Project description...</p>
136141
</article>
137142
</section>
138-
143+
139144
<section>
140145
<h2>System Status</h2>
141-
146+
142147
<div>
143148
<h3>Database</h3>
144149
<p>Status: Online</p>
@@ -152,6 +157,7 @@ import { FocusTrap } from '@radix-ui/react-focus-trap';
152157
```
153158

154159
### Loading States and Error Messages
160+
155161
```typescript
156162
// ✅ Include accessible loading states
157163
{isLoading && (
@@ -177,6 +183,7 @@ import { FocusTrap } from '@radix-ui/react-focus-trap';
177183
## Testing Patterns
178184

179185
### Component Testing with React Testing Library
186+
180187
```typescript
181188
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
182189
import userEvent from '@testing-library/user-event';
@@ -191,27 +198,27 @@ describe('UserProfile', () => {
191198

192199
it('renders user information correctly', () => {
193200
render(<UserProfile user={mockUser} />);
194-
201+
195202
expect(screen.getByText('John Doe')).toBeInTheDocument();
196203
expect(screen.getByText('[email protected]')).toBeInTheDocument();
197204
});
198205

199206
it('allows editing user name', async () => {
200207
const user = userEvent.setup();
201208
const onSave = jest.fn();
202-
209+
203210
render(<UserProfile user={mockUser} onSave={onSave} />);
204-
211+
205212
const editButton = screen.getByRole('button', { name: /edit/i });
206213
await user.click(editButton);
207-
214+
208215
const nameInput = screen.getByLabelText(/name/i);
209216
await user.clear(nameInput);
210217
await user.type(nameInput, 'Jane Doe');
211-
218+
212219
const saveButton = screen.getByRole('button', { name: /save/i });
213220
await user.click(saveButton);
214-
221+
215222
await waitFor(() => {
216223
expect(onSave).toHaveBeenCalledWith({
217224
...mockUser,
@@ -223,58 +230,60 @@ describe('UserProfile', () => {
223230
```
224231

225232
### Custom Hook Testing
233+
226234
```typescript
227-
import { renderHook, act } from '@testing-library/react';
228-
import { useEntityList } from './useEntityList';
235+
import { renderHook, act } from "@testing-library/react";
236+
import { useEntityList } from "./useEntityList";
229237

230-
describe('useEntityList', () => {
231-
it('fetches entities successfully', async () => {
238+
describe("useEntityList", () => {
239+
it("fetches entities successfully", async () => {
232240
const mockData = {
233-
content: [{ id: '1', name: 'Entity 1' }],
241+
content: [{ id: "1", name: "Entity 1" }],
234242
total: 1,
235243
};
236-
244+
237245
// Mock API call
238246
jest.mocked(api.get).mockResolvedValue({ data: mockData });
239-
247+
240248
const { result } = renderHook(() =>
241-
useEntityList({ workspaceName: 'test', page: 1, size: 10 })
249+
useEntityList({ workspaceName: "test", page: 1, size: 10 }),
242250
);
243-
251+
244252
await waitFor(() => {
245253
expect(result.current.isSuccess).toBe(true);
246254
});
247-
255+
248256
expect(result.current.data).toEqual(mockData);
249257
});
250258
});
251259
```
252260

253261
### API Mocking with MSW
262+
254263
```typescript
255264
// src/mocks/handlers.ts
256-
import { rest } from 'msw';
265+
import { rest } from "msw";
257266

258267
export const handlers = [
259-
rest.get('/api/v1/entities', (req, res, ctx) => {
268+
rest.get("/api/v1/entities", (req, res, ctx) => {
260269
return res(
261270
ctx.json({
262271
content: [
263-
{ id: '1', name: 'Entity 1' },
264-
{ id: '2', name: 'Entity 2' },
272+
{ id: "1", name: "Entity 1" },
273+
{ id: "2", name: "Entity 2" },
265274
],
266275
total: 2,
267-
})
276+
}),
268277
);
269278
}),
270-
271-
rest.delete('/api/v1/entities/:id', (req, res, ctx) => {
279+
280+
rest.delete("/api/v1/entities/:id", (req, res, ctx) => {
272281
return res(ctx.status(204));
273282
}),
274283
];
275284

276285
// In test files
277-
import { server } from '../mocks/server';
286+
import { server } from "../mocks/server";
278287

279288
beforeEach(() => {
280289
server.listen();
@@ -290,6 +299,7 @@ afterAll(() => {
290299
```
291300

292301
### Integration Testing
302+
293303
```typescript
294304
import { render, screen, waitFor } from '@testing-library/react';
295305
import userEvent from '@testing-library/user-event';
@@ -303,7 +313,7 @@ const createWrapper = () => {
303313
mutations: { retry: false },
304314
},
305315
});
306-
316+
307317
return ({ children }: { children: React.ReactNode }) => (
308318
<QueryClientProvider client={queryClient}>
309319
{children}
@@ -314,33 +324,33 @@ const createWrapper = () => {
314324
describe('EntityListPage Integration', () => {
315325
it('loads and displays entities', async () => {
316326
render(<EntityListPage />, { wrapper: createWrapper() });
317-
327+
318328
// Wait for loading to complete
319329
await waitFor(() => {
320330
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
321331
});
322-
332+
323333
// Check if entities are displayed
324334
expect(screen.getByText('Entity 1')).toBeInTheDocument();
325335
expect(screen.getByText('Entity 2')).toBeInTheDocument();
326336
});
327-
337+
328338
it('handles entity deletion flow', async () => {
329339
const user = userEvent.setup();
330340
render(<EntityListPage />, { wrapper: createWrapper() });
331-
341+
332342
await waitFor(() => {
333343
expect(screen.getByText('Entity 1')).toBeInTheDocument();
334344
});
335-
345+
336346
// Find and click delete button
337347
const deleteButton = screen.getByRole('button', { name: /delete entity 1/i });
338348
await user.click(deleteButton);
339-
349+
340350
// Confirm deletion
341351
const confirmButton = screen.getByRole('button', { name: /confirm/i });
342352
await user.click(confirmButton);
343-
353+
344354
// Check if entity is removed
345355
await waitFor(() => {
346356
expect(screen.queryByText('Entity 1')).not.toBeInTheDocument();
@@ -360,7 +370,7 @@ describe('EntityListPage Integration', () => {
360370

361371
```typescript
362372
// ✅ Good: Testing behavior
363-
expect(screen.getByRole('button', { name: /save/i })).toBeEnabled();
373+
expect(screen.getByRole("button", { name: /save/i })).toBeEnabled();
364374

365375
// ❌ Avoid: Testing implementation
366376
expect(component.state.isSaving).toBe(false);
@@ -370,5 +380,5 @@ expect(screen.getByLabelText(/email address/i)).toBeInTheDocument();
370380

371381
// ✅ Good: Testing error states
372382
await user.click(submitButton);
373-
expect(screen.getByRole('alert')).toHaveTextContent('Email is required');
374-
```
383+
expect(screen.getByRole("alert")).toHaveTextContent("Email is required");
384+
```

0 commit comments

Comments
 (0)