Skip to content

Commit def5b1c

Browse files
committed
e2e improvements
1 parent 20c7b63 commit def5b1c

File tree

3 files changed

+167
-227
lines changed

3 files changed

+167
-227
lines changed

.cursor/rules/TESTING_GUIDELINES.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,80 @@ test('User can create, customize, and delete a resource', async ({ page }) => {
376376
- Use unique identifiers (timestamps, UUIDs) for test data
377377
- Helper functions should be local to the test file unless truly reusable across multiple test files
378378

379+
### Parallel Test Execution Between Projects
380+
381+
When tests run in parallel across different browser projects (e.g., Chromium, Firefox), they share the same backend state. To avoid conflicts, **create project-specific resources** for each browser.
382+
383+
**Pattern: Project-Specific Test Users**
384+
385+
```typescript
386+
import { test, expect } from '@playwright/test'
387+
import { login, logout, registerUser } from './helpers.js'
388+
389+
const TEST_USER_PASSWORD = 'testpassword123'
390+
391+
// Generate unique username per project (browser)
392+
const getTestUserName = (projectName: string) => `test-user-${projectName}@test.com`
393+
394+
test.describe('User Management', () => {
395+
test('Admin can edit user roles', async ({ page }, testInfo) => {
396+
const projectName = testInfo.project.name // 'chromium', 'firefox', etc.
397+
const testUserName = getTestUserName(projectName)
398+
399+
// Register the project-specific test user inside the test
400+
await registerUser(page, testUserName, TEST_USER_PASSWORD)
401+
await logout(page)
402+
403+
// Login as admin
404+
await login(page)
405+
406+
// Navigate to users list and find the project-specific user
407+
await navigateToUsersSettings(page)
408+
const testUserRow = page.locator('tbody tr', { hasText: testUserName })
409+
await testUserRow.getByRole('button', { name: 'Edit' }).click()
410+
411+
// Perform operations on the project-specific user
412+
// ...
413+
})
414+
})
415+
```
416+
417+
**Note:** Register resources inside the test rather than in `beforeEach` to avoid conflicts when multiple tests would try to create the same resource.
418+
419+
**Key Guidelines:**
420+
421+
1. **Use `testInfo.project.name`** to get the current project (browser) name
422+
2. **Create unique resource names** by incorporating the project name (e.g., `[email protected]`)
423+
3. **Register/create resources in `beforeEach` or `beforeAll`** hooks
424+
4. **Operate on project-specific resources** instead of shared resources
425+
5. **Expect clean application state** - don't handle "already exists" errors gracefully
426+
427+
**When to Use This Pattern:**
428+
429+
- Tests that create, modify, or delete shared resources (users, files, settings)
430+
- Tests that depend on specific resource state
431+
- Any test that could conflict with the same test running in another browser
432+
433+
**Common Resource Types to Isolate:**
434+
435+
- Users: `test-user-${projectName}@test.com`
436+
- Files/Folders: `test-folder-${projectName}`
437+
- Settings/Configs: Use project-specific identifiers
438+
- Any entity with unique constraints
439+
440+
**Available Helpers in `e2e/helpers.ts`:**
441+
442+
```typescript
443+
// Register a new user
444+
export const registerUser = async (page: Page, username: string, password: string) => { ... }
445+
446+
// Login with credentials
447+
export const login = async (page: Page, username?: string, password?: string) => { ... }
448+
449+
// Logout current user
450+
export const logout = async (page: Page) => { ... }
451+
```
452+
379453
## Unit Testing Best Practices
380454

381455
### Component Testing

e2e/helpers.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,32 @@ export const navigateToUsersSettings = async (page: Page) => {
9898
await expect(usersListPage).toBeVisible({ timeout: 10000 })
9999
}
100100

101+
export const registerUser = async (page: Page, username: string, password: string) => {
102+
await page.goto('/')
103+
104+
// Navigate to registration page
105+
const createAccountButton = page.locator('button', { hasText: 'Create Account' })
106+
await expect(createAccountButton).toBeVisible()
107+
await createAccountButton.click()
108+
109+
// Fill registration form
110+
const registerForm = page.locator('shade-register form')
111+
await expect(registerForm).toBeVisible()
112+
113+
const usernameInput = registerForm.locator('input[name="userName"]')
114+
const passwordInput = registerForm.locator('input[name="password"]')
115+
const confirmPasswordInput = registerForm.locator('input[name="confirmPassword"]')
116+
const createAccountSubmitButton = page.locator('shade-register button', { hasText: 'Create Account' })
117+
118+
await usernameInput.fill(username)
119+
await passwordInput.fill(password)
120+
await confirmPasswordInput.fill(password)
121+
await createAccountSubmitButton.click()
122+
123+
// Should be logged in automatically after successful registration
124+
await assertAndDismissNoty(page, 'Account created successfully')
125+
}
126+
101127
export const uploadFile = async (page: Page, filePath: string, mime: string) => {
102128
const fileContent = await readFile(filePath, { encoding: 'utf-8' })
103129
const fileName = basename(filePath)

0 commit comments

Comments
 (0)