Real files, at real paths, counted every nightCounted 08:17 UTCDiff two files
7,205instruction files1,198repositories visited4,189rows written36files changed8formats20section tags85stacksCounted 13 September 2026
.cursor/rules/e2e-rules.mdcdotCMS/core on mainOpen on GitHubdotCMSRaw file7.4 kBDiff against another filePick the second file

e2e-rules.mdc in dotCMS/core runs 888 words across 22 headings.

The Visual Headless Content Management System for Enterprises

JavaJava950 starsChanged 1 month ago7.4 kBNested, not at the rootCursor rules
Covers

9 of the 20 section tags

In the order a file is read in
Headings

22 headings, in the order the file writes them

01Page Object Model (POM) Conventions for dotCMS E2E Tests
02Overview
03Directory Structure
04POM Rules
051. Page Objects
062. Components
073. Tests
08Selector Rules
09✅ ALWAYS Use data-testid
10❌ NEVER Use CSS selectors
11Why data-testid?
12Code Examples
13✅ Correct POM Implementation
14❌ Incorrect Implementation
15Environment Handling
16Page Objects should handle environment differences:
17Test Data Management
18Centralize test data:
19Naming Conventions
20Best Practices
21Migration Guidelines
22Using Playwright Codegen
Commands

1 commands this file writes down

Extracted from the file, verbatim
npx playwright codegen http://localhost:8080/dotAdmin/#/public/login
The file

.cursor/rules/e2e-rules.mdc

First 160 of 224 lines
1---
2globs: core-web/apps/dotcms-ui-e2e/**/*.spec.ts
3alwaysApply: false
4---
5
6# Page Object Model (POM) Conventions for dotCMS E2E Tests
7
8## Overview
9
10This project follows the **Page Object Model (POM)** pattern for all E2E tests. This ensures maintainability, reusability, and clear separation of concerns.
11
12## Directory Structure
13
14```
15src/
16├── pages/ # Page Objects (one class per page)
17│ ├── login.page.ts
18│ ├── dashboard.page.ts
19│ └── content.page.ts
20├── components/ # Reusable UI components
21│ ├── sideMenu.component.ts
22│ ├── header.component.ts
23│ └── modal.component.ts
24├── tests/ # Test files that use Page Objects
25│ ├── login/
26│ ├── content/
27│ └── navigation/
28├── utils/ # Shared utilities and helpers
29│ └── utils.ts
30└── config/ # Configuration files
31 └── environments.ts
32```
33
34## POM Rules
35
36### 1. Page Objects
37
38- **One class per page** - Each page has its own Page Object class
39- **Encapsulate all page interactions** - All `page.fill()`, `page.click()`, etc. should be in Page Objects
40- **Return meaningful data** - Methods should return relevant information when needed
41- **Handle environment differences** - Page Objects should adapt to different environments (dev/ci)
42- **ALWAYS use data-testid selectors** - Use `page.getByTestId()` instead of `page.locator()` with CSS selectors
43
44### 2. Components
45
46- **Reusable UI elements** - Components that appear across multiple pages
47- **Self-contained logic** - Each component manages its own state and interactions
48- **Composable** - Components can be used within Page Objects
49- **ALWAYS use data-testid selectors** - Use `page.getByTestId()` for all element interactions
50
51### 3. Tests
52
53- **Use Page Objects only** - Never interact directly with the DOM in tests
54- **Descriptive test names** - Clear, readable test descriptions
55- **One test per scenario** - Each test should verify one specific behavior
56- **Use test data files** - Centralize test data in separate files
57
58## Selector Rules
59
60### ✅ ALWAYS Use data-testid
61
62```typescript
63// CORRECT - Use data-testid selectors
64await this.page.getByTestId("userNameInput").click();
65await this.page.getByTestId("userNameInput").fill(username);
66await this.page.getByTestId("password").fill(password);
67await this.page.getByTestId("submitButton").click();
68```
69
70### ❌ NEVER Use CSS selectors
71
72```typescript
73// WRONG - Don't use CSS selectors
74await this.page.locator('input[id="userId"]').fill(username);
75await this.page.locator('button[id="loginButton"]').click();
76await this.page.locator(".login-form input").fill(username);
77```
78
79### Why data-testid?
80
811. **More stable** - Not affected by CSS class changes or styling updates
822. **More specific** - Designed specifically for testing
833. **Better performance** - Playwright's `getByTestId()` is optimized
844. **Clearer intent** - Makes it obvious the element is for testing
85
86## Code Examples
87
88### ✅ Correct POM Implementation
89
90```typescript
91// pages/login.page.ts
92export class LoginPage {
93 constructor(private page: Page) {}
94
95 async login(username: string, password: string): Promise<void> {
96 const currentEnv = process.env["CURRENT_ENV"] || "dev";
97 const loginUrl =
98 currentEnv === "ci" ? "/login/" : "/dotAdmin/#/public/login";
99
100 await this.page.goto(loginUrl);
101 await this.page.waitForLoadState();
102
103 // Use data-testid selectors
104 await this.page.getByTestId("userNameInput").click();
105 await this.page.getByTestId("userNameInput").fill(username);
106 await this.page.getByTestId("userNameInput").press("Tab");
107 await this.page.getByTestId("password").fill(password);
108 await this.page.getByTestId("submitButton").click();
109 }
110
111 async isLoggedIn(): Promise<boolean> {
112 const currentUrl = this.page.url();
113 return (
114 !currentUrl.includes("/login/") && !currentUrl.includes("/public/login")
115 );
116 }
117}
118
119// tests/login/login.spec.ts
120test("User can login with valid credentials", async ({ page }) => {
121 const loginPage = new LoginPage(page);
122
123 await loginPage.login("admin@dotcms.com", "admin");
124
125 expect(await loginPage.isLoggedIn()).toBe(true);
126});
127```
128
129### ❌ Incorrect Implementation
130
131```typescript
132// DON'T DO THIS - Direct DOM interaction in tests
133test("User can login", async ({ page }) => {
134 await page.goto("/login/");
135 await page.fill('input[id="userId"]', "admin@dotcms.com");
136 await page.fill('input[id="password"]', "admin");
137 await page.click('button[id="loginButton"]');
138});
139
140// DON'T DO THIS - Using CSS selectors in Page Objects
141export class LoginPage {
142 async login(username: string, password: string) {
143 await this.page.locator('input[id="userId"]').fill(username);
144 await this.page.locator('input[id="password"]').fill(password);
145 await this.page.locator('button[id="loginButton"]').click();
146 }
147}
148```
149
150## Environment Handling
151
152### Page Objects should handle environment differences:
153
154```typescript
155export class LoginPage {
156 private getLoginUrl(): string {
157 const currentEnv = process.env["CURRENT_ENV"] || "dev";
158 return currentEnv === "ci" ? "/login/" : "/dotAdmin/#/public/login";
159 }
160}

64 more lines are in the file. Read the raw file.

The rest of the repository

dotCMS/core ships 20 other instruction files

.cursor/rules/java-context.mdccore-web/libs/sdk/client/CLAUDE.md.github/instructions/frontend.instructions.mdcore-web/CLAUDE.mdcore-web/apps/dotcms-ui/AGENTS.mdcore-web/apps/mcp-server/CLAUDE.mddotCMS/src/main/java/com/dotcms/rest/CLAUDE.mdtest-jmeter/CLAUDE.mdcore-web/apps/dotcms-ui-e2e/AGENTS.md.cursor/rules/doc-updates.mdc.cursor/rules/frontend-context.mdccore-web/libs/sdk/react/CLAUDE.mdcore-web/AGENTS.md.github/copilot-instructions.md.cursor/rules/dotcms-guide.mdc.cursor/rules/test-context.mdcCLAUDE.mdcore-web/libs/block-editor/CLAUDE.mdcore-web/libs/new-block-editor/CLAUDE.mdcore-web/libs/portlets/CLAUDE.md

A row that is not a link is a file this repository ships that this app did not freeze a sheet for. It is listed because the corpus knows it exists, and it is not linked because there is nothing here to open.

This listing

Whoever runs dotCMS/core can claim it

This is yours? Claim this config and we will write to you when the measurement moves. The check is one token placed where only you can place it, and there is no account and no password.