Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
40f4930
feat(dotcms-ui-e2e): enhance end-to-end testing setup and add new tes…
oidacra Sep 12, 2025
f2087b2
refactor(login): simplify login URL navigation logic
oidacra Sep 15, 2025
a3476d8
feat(dotcms-ui-e2e): enhance end-to-end testing framework and add new…
oidacra Sep 15, 2025
b7a0b02
feat(dotcms-ui-e2e): update end-to-end testing configuration and add …
oidacra Sep 22, 2025
7afc3ed
chore(dotcms-ui-e2e): update configurations and refactor imports
oidacra Sep 23, 2025
69d2a13
refactor(dotcms-ui-e2e): standardize import styles and improve code r…
oidacra Sep 23, 2025
6e24261
Merge branch 'main' into setup-e2e-base
nicobytes Feb 19, 2026
317df4b
refactor(e2e): migrate E2E tests to Playwright and update configurations
nicobytes Feb 19, 2026
be98810
Merge branch 'main' into setup-e2e-base
nicobytes Feb 19, 2026
f7466e9
Merge branch 'main' into setup-e2e-base
nicobytes Feb 19, 2026
d140b89
Merge branch 'main' into setup-e2e-base
nicobytes Feb 20, 2026
576cbb6
chore: Update .gitignore and justfile, add yarn.lock
nicobytes Feb 20, 2026
38cf463
chore: Update E2E test configurations and dependencies
nicobytes Feb 20, 2026
73eb045
Merge branch 'main' into setup-e2e-base
nicobytes Feb 20, 2026
eccccab
chore: Update dependencies and refactor E2E test structure
nicobytes Feb 23, 2026
29c4ee3
Merge branch 'main' into setup-e2e-base
nicobytes Feb 23, 2026
6b2f2c6
Merge branch 'main' into setup-e2e-base
nicobytes Feb 23, 2026
d4a30d6
fix(e2e): Update E2E test configurations and improve test reliability
nicobytes Feb 23, 2026
202f8bd
chore(nx): update parallel execution setting to 1 for task runner
nicobytes Feb 26, 2026
65effa5
Merge branch 'main' into setup-e2e-base
nicobytes Feb 26, 2026
380be73
feat(dotcms-ui-e2e): integrate Playwright for E2E testing and update …
nicobytes Mar 2, 2026
8cd6765
chore: remove junit.xml from test results and update .gitignore to ex…
nicobytes Mar 2, 2026
f276a6a
chore: update ESLint configuration to include ignored dependencies fo…
nicobytes Mar 2, 2026
1c1c324
fix: add missing newline at end of mcp.json file
nicobytes Mar 2, 2026
c22cf5f
Merge branch 'main' into setup-e2e-base
nicobytes Mar 3, 2026
ed36f47
Merge branch 'main' into setup-e2e-base
nicobytes Mar 3, 2026
e2bd3b2
Merge branch 'main' into setup-e2e-base
nicobytes Mar 3, 2026
0715f57
fix: update test ID attribute and improve test case description
nicobytes Mar 3, 2026
19f888e
Merge branch 'main' into setup-e2e-base
nicobytes Mar 3, 2026
e185690
feat: add yarn.lock file to manage package dependencies
nicobytes Mar 3, 2026
f06919c
fix(e2e): Update E2E test configurations and improve test reliability
nicobytes Mar 4, 2026
7d4fff0
chore: clean up project files by removing unused yarn.lock files and …
nicobytes Mar 4, 2026
685499e
chore: add new yarn.lock file to manage dependencies for core-web
nicobytes Mar 4, 2026
d931d2b
refactor: restructure CLAUDE.md and enhance GitHub issue creation pro…
nicobytes Mar 4, 2026
798f343
Merge branch 'main' into setup-e2e-base
nicobytes Mar 4, 2026
c6deefe
Merge branch 'main' into setup-e2e-base
nicobytes Mar 5, 2026
57b0e85
fix(e2e): Update E2E configuration to conditionally enable based on b…
nicobytes Mar 5, 2026
cfe7034
Merge branch 'setup-e2e-base' of github.com:dotCMS/core into setup-e2…
nicobytes Mar 5, 2026
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
223 changes: 223 additions & 0 deletions .cursor/rules/e2e-rules.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
---
globs: core-web/apps/dotcms-ui-e2e/**/*.spec.ts
alwaysApply: false
---

# Page Object Model (POM) Conventions for dotCMS E2E Tests

## Overview

This project follows the **Page Object Model (POM)** pattern for all E2E tests. This ensures maintainability, reusability, and clear separation of concerns.

## Directory Structure

```
src/
├── pages/ # Page Objects (one class per page)
│ ├── login.page.ts
│ ├── dashboard.page.ts
│ └── content.page.ts
├── components/ # Reusable UI components
│ ├── sideMenu.component.ts
│ ├── header.component.ts
│ └── modal.component.ts
├── tests/ # Test files that use Page Objects
│ ├── login/
│ ├── content/
│ └── navigation/
├── utils/ # Shared utilities and helpers
│ └── utils.ts
└── config/ # Configuration files
└── environments.ts
```

## POM Rules

### 1. Page Objects

- **One class per page** - Each page has its own Page Object class
- **Encapsulate all page interactions** - All `page.fill()`, `page.click()`, etc. should be in Page Objects
- **Return meaningful data** - Methods should return relevant information when needed
- **Handle environment differences** - Page Objects should adapt to different environments (dev/ci)
- **ALWAYS use data-testid selectors** - Use `page.getByTestId()` instead of `page.locator()` with CSS selectors

### 2. Components

- **Reusable UI elements** - Components that appear across multiple pages
- **Self-contained logic** - Each component manages its own state and interactions
- **Composable** - Components can be used within Page Objects
- **ALWAYS use data-testid selectors** - Use `page.getByTestId()` for all element interactions

### 3. Tests

- **Use Page Objects only** - Never interact directly with the DOM in tests
- **Descriptive test names** - Clear, readable test descriptions
- **One test per scenario** - Each test should verify one specific behavior
- **Use test data files** - Centralize test data in separate files

## Selector Rules

### ✅ ALWAYS Use data-testid

```typescript
// CORRECT - Use data-testid selectors
await this.page.getByTestId("userNameInput").click();
await this.page.getByTestId("userNameInput").fill(username);
await this.page.getByTestId("password").fill(password);
await this.page.getByTestId("submitButton").click();
```

### ❌ NEVER Use CSS selectors

```typescript
// WRONG - Don't use CSS selectors
await this.page.locator('input[id="userId"]').fill(username);
await this.page.locator('button[id="loginButton"]').click();
await this.page.locator(".login-form input").fill(username);
```

### Why data-testid?

1. **More stable** - Not affected by CSS class changes or styling updates
2. **More specific** - Designed specifically for testing
3. **Better performance** - Playwright's `getByTestId()` is optimized
4. **Clearer intent** - Makes it obvious the element is for testing

## Code Examples

### ✅ Correct POM Implementation

```typescript
// pages/login.page.ts
export class LoginPage {
constructor(private page: Page) {}

async login(username: string, password: string): Promise<void> {
const currentEnv = process.env["CURRENT_ENV"] || "dev";
const loginUrl =
currentEnv === "ci" ? "/login/" : "/dotAdmin/#/public/login";

await this.page.goto(loginUrl);
await this.page.waitForLoadState();

// Use data-testid selectors
await this.page.getByTestId("userNameInput").click();
await this.page.getByTestId("userNameInput").fill(username);
await this.page.getByTestId("userNameInput").press("Tab");
await this.page.getByTestId("password").fill(password);
await this.page.getByTestId("submitButton").click();
}

async isLoggedIn(): Promise<boolean> {
const currentUrl = this.page.url();
return (
!currentUrl.includes("/login/") && !currentUrl.includes("/public/login")
);
}
}

// tests/login/login.spec.ts
test("User can login with valid credentials", async ({ page }) => {
const loginPage = new LoginPage(page);

await loginPage.login("admin@dotcms.com", "admin");

expect(await loginPage.isLoggedIn()).toBe(true);
});
```

### ❌ Incorrect Implementation

```typescript
// DON'T DO THIS - Direct DOM interaction in tests
test("User can login", async ({ page }) => {
await page.goto("/login/");
await page.fill('input[id="userId"]', "admin@dotcms.com");
await page.fill('input[id="password"]', "admin");
await page.click('button[id="loginButton"]');
});

// DON'T DO THIS - Using CSS selectors in Page Objects
export class LoginPage {
async login(username: string, password: string) {
await this.page.locator('input[id="userId"]').fill(username);
await this.page.locator('input[id="password"]').fill(password);
await this.page.locator('button[id="loginButton"]').click();
}
}
```

## Environment Handling

### Page Objects should handle environment differences:

```typescript
export class LoginPage {
private getLoginUrl(): string {
const currentEnv = process.env["CURRENT_ENV"] || "dev";
return currentEnv === "ci" ? "/login/" : "/dotAdmin/#/public/login";
}
}
```

## Test Data Management

### Centralize test data:

```typescript
// tests/login/credentialsData.ts
export const validCredentials = [
{ username: "admin@dotcms.com", password: "admin" },
{ username: "test@dotcms.com", password: "test" },
];

export const invalidCredentials = [
{ username: "wrong@dotcms.com", password: "wrong" },
];
```

## Naming Conventions

- **Page Objects**: `[PageName].page.ts` (e.g., `login.page.ts`)
- **Components**: `[ComponentName].component.ts` (e.g., `sideMenu.component.ts`)
- **Test Files**: `[FeatureName].spec.ts` (e.g., `login.spec.ts`)
- **Test Data**: `[FeatureName]Data.ts` (e.g., `credentialsData.ts`)

## Best Practices

1. **Keep Page Objects focused** - Each Page Object should handle one page only
2. **Use meaningful method names** - `login()`, `navigateToContent()`, `verifyUserIsLoggedIn()`
3. **Handle waits properly** - Use appropriate waits in Page Objects
4. **Return useful data** - Methods should return information that tests need
5. **Keep tests simple** - Tests should be easy to read and understand
6. **Use TypeScript** - Leverage type safety for better maintainability
7. **ALWAYS use data-testid** - Never use CSS selectors, always use `page.getByTestId()`

## Migration Guidelines

When migrating tests from Maven E2E:

1. **Identify pages** - Create Page Objects for each page
2. **Extract components** - Identify reusable UI components
3. **Centralize data** - Move test data to dedicated files
4. **Update tests** - Refactor tests to use Page Objects
5. **Handle environments** - Ensure Page Objects work in both dev and ci modes
6. **Convert selectors** - Replace all CSS selectors with data-testid selectors

## Using Playwright Codegen

When using Playwright's codegen to generate tests:

1. **Run codegen**: `npx playwright codegen http://localhost:8080/dotAdmin/#/public/login`
2. **Copy the generated selectors** - Use the `getByTestId()` calls from codegen
3. **Update Page Objects** - Replace old selectors with the new data-testid selectors
4. **Test the changes** - Verify the new selectors work correctly

---

**Remember**:

- Always use Page Objects for E2E tests
- Never interact directly with the DOM in test files
- ALWAYS use `data-testid` selectors with `page.getByTestId()`
- Never use CSS selectors like `page.locator('input[id="..."]')`
11 changes: 3 additions & 8 deletions .github/test-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,6 @@ test_types:
needs_node: true
base_maven_args: "verify -De2e.test.skip=false"
suites:
- name: "E2E Tests - JVM Suite"
maven_args: >-
-Dci=true -Dit.test=E2eTestSuite
-De2e.test.forkCount=1 -pl :dotcms-e2e-java
stage_name: "E2E JVM E2E Suite"
- name: "E2E Tests - Node Suite"
maven_args: "-De2e.test.env=ci -pl :dotcms-e2e-node"
stage_name: "E2E Node E2E Suite"
- name: "E2E Tests - Nx Playwright"
maven_args: "-De2e.test.env=ci -pl :dotcms-ui-e2e"
stage_name: "E2E Nx Playwright Suite"
2 changes: 1 addition & 1 deletion .github/workflows/cicd_1-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ jobs:
karate: ${{ fromJSON(needs.initialize.outputs.filters).backend == 'true' }}
frontend: ${{ fromJSON(needs.initialize.outputs.filters).frontend == 'true' }}
cli: ${{ fromJSON(needs.initialize.outputs.filters).cli == 'true' || fromJSON(needs.initialize.outputs.filters).backend == 'true' }}
e2e: false
e2e: ${{ fromJSON(needs.initialize.outputs.filters).build == 'true' }}
secrets:
DOTCMS_LICENSE: ${{ secrets.DOTCMS_LICENSE }}

Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ dotsecure
/core-web/out*/
dist-docs/
**/test-reports/
/core-web/apps/dotcms-ui-e2e/playwright-report
/core-web/apps/dotcms-ui-e2e/test-results
/core-web/apps/dotcms-ui-e2e/test-results/junit.xml
/core-web/apps/dotcms-ui-e2e/target

#env
/core-web/**/.env
Expand Down
6 changes: 5 additions & 1 deletion core-web/.cursor/mcp.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
{
"mcpServers": {
"primeng": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@primeng/mcp"]
},
"nx-mcp": {
"type": "stdio",
"command": "npx",
"args": ["nx", "mcp"]
}
}
}
3 changes: 0 additions & 3 deletions core-web/apps/dotcdn/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ export default {
preset: '../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
globals: {},
coverageReporters: [['lcovonly', { file: 'TEST-dotcdn.lcov' }]],
reporters: [
'default',
['github-actions', { silent: false }],
[
'jest-junit',
{
Expand Down
22 changes: 5 additions & 17 deletions core-web/apps/dotcms-ui-e2e/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
{
"extends": ["plugin:playwright/recommended", "../../.eslintrc.base.json"],
"ignorePatterns": ["!**/*"],
"ignorePatterns": ["playwright-report/**", "test-results/**", "target/**"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["src/**/*.{ts,js,tsx,jsx}"],
"rules": {}
}
{ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "rules": {} },
{ "files": ["*.ts", "*.tsx"], "rules": {} },
{ "files": ["*.js", "*.jsx"], "rules": {} },
{ "files": ["src/**/*.{ts,js,tsx,jsx}"], "rules": {} }
]
}
Loading
Loading