RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Diff/nowtec-nowcrm-cursor-rules-testing-guidelines ↔ nowtec-nowcrm-cursor-rules-readme

Comparison

A · Cursor rules · nowtec/nowCRMB · Cursor rules · nowtec/nowCRM
What each file covers, counted
DimensionSharedOnly in AOnly in BOverlap
Sections044230%
Commands000—
Section tags34330%

What each file covers

Sections

0 shared · 44 only in A · 23 only in B
  • − Testing Guidelines for NOWCRM
  • − Overview
  • − Test Structure
  • − Directory Organization
  • − File Naming Conventions
  • − Page Object Model (POM) Pattern
  • − Structure
  • − Locator Best Practices
  • − Action Methods
  • − Assertion Methods
  • − Test File Structure
  • − Basic Template
  • − Test Organization
  • − Test Data Management
  • − Using Faker for Test Data
  • − Unique Identifiers
  • − Test Credentials
  • − Authentication and Setup
  • − Global Setup
  • − Login Helper
  • − Test Execution Patterns
  • − Waiting Strategies
  • − Error Handling
  • − Test Isolation
  • − Assertions
  • − Best Practices
  • − Helper Functions
  • − Reusable Test Helpers
  • − External Service Helpers
  • − Test Configuration
  • − Playwright Config
  • − Environment Variables
  • − Test Maintenance
  • − Handling Flaky Tests
  • − Skipping Tests
  • − Code Style
  • − Comments
  • − Naming Conventions
  • − Common Patterns
  • − Row Operations
  • − Modal/Dialog Operations
  • − Mass Actions
  • − Best Practices Summary
  • − Anti-Patterns to Avoid
  • + Twenty Development Rules
  • + Rules Overview
  • + Core Guidelines
  • + Code Quality
  • + React Development
  • + Testing & Quality
  • + Internationalization
  • + How Rules Work
  • + Automatic Attachment
  • + Manual Reference
  • + Rule Types Used
  • + Development Commands
  • + Frontend Commands
  • + Backend Commands
  • + Usage Guidelines
  • + For Developers
  • + For AI Assistants
  • + Contributing to Rules
  • + Adding New Rules
  • + Updating Existing Rules
  • + Rule Format Reference
  • + Rule Title
  • + Migration from Legacy Format

Commands

neither file has any

Section tags

3 shared · 4 only in A · 3 only in B
  • − setup
  • − code-style
  • − security
  • − docs
  • + lint-format
  • + types
  • + database
  •   test
  •   architecture
  •   do-not

Line diff

+75 added−521 removed33 unchanged6.0% identical
nowtec/nowCRM · .cursor/rules/testing-guidelines.mdc
@@ −1 @@
1---
2description: Testing guidelines for NOWCRM
3globs: ["**/*.spec.ts", "**/*.test.ts", "**/tests/**", "**/playwright.config.ts"]
4alwaysApply: false
5---
 
6 
7# Testing Guidelines for NOWCRM
8 
9## Overview
10 
11NOWCRM uses **Playwright** for end-to-end (E2E) testing. All tests follow the **Page Object Model (POM)** pattern for maintainability and reusability.
 
12 
13## Test Structure
 
 
 
14 
15### Directory Organization
 
16 
17```
18apps/nowcrm/tests/
19├── *.spec.ts # Test specification files (numbered for execution order)
20├── pages/ # Page Object Models (POMs)
21│ ├── CommonPage.ts
22│ ├── ContactsListPage.ts
23│ └── ...
24├── utils/ # Test utilities and helpers
25│ ├── authHelper.ts
26│ └── data.ts
27├── setup/ # Setup and teardown scripts
28│ ├── global-setup.ts
29│ ├── create-users.ts
30│ └── delete-users.ts
31└── files/ # Test fixtures and data files
32```
33 
34### File Naming Conventions
 
35 
36- **Test files**: Use numbered prefixes for execution order (e.g., `01Authentication.spec.ts`, `02Contacts.spec.ts`)
37- **Page Objects**: Use descriptive names ending with `Page` or `Modal` (e.g., `ContactsListPage.ts`, `ContactCreateModal.ts`)
38- **Utilities**: Use descriptive names (e.g., `authHelper.ts`, `data.ts`)
39 
40## Page Object Model (POM) Pattern
 
41 
42### Structure
 
 
 
43 
44Every Page Object should follow this structure:
 
 
 
 
45 
46```typescript
47import { type Locator, type Page, expect } from '@playwright/test';
48 
49export class PageName {
50 readonly page: Page;
51
52 // Locators - declare as readonly
53 readonly elementName: Locator;
54
55 constructor(page: Page) {
56 this.page = page;
57 // Initialize locators
58 this.elementName = page.getByRole('button', { name: 'Button Name' });
59 }
60
61 // Actions - async methods that perform interactions
62 async performAction() {
63 await expect(this.elementName).toBeVisible();
64 await this.elementName.click();
65 }
66
67 // Assertions - async methods that verify state
68 async expectSomethingVisible(timeout: number = 5000) {
69 await expect(this.elementName, 'Descriptive message').toBeVisible({ timeout });
70 }
71}
72```
73 
74### Locator Best Practices
75 
761. **Prefer role-based selectors**:
77```typescript
78// ✅ Good - accessible and stable
79this.createButton = page.getByRole('button', { name: 'Create' });
80this.emailInput = page.getByRole('textbox', { name: 'Email' });
81 
82// ❌ Avoid - fragile CSS selectors
83this.createButton = page.locator('.btn-primary');
84```
85 
862. **Scope locators within dialogs/modals**:
87```typescript
88constructor(page: Page) {
89 this.dialog = page.getByRole('dialog', { name: /Create Contact/i });
90 // Scope inputs within dialog
91 this.firstNameInput = this.dialog.getByRole('textbox', { name: 'First name' });
92}
93```
94 
953. **Use descriptive locator names**:
96```typescript
97// ✅ Good
98readonly userMenuTrigger: Locator;
99readonly deleteMassActionMenuItem: Locator;
100 
101// ❌ Avoid
102readonly btn1: Locator;
103readonly menuItem: Locator;
104```
 
105 
106### Action Methods
107 
108- **Naming**: Use verb phrases (e.g., `clickCreateButton`, `fillAndSubmit`, `openUserMenu`)
109- **Wait for visibility**: Always wait for elements before interacting
110- **Return values**: Return relevant data when needed (e.g., created entity ID)
 
 
 
111 
112```typescript
113async clickCreateButton() {
114 await expect(this.createButton, 'Create button should be visible').toBeVisible({ timeout: 20000 });
115 await this.createButton.click();
116}
117 
118async fillAndSubmit(data: ContactData) {
119 await this.firstNameInput.fill(data.firstName);
120 await this.lastNameInput.fill(data.lastName);
121 await this.emailInput.fill(data.email);
122 await this.createButton.click();
123}
124```
125 
126### Assertion Methods
127 
128- **Naming**: Prefix with `expect` (e.g., `expectDashboardVisible`, `expectStatusMessage`)
129- **Descriptive messages**: Always include meaningful error messages
130- **Configurable timeouts**: Accept timeout parameters with sensible defaults
 
 
 
131 
132```typescript
133async expectStatusMessage(message: string, timeout: number = 20000) {
134 const messageLocator = this.page.getByText(message, { exact: true });
135 await expect(messageLocator, `Status message "${message}" should be visible`)
136 .toBeVisible({ timeout });
137}
138 
139async expectDashboardVisible(timeout: number = 10000) {
140 await expect(this.page, 'URL should indicate CRM dashboard')
141 .toHaveURL(/\/crm$/, { timeout });
142}
143```
144 
145## Test File Structure
146 
147### Basic Template
 
 
 
 
148 
149```typescript
150import { test, expect } from '@playwright/test';
151import { faker } from '@faker-js/faker';
152 
153// Import Page Object Models
154import { ContactsListPage } from './pages/ContactsListPage';
155import { ContactCreateModal } from './pages/ContactCreateModal';
156 
157// Import utilities
158import { loginUser } from './utils/authHelper';
159 
160test.describe('Feature Name', () => {
161 let pageObject1: ContactsListPage;
162 let pageObject2: ContactCreateModal;
163 
164 test.beforeEach(async ({ page }) => {
165 // Initialize POMs
166 pageObject1 = new ContactsListPage(page);
167 pageObject2 = new ContactCreateModal(page);
168
169 // Common setup (e.g., login)
170 await loginUser(page);
171 await pageObject1.goto();
172 });
173 
174 test('User can perform action', async () => {
175 // Arrange - set up test data
176 const testData = {
177 firstName: faker.person.firstName(),
178 email: faker.internet.email()
179 };
180
181 // Act - perform actions
182 await pageObject1.clickCreateButton();
183 await pageObject2.fillAndSubmit(testData);
184
185 // Assert - verify results
186 await pageObject2.expectCreationStatusMessage(testData.firstName);
187 await expect(pageObject1.getRowLocator(testData.email))
188 .toBeVisible({ timeout: 10000 });
189 });
190});
191```
192 
193### Test Organization
194 
1951. **Use `test.describe` blocks** to group related tests
1962. **Initialize POMs in `beforeEach`** for consistency
1973. **Number test files** for execution order (e.g., `01Authentication.spec.ts`)
1984. **One feature per describe block** (e.g., 'Contact Management', 'Authentication Flow')
199 
200## Test Data Management
201 
202### Using Faker for Test Data
203 
204```typescript
205import { faker } from '@faker-js/faker';
206 
207// Generate unique test data
208const contact = {
209 firstName: faker.person.firstName(),
210 lastName: faker.person.lastName(),
211 email: faker.internet.email({ provider: `test.${faker.string.alphanumeric(5)}.pw` }),
212 address: faker.location.streetAddress(),
213};
214```
215 
216### Unique Identifiers
217 
218- **Use timestamps or random strings** to ensure uniqueness:
219```typescript
220const uniqueEmail = `testuser+${Date.now()}@example.com`;
221const uniqueListName = `List_${faker.string.alphanumeric(6)}`;
222```
223 
224### Test Credentials
225 
226- **Store in environment variables** via `utils/data.ts`:
227```typescript
228export const testCredentials = {
229 email: process.env.TEST_USER_EMAIL || 'testuser@example.com',
230 password: process.env.TEST_USER_PASSWORD || 'StrongPassword123!',
231};
232```
233 
234## Authentication and Setup
235 
236### Global Setup
237 
238- **Use `global-setup.ts`** for authentication state management
239- **Save storage state** to avoid repeated logins:
240```typescript
241await page.context().storageState({ path: STORAGE_STATE_PATH });
242```
243 
244### Login Helper
245 
246- **Create reusable login function** in `utils/authHelper.ts`:
247```typescript
248export async function loginUser(
249 page: Page,
250 postLoginUrlRegex: RegExp = /\/crm$/
251): Promise<void> {
252 await page.goto('/en/auth');
253 await page.getByRole('textbox', { name: 'Email' }).fill(testCredentials.email);
254 await page.getByRole('textbox', { name: 'Password' }).fill(testCredentials.password);
255 await page.getByRole('button', { name: 'Sign in' }).click();
256 await expect(page).toHaveURL(postLoginUrlRegex, { timeout: 15000 });
257}
258```
259 
260## Test Execution Patterns
261 
262### Waiting Strategies
263 
2641. **Use Playwright's auto-waiting**:
265```typescript
266// ✅ Good - Playwright waits automatically
267await button.click();
268 
269// ❌ Avoid - unnecessary manual waits
270await page.waitForTimeout(1000);
271await button.click();
272```
273 
2742. **Use explicit waits for async operations**:
275```typescript
276// ✅ Good - wait for specific condition
277await expect(element).toBeVisible({ timeout: 10000 });
278 
279// ✅ Good - wait for URL change
280await expect(page).toHaveURL(/\/contacts\/\d+\/details/);
281```
282 
2833. **Use `waitForTimeout` sparingly** (only when necessary):
284```typescript
285// Only when waiting for async operations that can't be detected
286await page.waitForTimeout(300); // Wait for dropdown to render
287```
288 
289### Error Handling
290 
291- **Use try/finally blocks** for cleanup:
292```typescript
293test('User can perform action', async ({ page, request }) => {
294 const uniqueEmail = `test+${Date.now()}@example.com`;
295
296 try {
297 // Test logic
298 await createTestUser(request, { email: uniqueEmail });
299 // ... test steps ...
300 } finally {
301 // Cleanup
302 await deleteUserFromStrapi(request, uniqueEmail);
303 await request.delete('http://localhost:8025/api/v1/messages');
304 }
305});
306```
307 
308### Test Isolation
309 
310- **Each test should be independent** - don't rely on test execution order
311- **Clean up test data** after each test
312- **Use unique identifiers** to avoid conflicts
313 
314## Assertions
315 
316### Best Practices
317 
3181. **Always include descriptive messages**:
319```typescript
320// ✅ Good
321await expect(contactRow, 'Contact row should contain correct email')
322 .toContainText(contact.email);
323 
324// ❌ Avoid
325await expect(contactRow).toContainText(contact.email);
326```
327 
3282. **Use appropriate matchers**:
329```typescript
330await expect(element).toBeVisible({ timeout: 10000 });
331await expect(element).toHaveText('Expected Text');
332await expect(element).toContainText('Partial Text');
333await expect(page).toHaveURL(/\/crm$/);
334await expect(locator).toHaveCount(1);
335```
336 
3373. **Set reasonable timeouts**:
338```typescript
339// Default timeout: 5000ms
340await expect(element).toBeVisible();
341 
342// Custom timeout for slow operations
343await expect(element).toBeVisible({ timeout: 20000 });
344```
345 
346## Helper Functions
347 
348### Reusable Test Helpers
349 
350Create helper functions for common operations:
351 
352```typescript
353// In test file or utils
354async function createContactViaUI(data: ContactData) {
355 await contactsListPage.clickCreateButton();
356 await contactCreateModal.waitForDialogVisible();
357 await contactCreateModal.fillAndSubmit(data);
358 await contactCreateModal.expectCreationStatusMessage(data.firstName);
359 await contactsListPage.goto();
360 await expect(contactsListPage.getRowLocator(data.email))
361 .toBeVisible({ timeout: 10000 });
362}
363```
364 
365### External Service Helpers
366 
367For services like Mailpit, create helper classes:
368 
369```typescript
370export class MailpitHelper {
371 readonly request: APIRequestContext;
372
373 constructor(request: APIRequestContext) {
374 this.request = request;
375 }
376
377 async waitForEmails(recipient: string, subject: string, expectedCount = 2) {
378 // Implementation
379 }
380}
381```
382 
383## Test Configuration
384 
385### Playwright Config
386 
387Key configuration patterns:
388 
389```typescript
390export default defineConfig({
391 testDir: './tests',
392 timeout: TIMEOUT, // Default: 30000
393 globalSetup: require.resolve('./tests/setup/global-setup'),
394 expect: {
395 timeout: EXPECT_TIMEOUT, // Default: 5000
396 },
397 fullyParallel: false, // Set to false for sequential execution
398 retries: CI ? 1 : 0,
399 workers: CI ? 1 : WORKERS,
400 use: {
401 baseURL: CRM_BASE_URL,
402 trace: 'on-first-retry',
403 screenshot: 'only-on-failure',
404 video: 'on-first-retry',
405 },
406});
407```
408 
409### Environment Variables
410 
411Required environment variables:
412 
413- `CRM_BASE_URL` - Base URL for the application
414- `TEST_USER_EMAIL` - Test user email
415- `TEST_USER_PASSWORD` - Test user password
416- `STRAPI_TEST_ADMIN_EMAIL` - Strapi admin email
417- `STRAPI_TEST_ADMIN_PASSWORD` - Strapi admin password
418- `PLAYWRIGHT_WORKERS` - Number of workers (optional)
419- `PLAYWRIGHT_RETRIES` - Number of retries (optional)
420- `PLAYWRIGHT_TIMEOUT` - Test timeout (optional)
421 
422## Test Maintenance
423 
424### Handling Flaky Tests
425 
4261. **Increase timeouts** for slow operations
4272. **Add explicit waits** for async operations
4283. **Use more stable locators** (role-based over CSS)
4294. **Retry logic** for known flaky operations:
430```typescript
431let langSelected = false;
432for (let i = 0; i < 3; i++) {
433 try {
434 await langOption.click();
435 langSelected = true;
436 break;
437 } catch (err) {
438 if (i === 2) throw err;
439 await this.page.waitForTimeout(100);
440 }
441}
442```
443 
444### Skipping Tests
445 
446- **Use `test.skip()`** for temporarily disabled tests:
447```typescript
448test.skip('should allow creating a journey with drag-and-drop', async () => {
449 // Test implementation
450});
451```
452 
453- **Use `test.fail()`** for tests that are expected to fail (document why):
454```typescript
455// This test is marked as expected to fail due to a known application bug.
456test.fail('User can edit a list name (expected failure due to edit bug)', async () => {
457 // Test implementation
458});
459```
460 
461## Code Style
462 
463### Comments
464 
465- **Add comments** explaining complex test logic
466- **Document test steps** in multi-step tests:
467```typescript
468// Step 1: Navigate to the login page
469await loginPage.goto();
470 
471// Step 2: Fill in credentials
472await loginPage.fillCredentials(email, password);
473 
474// Step 3: Submit and verify
475await loginPage.clickSignIn();
476await commonPage.expectDashboardVisible();
477```
478 
479### Naming Conventions
480 
481- **Test descriptions**: Use "User can..." or "should..." format
482- **Helper functions**: Use descriptive verb phrases
483- **Variables**: Use camelCase with descriptive names
484 
485## Common Patterns
486 
487### Row Operations
488 
489```typescript
490// Get row locator
491const row = contactsListPage.getRowLocator(uniqueEmail);
492 
493// Get row-specific elements
494const checkbox = contactsListPage.getCheckboxForRow(row);
495const link = contactsListPage.getLinkForRow(row, firstName);
496const deleteButton = contactsListPage.getDeleteButtonForRow(row);
497 
498// Interact with row
499await checkbox.check();
500await link.click();
501await deleteButton.click();
502```
503 
504### Modal/Dialog Operations
505 
506```typescript
507// Wait for modal
508await modal.waitForDialogVisible();
509 
510// Fill form
511await modal.fillAndSubmit(data);
512 
513// Verify success
514await modal.expectCreationStatusMessage(data.name);
515```
516 
517### Mass Actions
518 
519```typescript
520// Select items
521await contactsListPage.getCheckboxForRow(row).check();
522 
523// Open mass actions menu
524await contactsListPage.openMassActionsMenu();
525 
526// Perform action
527await contactsListPage.clickDeleteMassAction();
528await contactsListPage.clickDeleteConfirmMassAction();
529```
530 
531## Best Practices Summary
532 
5331. ✅ **Use Page Object Model** for all page interactions
5342. ✅ **Prefer role-based locators** over CSS selectors
5353. ✅ **Use descriptive test names** and assertion messages
5364. ✅ **Generate unique test data** using Faker
5375. ✅ **Clean up test data** in finally blocks
5386. ✅ **Use helper functions** for common operations
5397. ✅ **Set appropriate timeouts** for async operations
5408. ✅ **Keep tests independent** and isolated
5419. ✅ **Document complex test logic** with comments
54210. ✅ **Handle errors gracefully** with try/finally blocks
543 
544## Anti-Patterns to Avoid
545 
5461. ❌ **Hardcoded test data** - Use Faker or environment variables
5472. ❌ **CSS selectors** - Prefer role-based or accessible selectors
5483. ❌ **Unnecessary waits** - Use Playwright's auto-waiting
5494. ❌ **Test dependencies** - Keep tests independent
5505. ❌ **Missing cleanup** - Always clean up test data
5516. ❌ **Vague assertions** - Include descriptive error messages
5527. ❌ **Duplicate code** - Extract to helper functions or POMs
5538. ❌ **Fragile locators** - Use stable, accessible selectors
554 
nowtec/nowCRM · .cursor/rules/readme.mdc
@@ +1 @@
1---
2description: NOWCRM development rules and best practices
3globs: []
4alwaysApply: true
5---
6# Twenty Development Rules
7 
8This directory contains NOWCRM's development guidelines and best practices in the modern Cursor Rules format (MDC). These rules are automatically applied based on file patterns and provide context-aware guidance to AI assistants.
9 
10## Rules Overview
11 
12### Core Guidelines
13- **architecture.mdc** - Project overview, technology stack, and infrastructure setup (Always Applied)
14 
15### Code Quality
16- **typescript-guidelines.mdc** - TypeScript best practices and conventions (Auto-attached to .ts/.tsx files)
17- **code-style.mdc** - General coding standards and style guide (Auto-attached to code files)
18- **file-structure.mdc** - File and directory organization patterns (Auto-attached to config files)
19 
20### React Development
21- **react-general-guidelines.mdc** - Core React development principles (Auto-attached to React files)
22 
23### Testing & Quality
24- **testing-guidelines.mdc** - Testing strategies and best practices (Auto-attached to test files)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25 
26### Internationalization
27- **translations.mdc** - Translation workflow and i18n setup (Auto-attached to locale files)
28 
29## How Rules Work
 
 
30 
31### Automatic Attachment
32Rules are automatically included in your AI context based on file patterns (globs). When you work on TypeScript files, the TypeScript guidelines are automatically loaded.
33 
34### Manual Reference
35You can manually reference any rule using the `@ruleName` syntax:
36- `@react-general-guidelines` - Load React best practices
37- `@testing-guidelines` - Get testing recommendations
38 
39### Rule Types Used
40- **Always Applied** - Loaded in every context (architecture.mdc, README.mdc)
41- **Auto Attached** - Loaded when matching file patterns are referenced
42- **Agent Requested** - Available for AI to include when relevant
43- **Manual** - Only included when explicitly mentioned
44 
45## Development Commands
 
46 
47### Frontend Commands
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48 
49todo:
50 
51### Backend Commands
 
 
 
 
52 
53todo:
 
 
54 
55## Usage Guidelines
 
 
 
 
 
 
 
56 
57### For Developers
58- Rules are automatically applied based on file context
59- Check rule descriptions to understand when they're activated
60- Use manual references (`@ruleName`) for additional context
61- Keep rules updated as the codebase evolves
62 
63### For AI Assistants
64- Rules provide consistent guidance across conversations
65- Use rule context to maintain coding standards
66- Reference specific rules when making recommendations
67- Apply rule principles in code suggestions and reviews
68 
69## Contributing to Rules
70 
71### Adding New Rules
721. Create a new `.mdc` file in this directory
732. Include proper metadata headers with description and globs
743. Write clear, actionable guidelines with examples
754. Test the rule with relevant file patterns
765. Update this README if needed
77 
78### Updating Existing Rules
791. Modify the rule content while preserving metadata
802. Test changes with affected file patterns
813. Ensure consistency with other rules
824. Update examples and best practices as needed
83 
84## Rule Format Reference
 
 
 
 
 
 
85 
86Each rule file uses the MDC format with metadata:
87 
88```markdown
89---
90description: Brief description of the rule's purpose
91globs: ["**/*.ts", "**/*.tsx"] # File patterns for auto-attachment
92alwaysApply: false # Whether to always include this rule
93---
94 
95# Rule Title
 
 
 
 
 
96 
97Rule content in Markdown format...
 
 
 
98```
99 
100## Migration from Legacy Format
101 
102The rules have been migrated from the legacy `.md` format to the modern `.mdc` format, providing:
103- Better context awareness through file pattern matching
104- Improved organization with metadata headers
105- More flexible rule application strategies
106- Enhanced integration with Cursor's AI features
107 
108For the most up-to-date version of these guidelines, always refer to the files in this directory.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
@@ −1 +1 @@
11 ---
2−description: Testing guidelines for NOWCRM
3−globs: ["**/*.spec.ts", "**/*.test.ts", "**/tests/**", "**/playwright.config.ts"]
4−alwaysApply: false
2+description: NOWCRM development rules and best practices
3+globs: []
4+alwaysApply: true
55 ---
6+# Twenty Development Rules
67  
7−# Testing Guidelines for NOWCRM
8+This directory contains NOWCRM's development guidelines and best practices in the modern Cursor Rules format (MDC). These rules are automatically applied based on file patterns and provide context-aware guidance to AI assistants.
89  
9−## Overview
10+## Rules Overview
1011  
11−NOWCRM uses **Playwright** for end-to-end (E2E) testing. All tests follow the **Page Object Model (POM)** pattern for maintainability and reusability.
12+### Core Guidelines
13+- **architecture.mdc** - Project overview, technology stack, and infrastructure setup (Always Applied)
1214  
13−## Test Structure
15+### Code Quality
16+- **typescript-guidelines.mdc** - TypeScript best practices and conventions (Auto-attached to .ts/.tsx files)
17+- **code-style.mdc** - General coding standards and style guide (Auto-attached to code files)
18+- **file-structure.mdc** - File and directory organization patterns (Auto-attached to config files)
1419  
15−### Directory Organization
20+### React Development
21+- **react-general-guidelines.mdc** - Core React development principles (Auto-attached to React files)
1622  
17−```
18−apps/nowcrm/tests/
19−├── *.spec.ts # Test specification files (numbered for execution order)
20−├── pages/ # Page Object Models (POMs)
21−│ ├── CommonPage.ts
22−│ ├── ContactsListPage.ts
23−│ └── ...
24−├── utils/ # Test utilities and helpers
25−│ ├── authHelper.ts
26−│ └── data.ts
27−├── setup/ # Setup and teardown scripts
28−│ ├── global-setup.ts
29−│ ├── create-users.ts
30−│ └── delete-users.ts
31−└── files/ # Test fixtures and data files
32−```
23+### Testing & Quality
24+- **testing-guidelines.mdc** - Testing strategies and best practices (Auto-attached to test files)
3325  
34−### File Naming Conventions
26+### Internationalization
27+- **translations.mdc** - Translation workflow and i18n setup (Auto-attached to locale files)
3528  
36−- **Test files**: Use numbered prefixes for execution order (e.g., `01Authentication.spec.ts`, `02Contacts.spec.ts`)
37−- **Page Objects**: Use descriptive names ending with `Page` or `Modal` (e.g., `ContactsListPage.ts`, `ContactCreateModal.ts`)
38−- **Utilities**: Use descriptive names (e.g., `authHelper.ts`, `data.ts`)
29+## How Rules Work
3930  
40−## Page Object Model (POM) Pattern
31+### Automatic Attachment
32+Rules are automatically included in your AI context based on file patterns (globs). When you work on TypeScript files, the TypeScript guidelines are automatically loaded.
4133  
42−### Structure
34+### Manual Reference
35+You can manually reference any rule using the `@ruleName` syntax:
36+- `@react-general-guidelines` - Load React best practices
37+- `@testing-guidelines` - Get testing recommendations
4338  
44−Every Page Object should follow this structure:
39+### Rule Types Used
40+- **Always Applied** - Loaded in every context (architecture.mdc, README.mdc)
41+- **Auto Attached** - Loaded when matching file patterns are referenced
42+- **Agent Requested** - Available for AI to include when relevant
43+- **Manual** - Only included when explicitly mentioned
4544  
46−```typescript
47−import { type Locator, type Page, expect } from '@playwright/test';
45+## Development Commands
4846  
49−export class PageName {
50− readonly page: Page;
51−
52− // Locators - declare as readonly
53− readonly elementName: Locator;
54−
55− constructor(page: Page) {
56− this.page = page;
57− // Initialize locators
58− this.elementName = page.getByRole('button', { name: 'Button Name' });
59− }
60−
61− // Actions - async methods that perform interactions
62− async performAction() {
63− await expect(this.elementName).toBeVisible();
64− await this.elementName.click();
65− }
66−
67− // Assertions - async methods that verify state
68− async expectSomethingVisible(timeout: number = 5000) {
69− await expect(this.elementName, 'Descriptive message').toBeVisible({ timeout });
70− }
71−}
72−```
47+### Frontend Commands
7348  
74−### Locator Best Practices
49+todo:
7550  
76−1. **Prefer role-based selectors**:
77−```typescript
78−// ✅ Good - accessible and stable
79−this.createButton = page.getByRole('button', { name: 'Create' });
80−this.emailInput = page.getByRole('textbox', { name: 'Email' });
51+### Backend Commands
8152  
82−// ❌ Avoid - fragile CSS selectors
83−this.createButton = page.locator('.btn-primary');
84−```
53+todo:
8554  
86−2. **Scope locators within dialogs/modals**:
87−```typescript
88−constructor(page: Page) {
89− this.dialog = page.getByRole('dialog', { name: /Create Contact/i });
90− // Scope inputs within dialog
91− this.firstNameInput = this.dialog.getByRole('textbox', { name: 'First name' });
92−}
93−```
55+## Usage Guidelines
9456  
95−3. **Use descriptive locator names**:
96−```typescript
97−// ✅ Good
98−readonly userMenuTrigger: Locator;
99−readonly deleteMassActionMenuItem: Locator;
57+### For Developers
58+- Rules are automatically applied based on file context
59+- Check rule descriptions to understand when they're activated
60+- Use manual references (`@ruleName`) for additional context
61+- Keep rules updated as the codebase evolves
10062  
101−// ❌ Avoid
102−readonly btn1: Locator;
103−readonly menuItem: Locator;
104−```
63+### For AI Assistants
64+- Rules provide consistent guidance across conversations
65+- Use rule context to maintain coding standards
66+- Reference specific rules when making recommendations
67+- Apply rule principles in code suggestions and reviews
10568  
106−### Action Methods
69+## Contributing to Rules
10770  
108−- **Naming**: Use verb phrases (e.g., `clickCreateButton`, `fillAndSubmit`, `openUserMenu`)
109−- **Wait for visibility**: Always wait for elements before interacting
110−- **Return values**: Return relevant data when needed (e.g., created entity ID)
71+### Adding New Rules
72+1. Create a new `.mdc` file in this directory
73+2. Include proper metadata headers with description and globs
74+3. Write clear, actionable guidelines with examples
75+4. Test the rule with relevant file patterns
76+5. Update this README if needed
11177  
112−```typescript
113−async clickCreateButton() {
114− await expect(this.createButton, 'Create button should be visible').toBeVisible({ timeout: 20000 });
115− await this.createButton.click();
116−}
78+### Updating Existing Rules
79+1. Modify the rule content while preserving metadata
80+2. Test changes with affected file patterns
81+3. Ensure consistency with other rules
82+4. Update examples and best practices as needed
11783  
118−async fillAndSubmit(data: ContactData) {
119− await this.firstNameInput.fill(data.firstName);
120− await this.lastNameInput.fill(data.lastName);
121− await this.emailInput.fill(data.email);
122− await this.createButton.click();
123−}
124−```
84+## Rule Format Reference
12585  
126−### Assertion Methods
86+Each rule file uses the MDC format with metadata:
12787  
128−- **Naming**: Prefix with `expect` (e.g., `expectDashboardVisible`, `expectStatusMessage`)
129−- **Descriptive messages**: Always include meaningful error messages
130−- **Configurable timeouts**: Accept timeout parameters with sensible defaults
88+```markdown
89+---
90+description: Brief description of the rule's purpose
91+globs: ["**/*.ts", "**/*.tsx"] # File patterns for auto-attachment
92+alwaysApply: false # Whether to always include this rule
93+---
13194  
132−```typescript
133−async expectStatusMessage(message: string, timeout: number = 20000) {
134− const messageLocator = this.page.getByText(message, { exact: true });
135− await expect(messageLocator, `Status message "${message}" should be visible`)
136− .toBeVisible({ timeout });
137−}
95+# Rule Title
13896  
139−async expectDashboardVisible(timeout: number = 10000) {
140− await expect(this.page, 'URL should indicate CRM dashboard')
141− .toHaveURL(/\/crm$/, { timeout });
142−}
97+Rule content in Markdown format...
14398 ```
14499  
145−## Test File Structure
100+## Migration from Legacy Format
146101  
147−### Basic Template
102+The rules have been migrated from the legacy `.md` format to the modern `.mdc` format, providing:
103+- Better context awareness through file pattern matching
104+- Improved organization with metadata headers
105+- More flexible rule application strategies
106+- Enhanced integration with Cursor's AI features
148107  
149−```typescript
150−import { test, expect } from '@playwright/test';
151−import { faker } from '@faker-js/faker';
152− 
153−// Import Page Object Models
154−import { ContactsListPage } from './pages/ContactsListPage';
155−import { ContactCreateModal } from './pages/ContactCreateModal';
156− 
157−// Import utilities
158−import { loginUser } from './utils/authHelper';
159− 
160−test.describe('Feature Name', () => {
161− let pageObject1: ContactsListPage;
162− let pageObject2: ContactCreateModal;
163− 
164− test.beforeEach(async ({ page }) => {
165− // Initialize POMs
166− pageObject1 = new ContactsListPage(page);
167− pageObject2 = new ContactCreateModal(page);
168−
169− // Common setup (e.g., login)
170− await loginUser(page);
171− await pageObject1.goto();
172− });
173− 
174− test('User can perform action', async () => {
175− // Arrange - set up test data
176− const testData = {
177− firstName: faker.person.firstName(),
178− email: faker.internet.email()
179− };
180−
181− // Act - perform actions
182− await pageObject1.clickCreateButton();
183− await pageObject2.fillAndSubmit(testData);
184−
185− // Assert - verify results
186− await pageObject2.expectCreationStatusMessage(testData.firstName);
187− await expect(pageObject1.getRowLocator(testData.email))
188− .toBeVisible({ timeout: 10000 });
189− });
190−});
191−```
192− 
193−### Test Organization
194− 
195−1. **Use `test.describe` blocks** to group related tests
196−2. **Initialize POMs in `beforeEach`** for consistency
197−3. **Number test files** for execution order (e.g., `01Authentication.spec.ts`)
198−4. **One feature per describe block** (e.g., 'Contact Management', 'Authentication Flow')
199− 
200−## Test Data Management
201− 
202−### Using Faker for Test Data
203− 
204−```typescript
205−import { faker } from '@faker-js/faker';
206− 
207−// Generate unique test data
208−const contact = {
209− firstName: faker.person.firstName(),
210− lastName: faker.person.lastName(),
211− email: faker.internet.email({ provider: `test.${faker.string.alphanumeric(5)}.pw` }),
212− address: faker.location.streetAddress(),
213−};
214−```
215− 
216−### Unique Identifiers
217− 
218−- **Use timestamps or random strings** to ensure uniqueness:
219−```typescript
220−const uniqueEmail = `testuser+${Date.now()}@example.com`;
221−const uniqueListName = `List_${faker.string.alphanumeric(6)}`;
222−```
223− 
224−### Test Credentials
225− 
226−- **Store in environment variables** via `utils/data.ts`:
227−```typescript
228−export const testCredentials = {
229− email: process.env.TEST_USER_EMAIL || 'testuser@example.com',
230− password: process.env.TEST_USER_PASSWORD || 'StrongPassword123!',
231−};
232−```
233− 
234−## Authentication and Setup
235− 
236−### Global Setup
237− 
238−- **Use `global-setup.ts`** for authentication state management
239−- **Save storage state** to avoid repeated logins:
240−```typescript
241−await page.context().storageState({ path: STORAGE_STATE_PATH });
242−```
243− 
244−### Login Helper
245− 
246−- **Create reusable login function** in `utils/authHelper.ts`:
247−```typescript
248−export async function loginUser(
249− page: Page,
250− postLoginUrlRegex: RegExp = /\/crm$/
251−): Promise<void> {
252− await page.goto('/en/auth');
253− await page.getByRole('textbox', { name: 'Email' }).fill(testCredentials.email);
254− await page.getByRole('textbox', { name: 'Password' }).fill(testCredentials.password);
255− await page.getByRole('button', { name: 'Sign in' }).click();
256− await expect(page).toHaveURL(postLoginUrlRegex, { timeout: 15000 });
257−}
258−```
259− 
260−## Test Execution Patterns
261− 
262−### Waiting Strategies
263− 
264−1. **Use Playwright's auto-waiting**:
265−```typescript
266−// ✅ Good - Playwright waits automatically
267−await button.click();
268− 
269−// ❌ Avoid - unnecessary manual waits
270−await page.waitForTimeout(1000);
271−await button.click();
272−```
273− 
274−2. **Use explicit waits for async operations**:
275−```typescript
276−// ✅ Good - wait for specific condition
277−await expect(element).toBeVisible({ timeout: 10000 });
278− 
279−// ✅ Good - wait for URL change
280−await expect(page).toHaveURL(/\/contacts\/\d+\/details/);
281−```
282− 
283−3. **Use `waitForTimeout` sparingly** (only when necessary):
284−```typescript
285−// Only when waiting for async operations that can't be detected
286−await page.waitForTimeout(300); // Wait for dropdown to render
287−```
288− 
289−### Error Handling
290− 
291−- **Use try/finally blocks** for cleanup:
292−```typescript
293−test('User can perform action', async ({ page, request }) => {
294− const uniqueEmail = `test+${Date.now()}@example.com`;
295−
296− try {
297− // Test logic
298− await createTestUser(request, { email: uniqueEmail });
299− // ... test steps ...
300− } finally {
301− // Cleanup
302− await deleteUserFromStrapi(request, uniqueEmail);
303− await request.delete('http://localhost:8025/api/v1/messages');
304− }
305−});
306−```
307− 
308−### Test Isolation
309− 
310−- **Each test should be independent** - don't rely on test execution order
311−- **Clean up test data** after each test
312−- **Use unique identifiers** to avoid conflicts
313− 
314−## Assertions
315− 
316−### Best Practices
317− 
318−1. **Always include descriptive messages**:
319−```typescript
320−// ✅ Good
321−await expect(contactRow, 'Contact row should contain correct email')
322− .toContainText(contact.email);
323− 
324−// ❌ Avoid
325−await expect(contactRow).toContainText(contact.email);
326−```
327− 
328−2. **Use appropriate matchers**:
329−```typescript
330−await expect(element).toBeVisible({ timeout: 10000 });
331−await expect(element).toHaveText('Expected Text');
332−await expect(element).toContainText('Partial Text');
333−await expect(page).toHaveURL(/\/crm$/);
334−await expect(locator).toHaveCount(1);
335−```
336− 
337−3. **Set reasonable timeouts**:
338−```typescript
339−// Default timeout: 5000ms
340−await expect(element).toBeVisible();
341− 
342−// Custom timeout for slow operations
343−await expect(element).toBeVisible({ timeout: 20000 });
344−```
345− 
346−## Helper Functions
347− 
348−### Reusable Test Helpers
349− 
350−Create helper functions for common operations:
351− 
352−```typescript
353−// In test file or utils
354−async function createContactViaUI(data: ContactData) {
355− await contactsListPage.clickCreateButton();
356− await contactCreateModal.waitForDialogVisible();
357− await contactCreateModal.fillAndSubmit(data);
358− await contactCreateModal.expectCreationStatusMessage(data.firstName);
359− await contactsListPage.goto();
360− await expect(contactsListPage.getRowLocator(data.email))
361− .toBeVisible({ timeout: 10000 });
362−}
363−```
364− 
365−### External Service Helpers
366− 
367−For services like Mailpit, create helper classes:
368− 
369−```typescript
370−export class MailpitHelper {
371− readonly request: APIRequestContext;
372−
373− constructor(request: APIRequestContext) {
374− this.request = request;
375− }
376−
377− async waitForEmails(recipient: string, subject: string, expectedCount = 2) {
378− // Implementation
379− }
380−}
381−```
382− 
383−## Test Configuration
384− 
385−### Playwright Config
386− 
387−Key configuration patterns:
388− 
389−```typescript
390−export default defineConfig({
391− testDir: './tests',
392− timeout: TIMEOUT, // Default: 30000
393− globalSetup: require.resolve('./tests/setup/global-setup'),
394− expect: {
395− timeout: EXPECT_TIMEOUT, // Default: 5000
396− },
397− fullyParallel: false, // Set to false for sequential execution
398− retries: CI ? 1 : 0,
399− workers: CI ? 1 : WORKERS,
400− use: {
401− baseURL: CRM_BASE_URL,
402− trace: 'on-first-retry',
403− screenshot: 'only-on-failure',
404− video: 'on-first-retry',
405− },
406−});
407−```
408− 
409−### Environment Variables
410− 
411−Required environment variables:
412− 
413−- `CRM_BASE_URL` - Base URL for the application
414−- `TEST_USER_EMAIL` - Test user email
415−- `TEST_USER_PASSWORD` - Test user password
416−- `STRAPI_TEST_ADMIN_EMAIL` - Strapi admin email
417−- `STRAPI_TEST_ADMIN_PASSWORD` - Strapi admin password
418−- `PLAYWRIGHT_WORKERS` - Number of workers (optional)
419−- `PLAYWRIGHT_RETRIES` - Number of retries (optional)
420−- `PLAYWRIGHT_TIMEOUT` - Test timeout (optional)
421− 
422−## Test Maintenance
423− 
424−### Handling Flaky Tests
425− 
426−1. **Increase timeouts** for slow operations
427−2. **Add explicit waits** for async operations
428−3. **Use more stable locators** (role-based over CSS)
429−4. **Retry logic** for known flaky operations:
430−```typescript
431−let langSelected = false;
432−for (let i = 0; i < 3; i++) {
433− try {
434− await langOption.click();
435− langSelected = true;
436− break;
437− } catch (err) {
438− if (i === 2) throw err;
439− await this.page.waitForTimeout(100);
440− }
441−}
442−```
443− 
444−### Skipping Tests
445− 
446−- **Use `test.skip()`** for temporarily disabled tests:
447−```typescript
448−test.skip('should allow creating a journey with drag-and-drop', async () => {
449− // Test implementation
450−});
451−```
452− 
453−- **Use `test.fail()`** for tests that are expected to fail (document why):
454−```typescript
455−// This test is marked as expected to fail due to a known application bug.
456−test.fail('User can edit a list name (expected failure due to edit bug)', async () => {
457− // Test implementation
458−});
459−```
460− 
461−## Code Style
462− 
463−### Comments
464− 
465−- **Add comments** explaining complex test logic
466−- **Document test steps** in multi-step tests:
467−```typescript
468−// Step 1: Navigate to the login page
469−await loginPage.goto();
470− 
471−// Step 2: Fill in credentials
472−await loginPage.fillCredentials(email, password);
473− 
474−// Step 3: Submit and verify
475−await loginPage.clickSignIn();
476−await commonPage.expectDashboardVisible();
477−```
478− 
479−### Naming Conventions
480− 
481−- **Test descriptions**: Use "User can..." or "should..." format
482−- **Helper functions**: Use descriptive verb phrases
483−- **Variables**: Use camelCase with descriptive names
484− 
485−## Common Patterns
486− 
487−### Row Operations
488− 
489−```typescript
490−// Get row locator
491−const row = contactsListPage.getRowLocator(uniqueEmail);
492− 
493−// Get row-specific elements
494−const checkbox = contactsListPage.getCheckboxForRow(row);
495−const link = contactsListPage.getLinkForRow(row, firstName);
496−const deleteButton = contactsListPage.getDeleteButtonForRow(row);
497− 
498−// Interact with row
499−await checkbox.check();
500−await link.click();
501−await deleteButton.click();
502−```
503− 
504−### Modal/Dialog Operations
505− 
506−```typescript
507−// Wait for modal
508−await modal.waitForDialogVisible();
509− 
510−// Fill form
511−await modal.fillAndSubmit(data);
512− 
513−// Verify success
514−await modal.expectCreationStatusMessage(data.name);
515−```
516− 
517−### Mass Actions
518− 
519−```typescript
520−// Select items
521−await contactsListPage.getCheckboxForRow(row).check();
522− 
523−// Open mass actions menu
524−await contactsListPage.openMassActionsMenu();
525− 
526−// Perform action
527−await contactsListPage.clickDeleteMassAction();
528−await contactsListPage.clickDeleteConfirmMassAction();
529−```
530− 
531−## Best Practices Summary
532− 
533−1. ✅ **Use Page Object Model** for all page interactions
534−2. ✅ **Prefer role-based locators** over CSS selectors
535−3. ✅ **Use descriptive test names** and assertion messages
536−4. ✅ **Generate unique test data** using Faker
537−5. ✅ **Clean up test data** in finally blocks
538−6. ✅ **Use helper functions** for common operations
539−7. ✅ **Set appropriate timeouts** for async operations
540−8. ✅ **Keep tests independent** and isolated
541−9. ✅ **Document complex test logic** with comments
542−10. ✅ **Handle errors gracefully** with try/finally blocks
543− 
544−## Anti-Patterns to Avoid
545− 
546−1. ❌ **Hardcoded test data** - Use Faker or environment variables
547−2. ❌ **CSS selectors** - Prefer role-based or accessible selectors
548−3. ❌ **Unnecessary waits** - Use Playwright's auto-waiting
549−4. ❌ **Test dependencies** - Keep tests independent
550−5. ❌ **Missing cleanup** - Always clean up test data
551−6. ❌ **Vague assertions** - Include descriptive error messages
552−7. ❌ **Duplicate code** - Extract to helper functions or POMs
553−8. ❌ **Fragile locators** - Use stable, accessible selectors
554− 
108+For the most up-to-date version of these guidelines, always refer to the files in this directory.
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack