Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
8ad1933
Create End2EndTests project structure and add to solution
tjementum May 26, 2025
9306e2e
Add Playwright End2EndTests project with complete configuration
tjementum May 26, 2025
f78bff8
Add VS Code IDE integration for Playwright End2EndTests
tjementum May 26, 2025
8ebce3e
Move e2e test utilities to shared-webapp with failing tests
tjementum May 31, 2025
8de6ec6
Move e2e tests into self-contained systems and delete End2EndTests pr…
tjementum May 31, 2025
655aba4
Configure separate build for e2e tests to avoid hot reload and WebApp…
tjementum Jun 1, 2025
df6d6b3
Add authentication types and utilities for e2e test fixtures
tjementum Jun 4, 2025
a123c76
Add worker-scoped authentication fixtures for parallel e2e testing
tjementum Jun 5, 2025
1b1396a
Change e2e tests to use new shared infrastructure to reuse existing t…
tjementum Jun 5, 2025
eee6197
Update e2e foundation with owner, admin, and member user for each ten…
tjementum Jun 6, 2025
1433ac6
Remove unused e2e test infrastructure to simplify codebase
tjementum Jun 6, 2025
5d17112
Update e2e rules files to reflect separate smoke.spec.ts file per sel…
tjementum Jun 6, 2025
bacf6fa
Stream line e2e tests
tjementum Jun 8, 2025
b6e4289
Update e2e tests
tjementum Jun 10, 2025
8bce55e
Move logic for installing Playwright browsers from npm to e2e CLI
tjementum Jun 10, 2025
8885def
Extract localization flows form big smoke test to separate e2e tests
tjementum Jun 11, 2025
73e0b38
Split smoke tests into focused and dedicated e2e test flows
tjementum Jun 11, 2025
b922a4f
Update shared Playwright.config.ts and e2e CLI to allow filtering of …
tjementum Jun 11, 2025
8308e72
Update e2e to account for auto-submit of one-time verification code
tjementum Jun 11, 2025
3bac627
Change signup e2e tests
tjementum Jun 12, 2025
c0a2213
Change login e2e tests
tjementum Jun 13, 2025
0a55f36
Update e2e tests for new validation rules
tjementum Jun 13, 2025
e5329ff
Add e2e test for bulk deletion in comprehensive test and include test…
tjementum Jun 14, 2025
86c881f
Change all e2e test to use new step decorator
tjementum Jun 14, 2025
7f1115b
Remove names of e2e test flows to elevate @smoke, @comprehensive, and…
tjementum Jun 14, 2025
648dc7c
Rename step-decorator.ts to test-step-wrapper.ts to better reflect fu…
tjementum Jun 29, 2025
ce66cf6
Change email domains in e2e tests to use [timestamp].local
tjementum Jun 28, 2025
0b8579d
Add step decorators for Playwright E2E test maintainability
tjementum Jun 14, 2025
44f9381
Add guidance to e2e-test about location
tjementum Jun 15, 2025
aa9a09e
Update e2e-tests with new naming of expect utilities
tjementum Jun 15, 2025
0c5ec01
Update e2e-tests AI rules to match new infrastructure and conventions
tjementum Jun 15, 2025
4ba334f
Remove background polling for toasts to improve test speed and enhanc…
tjementum Jun 14, 2025
37c6aec
Add sample test using new infrastructure
tjementum Jun 14, 2025
5711c25
Clean up test assertion and auto-close toast to remove overlay blocki…
tjementum Jun 14, 2025
de55c33
Double timeout for slow-mo tests
tjementum Jun 14, 2025
c524e52
Change test-assertion to avoid hardcoded HTTP status codes
tjementum Jun 13, 2025
24c050b
Update end-to-end tests to use http error codes instead of titels
tjementum Jun 13, 2025
c1365f4
Add successful e2e infrastructure cherry-picks
tjementum Jul 2, 2025
1d9738a
Update Playwright configuration to run smoke tests first and set acti…
tjementum Jun 10, 2025
aa30e45
Change Windows to use max 4 workers for end-to-end tests
tjementum Jun 6, 2025
a0c1fb6
Fix end-to-end-tests
tjementum Jul 4, 2025
5caf337
Fix e2e tests after latest changes
tjementum Jul 24, 2025
df4df28
Add comprehensive permission-based UI end-to-end tests
tjementum Jun 30, 2025
e6a381c
Remove exception for 400 errors and apple-touch-icon.png and favicon.ico
tjementum Jul 24, 2025
c712126
Remove legacy decorator support in test-step-wrapper
tjementum Jul 24, 2025
a4223c7
Remove warnings for slow e2e tests
tjementum Jul 24, 2025
7bc92ab
Remove warning in e2e tests about document access
tjementum Jul 24, 2025
41a398a
Split theme and responsiveness flow into theme and new mobile tests
tjementum Jul 24, 2025
012d3ae
Fix end-to-end tests for mobile device optimization
tjementum Jul 27, 2025
3b3d7cd
Remove duplicated test steps
tjementum Jul 28, 2025
ab004f5
Improve login-flows test structure and step naming
tjementum Jul 28, 2025
c98e58f
Improve signup-flows test structure and step naming
tjementum Jul 28, 2025
ee3924f
Improve theme-flows test structure and step naming
tjementum Jul 28, 2025
1c87522
Improve localization-flows test structure and step naming
tjementum Jul 28, 2025
3c964f5
Improve permission-based UI test step naming
tjementum Jul 28, 2025
4790898
Improve user management test step naming
tjementum Jul 28, 2025
3501dea
Improve mobile view test step naming
tjementum Jul 28, 2025
df5bee5
Improve back-office homepage test step naming
tjementum Jul 28, 2025
d3fda54
Add comments to end-to-end tests
tjementum Jul 29, 2025
0d9bd11
Fix duplicate tenant creation logic in authentication caching
tjementum Jul 29, 2025
8f07f5e
Fix timeout values in slow e2e tests and update step name
tjementum Jul 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
262 changes: 262 additions & 0 deletions .cursor/rules/end-to-end-tests/e2e-tests.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
---
description: Rules for end-to-end tests
globs: */tests/e2e/**
alwaysApply: false
---
# End-to-End Tests

These rules outline the structure, patterns, and best practices for writing end-to-end tests.

## Implementation

1. Use `[CLI_ALIAS] e2e` with these option categories to optimize test execution:
- Test filtering: `--smoke`, `--include-slow`, search terms (e.g., `"@smoke"`, `"smoke"`, `"user"`, `"localization"`), `--browser`
- Change scoping: `--last-failed`, `--only-changed`
- Flaky test detection: `--repeat-each`, `--retries`, `--stop-on-first-failure`
- Performance: `--debug-timings` shows step execution times with color coding

2. Test Search and Filtering:
- Search by test tags: `[CLI_ALIAS] e2e "@smoke"` or `[CLI_ALIAS] e2e "smoke"` (both work the same)
- Search by test content: `[CLI_ALIAS] e2e "user"` (finds tests with "user" in title or content)
- Search by filename: `[CLI_ALIAS] e2e "localization"` (finds localization-flows.spec.ts)
- Search by specific file: `[CLI_ALIAS] e2e "user-management-flows.spec.ts"`
- Multiple search terms: `[CLI_ALIAS] e2e "user" "management"`
- The CLI automatically detects which self-contained systems contain matching tests and only runs those

3. Test-Driven Debugging Process:
- Focus on one failing test at a time and make it pass before moving to the next.
- Ensure tests use Playwright's built-in auto-waiting assertions: `toHaveURL()`, `toBeVisible()`, `toBeEnabled()`, `toHaveValue()`, `toContainText()`.
- Consider if root causes can be fixed in the application code, and fix application bugs rather than masking them with test workarounds.

4. Organize tests in a consistent file structure:
- All e2e test files must be located in `[self-contained-system]/WebApp/tests/e2e/` folder (e.g., `application/account-management/WebApp/tests/e2e/`).
- All test files use the `*-flows.spec.ts` naming convention (e.g., `login-flows.spec.ts`, `signup-flows.spec.ts`, `user-management-flows.spec.ts`).
- Top-level describe blocks must use only these 3 approved tags: `test.describe("@smoke", () => {})`, `test.describe("@comprehensive", () => {})`, `test.describe("@slow", () => {})`.
- `@smoke` tests:
- Critical tests run on deployment of any self-contained system.
- Should be comprehensive scenarios that test core user journeys.
- Keep tests focused on specific flows to reduce fragility while maintaining coverage.
- Focus on must-work functionality with extensive validation steps.
- Include boundary cases and error handling within the same test scenario.
- Avoid testing the same functionality multiple times across different tests.

- `@comprehensive` tests:
- Thorough tests run when a specific self-contained system is deployed.
- Focus on edge cases, error conditions, and less common scenarios.
- Test specific features in depth with various input combinations.
- Include tests for concurrency, validation rules, accessibility, etc.
- Group related edge cases together to reduce test count while maintaining coverage.

- `@slow` tests:
- Optional and run only ad-hoc using `--include-slow` flag.
- Any tests that require waiting like `waitForTimeout` (e.g., for OTP timeouts) must be marked as `@slow`.
- Include tests for rate limiting with actual wait times, session timeouts, etc.
- Use `test.setTimeout()` at the individual test level based on actual wait times needed.

5. Write clear test descriptions and documentation:
- Test descriptions must accurately reflect what the test covers and be kept in sync with test implementation.
- Use descriptive test names that clearly indicate the functionality being tested (e.g., "should handle single and bulk user deletion workflows with dashboard integration").
- Include JSDoc comments above complex tests listing all major features/scenarios covered.
- When adding new functionality to existing tests, update both the test description and JSDoc comments to reflect changes.

6. Structure each test with step decorators and proper monitoring:
- All tests must start with `const context = createTestContext(page);` for proper error monitoring.
- Use step decorators: `await step("Complete signup & verify account creation")(async () => { /* test logic */ })();`
- Step naming conventions:
- Always follow "[Business action + details] & [expected outcome]" pattern.
- Use business action verbs like "Sign up", "Login", "Invite", "Rename", "Update", "Delete", "Create", "Submit".
- Never use test/assertion prefixes like "Test", "Verify", "Check", "Validate", "Ensure"; use descriptive business actions instead.
- Every step must include an action (arrange/act) followed by assertions, not pure assertion steps.
- Step structure:
- Use blank lines to separate arrange/act/assert sections within steps.
- Keep shared variable declarations outside steps when used across multiple steps.
- Use section headers with `// === SECTION NAME ===` to group related steps.
- Add JSDoc comments for complex test workflows.
- Use semantic selectors: `page.getByRole("button", { name: "Submit" })`, `page.getByText("Welcome")`, `page.getByLabel("Email")`.
- Assert side effects immediately after actions using `expectToastMessage`, `expectValidationError`, `expectNetworkErrors`.
- Form validation pattern: Use `await blurActiveElement(page);` when updating a textbox the second time before submitting a form to trigger validation.

7. Timeout Configuration:
- Always use Playwright's built-in auto-waiting assertions: `toHaveURL()`, `toBeVisible()`, `toBeEnabled()`, `toHaveValue()`, `toContainText()`.
- Never add timeouts to `.click()`, `.waitForSelector()`, etc.
- Global timeout configuration is handled in the shared Playwright. Don't change this.

8. Write deterministic tests - This is critical for reliable testing:
- Each test should have a clear, linear flow of actions and assertions.
- Never use if statements, custom error handling, or try/catch blocks in tests.
- Never use regular expressions in tests; use simple string matching instead.

9. What to test:
- Enter invalid values, such as empty strings, only whitespace characters, long strings, negative numbers, Unicode, etc.
- Tooltips, keyboard navigation, accessibility, validation messages, translations, responsiveness, etc.

10. Test Fixtures and Page Management:
- Use appropriate fixtures: `{ page }` for basic tests, `{ anonymousPage }` for tests with existing tenant/owner but not logged in, `{ ownerPage }`, `{ adminPage }`, `{ memberPage }` for authenticated tests.
- Destructure anonymous page data: `const { page, tenant } = anonymousPage; const existingUser = tenant.owner;`
- Pre-logged in users (`ownerPage`, `adminPage`, `memberPage`) are isolated between workers and will not conflict between tests.
- When using pre-logged in users, do not put the tenant or user into an invalid state that could affect other tests.

11. Test Data and Constants:
- Use underscore separators: `const timeout = 30_000; // 30 seconds`
- Generate unique data: `const email = uniqueEmail();`
- Use faker.js to generate realistic test data: `const firstName = faker.person.firstName(); const email = faker.internet.email();`
- Long string testing: `const longEmail = \`${"a".repeat(90)}@example.com\`; // 101 characters total`

12. Memory Management in E2E Tests:
- Playwright automatically handles browser context cleanup after tests
- Manual cleanup steps are unnecessary - focus on test clarity over micro-optimizations
- E2E test suites have minimal memory leak concerns due to their limited scope and duration

## Examples

### ✅ Good Step Naming Examples
```typescript
// ✅ DO: Business action + details & expected outcome
await step("Submit invalid email & verify validation error")(async () => {
await page.getByLabel("Email").fill("invalid-email");
await blurActiveElement(page);

await expectValidationError(context, "Invalid email.");
})();

await step("Sign up with valid credentials & verify account creation")(async () => {
await page.getByRole("button", { name: "Submit" }).click();

await expect(page.getByText("Welcome")).toBeVisible();
})();

await step("Update user role to admin & verify permission change")(async () => {
const userRow = page.locator("tbody tr").first();

await userRow.getByLabel("User actions").click();
await page.getByRole("menuitem", { name: "Change role" }).click();

await expect(page.getByRole("alertdialog", { name: "Change user role" })).toBeVisible();
})();
```

### ❌ Bad Step Naming Examples
```typescript
// ❌ DON'T: Pure assertion steps without actions
await step("Verify button is visible")(async () => {
await expect(page.getByRole("button")).toBeVisible(); // No action, only assertion
})();

// ❌ DON'T: Using test/assertion prefixes
await step("Check user permissions")(async () => { // "Check" is assertion prefix
await expect(page.getByText("Admin")).toBeVisible();
})();

await step("Validate form state")(async () => { // "Validate" is assertion prefix
await expect(page.getByRole("textbox")).toBeEmpty();
})();

await step("Ensure user is deleted")(async () => { // "Ensure" is assertion prefix
await expect(page.getByText("user@example.com")).not.toBeVisible();
})();
```

### ✅ Complete Test Example
```typescript
import { step } from "@shared/e2e/utils/step-decorator";
import { expectValidationError, blurActiveElement, createTestContext } from "@shared/e2e/utils/test-assertions";
import { testUser } from "@shared/e2e/utils/test-data";

test.describe("@smoke", () => {
test("should complete signup with validation", async ({ page }) => {
const context = createTestContext(page);
const user = testUser();

await step("Submit invalid email & verify validation error")(async () => {
await page.goto("/signup");
await page.getByLabel("Email").fill("invalid-email");
await blurActiveElement(page); // ✅ DO: Trigger validation when updating textbox second time

await expectValidationError(context, "Invalid email.");
})();

await step("Sign up with valid email & verify verification redirect")(async () => {
await page.getByLabel("Email").fill(user.email);
await page.getByRole("button", { name: "Continue" }).click();

await expect(page).toHaveURL("/verify");
})();
});
});

test.describe("@comprehensive", () => {
test("should handle user management with pre-logged owner", async ({ ownerPage }) => {
createTestContext(ownerPage); // ✅ DO: Create context for pre-logged users

await step("Access user management & verify owner permissions")(async () => {
await ownerPage.getByRole("button", { name: "Users" }).click();

await expect(ownerPage.getByRole("heading", { name: "Users" })).toBeVisible();
})();
});
});

test.describe("@slow", () => {
const requestNewCodeTimeout = 30_000; // 30 seconds
const codeValidationTimeout = 60_000; // 5 minutes
const sessionTimeout = codeValidationTimeout + 60_000; // 6 minutes

test("should handle user logout after to many login attempts", async ({ page }) => { // ✅ DO: use new page, when testing e.g. account lockout
test.setTimeout(sessionTimeout); // ✅ DO: Set timeout based on actual wait times
const context = createTestContext(page);

// ...

await step("Wait for code expiration & verify timeout behavior")(async () => {
await page.goto("/login/verify");
await page.waitForTimeout(codeValidationTimeout); // ✅ DO: Use actual waits in @slow tests

await expect(page.getByText("Your verification code has expired")).toBeVisible();
})();
});
});
```

```typescript
test.describe("@security", () => { // ❌ DON'T: Don't invent new tags - use @smoke, @comprehensive, @slow only
test("should handle login", async ({ page }) => {
// ❌ DON'T: Skip createTestContext(page); step
page.setDefaultTimeout(5000); // ❌ DON'T: Set timeouts manually - use global config

// ❌ DON'T: Use test/assertion prefixes in step descriptions
await step("Test login functionality")(async () => { // ❌ Should be "Submit login form & verify authentication"
await step("Verify button is visible")(async () => { // ❌ Should be "Navigate to page & verify button is visible"
await step("Check user permissions")(async () => { // ❌ Should be "Click user menu & verify permissions"
if (page.url().includes("/login/verify")) { // ❌ DON'T: Add conditional logic - tests should be linear
await page.waitForTimeout(2000); // ❌ DON'T: Add manual timeouts
// Continue with verification... // ❌ DON'T: Write verbose explanatory comments
}

await page.click("#submit-btn"); // ❌ DON'T: Use CSS selectors - use semantic selectors

// ❌ DON'T: Skip assertions for side effects
})();

// ❌ DON'T: Use regular expressions - use simple string matching instead
await expect(page.getByText(/welcome.*home/i)).toBeVisible(); // ❌ Should be: page.getByText("Welcome home")
await expect(page.locator('input[name*="email"]')).toBeFocused(); // ❌ Should be: page.getByLabel("Email")
});

// ❌ DON'T: Place assertions outside test functions
expect(page.url().includes("/admin") || page.url().includes("/login")).toBeTruthy(); // ❌ DON'T: Use ambiguous assertions

// ❌ DON'T: Use try/catch to handle flaky behavior - makes tests unreliable
try {
await page.waitForLoadState("networkidle"); // ❌ DON'T: Add timeout logic in tests
await page.getByRole("button", { name: "Submit" }).click({ timeout: 1000 }); // ❌ DON'T: Add timeouts to actions
} catch (error) {
await page.waitForTimeout(1000); // ❌ DON'T: Add manual waits
console.log("Retrying..."); // ❌ DON'T: Add custom error handling
}
});

// ❌ DON'T: Create tests without proper organization
test("isolated test without describe block", async ({ page }) => {
// ❌ DON'T: Violates organization rules
});
```
64 changes: 64 additions & 0 deletions .cursor/rules/workflows/create-e2e-tests.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
description: Workflow for creating end-to-end tests
globs:
alwaysApply: false
---
# E2E Testing Workflow

This workflow guides you through the process of creating comprehensive end-to-end tests for specific features like login and signup. It focuses on identifying what tests to write, planning complex scenarios, and ensuring tests follow the established conventions.

## Workflow

1. Understand the feature under test:
- Study the frontend components and their interactions.
- Review API endpoints and authentication flows.
- Understand validation rules and error handling.
- Identify key user interactions and expected behaviors.

2. Use Browser MCP to explore the webapp functionality:
- Navigate to the application: `mcp0_browser_navigate({ url: "https://localhost:9000" })`.
- Interact with the feature manually to understand user flows.
- Take snapshots to identify UI elements and their structure.
- Document key interactions and expected behaviors.
- Note any edge cases or potential issues discovered during exploration.

3. Review existing test examples:
- Read [End-to-End Tests](/.cursor/rules/end-to-end-tests/e2e-tests.mdc) for detailed information.
- Examine [signup.spec.ts](/application/account-management/WebApp/tests/e2e/signup.spec.ts) and [login.spec.ts](/application/account-management/WebApp/tests/e2e/login.spec.ts) for inspiration.
- Note the structure, assertions, test organization, and the "Act & Assert:" comment format.

4. Plan comprehensive test scenarios:
- Identify standard user journeys through the feature.
- Plan for complex multi-session scenarios like:
- Concurrent sessions: What happens when a user has two tabs open?
- Cross-session state changes: What happens when state changes in one session affect another?
- Authentication conflicts: How does the system handle authentication changes across sessions?
- Form submissions across sessions: What happens with concurrent form submissions?
- Antiforgery token handling: How are antiforgery tokens managed across tabs?
- Browser navigation: Back/forward buttons, refresh, direct URL access.
- Network conditions: Slow connections, disconnections during operations.
- Input validation: Boundary values, special characters, extremely long inputs.
- Accessibility: Keyboard navigation, screen reader compatibility.
- Localization: Testing with different languages and formats.

5. Categorize tests appropriately:
- `@smoke`: Essential functionality that will run on deployment of any system.
- Create one comprehensive smoke.spec.ts per self-contained system.
- Test complete user journeys: signup → profile setup → invite users → manage roles → tenant settings → logout.
- Include validation errors, retries, and recovery scenarios within the journey.
- `@comprehensive`: More thorough tests covering edge cases that will run on deployment of the system under test.
- Focus on specific feature areas with deep testing of edge cases.
- Group related scenarios to minimize test count while maximizing coverage.
- `@slow`: Tests involving timeouts or waiting periods that will run ad-hoc, when features under test are changed.

6. Create or update test structure:
- For smoke tests: Create/update `application/[scs-name]/WebApp/tests/e2e/smoke.spec.ts`.
- For comprehensive tests: Create feature-specific files like `user-management.spec.ts`, `authentication.spec.ts`.
- Avoid creating many small, isolated tests - prefer comprehensive scenarios that test multiple aspects.

## Key principles

- Comprehensive coverage: Test all critical paths and important edge cases.
- Follow conventions: Adhere to the established patterns in [End-to-End Tests](/.cursor/rules/end-to-end-tests/e2e-tests.mdc).
- Clear organization: Properly categorize tests and use descriptive names.
- Realistic user journeys: Test scenarios that reflect actual user behavior.
12 changes: 8 additions & 4 deletions .cursor/rules/workflows/implement-product-increment.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ Follow these steps which describe in detail how you must implement the tasks in

Before implementing each task, review the relevant rules thoroughly:

- For **backend tasks**:
- Review all the [Backend](mdc:.cursor/rules/backend) rule files.
- For **frontend tasks**:
- Review all the [Frontend](mdc:.cursor/rules/frontend) rule files.
- For **backend tasks**:
- Review all the [Backend](mdc:.cursor/rules/backend) rule files.
- For **frontend tasks**:
- Review all the [Frontend](mdc:.cursor/rules/frontend) rule files.
- For **end-to-end tests**:
- Review the [E2E Testing Workflow](mdc:.cursor/rules/workflows/create-e2e-tests.mdc) and all the [End-to-End Tests](mdc:.cursor/rules/end-to-end-tests) rule files.
- For **Developer CLI commands**:
- Review all the [Developer CLI](mdc:.cursor/rules/developer-cli) rule files.

These rules define the conventions and must be strictly adhered to during implementation.

Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,10 @@ dist/

# Git submodules
.gitmodules

# Playwright E2E testing artifacts
test-results/
playwright-report/
**/playwright/.cache/
**/.auth/

5 changes: 3 additions & 2 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"recommendations": [
"biomejs.biome",
"bradlc.vscode-tailwindcss",
"ms-azuretools.vscode-bicep",
"github.vscode-github-actions",
"biomejs.biome",
"ms-playwright.playwright",
"github.vscode-github-actions"
]
}
Loading
Loading