Stack: Playwright + TypeScript | Pattern: Page Object Model
Local: http://localhost:8080 (production build) or http://localhost:4200 (dev server)
Production: https://rapidpm.uk (no dedicated test server — all stress tests target production)
cd rpm-ui
# Run full suite
npx playwright test --reporter=list
# Run specific spec file
npx playwright test e2e/specs/gantt-crud.spec.ts --reporter=list
# Run by tag
npx playwright test --grep "@p0" --reporter=list
# Run with headed browser (for debugging)
npx playwright test --headed --grep "GNT-001"
# Generate HTML report
npx playwright test --reporter=html && npx playwright show-report
All page objects live in e2e/pages/ and extend BasePage:
BasePage (base.page.ts)
├── LoginPage (login.page.ts)
├── DashboardPage (dashboard.page.ts)
├── GanttPage (gantt.page.ts)
├── ProjectContentPage (project-content.page.ts)
├── ArtefactDetailsPage (artefact-details.page.ts)
├── CompanyDetailsPage (company-details.page.ts)
├── ProjectDetailsPage (project-details.page.ts)
├── OnboardingPage (onboarding.page.ts)
├── TimesheetPage (timesheet.page.ts)
└── ProjectMembersPage (project-members.page.ts)
Import via barrel: import { GanttPage, DashboardPage } from '../pages';
| File | Functions | Purpose |
|---|---|---|
helpers/navigation.ts |
ensureOnCompanyList(), waitForCompaniesLoaded() |
Navigate to company list, wait for data |
storageState from auth.setup.ts (cookies + e2e_user_id in localStorage)E2E-* tasks when count > 450E2E- prefix for easy identificationDate.now() suffix for uniquenesstoHaveScreenshot() with masking for date-dependent elementse2e/specs/*.spec.ts-snapshots/.gantt_marker_area, .cookie-consent, timeline headers| File | Test IDs | Priority | Description |
|---|---|---|---|
setup-test-account.spec.ts |
— | Setup | Register primary E2E test account |
user-registration.spec.ts |
— | P1 | Full registration flow |
auth-extended.spec.ts |
AUTH-020–022, AUTH-030–033 | P1 | Password reset, session management |
onboarding-extended.spec.ts |
ONB-006–007, ONB-010+ | P2 | Onboarding navigation & flow |
| File | Test IDs | Priority | Description |
|---|---|---|---|
company-management.spec.ts |
CMP-001–005, CMP-010–013 | P1 | Full CRUD: create, read, update, delete companies; user management |
CMP Test Details:
| ID | Name | What it does |
|---|---|---|
| CMP-001 | Create company | Click "Add Company" → fill form → save → verify card appears |
| CMP-002 | Read company details | Open company → verify name, contact, address populated |
| CMP-003 | Update company name | Open → change name → save → verify updated |
| CMP-004 | Delete company | Click delete → confirm dialog → verify removed |
| CMP-005 | Switch between companies | Click different cards → verify URL changes |
| CMP-010 | Add user | Navigate to Users → click Add User → verify form |
| CMP-012 | User roles | Navigate to Users → verify role indicators |
| CMP-013 | Users list | Navigate to Users → verify list content |
| File | Test IDs | Priority | Description |
|---|---|---|---|
project-management.spec.ts |
PRJ-001–006, PRJ-012–013 | P0/P1 | Full CRUD: create, read, update, delete projects; navigation |
PRJ Test Details:
| ID | Name | What it does |
|---|---|---|
| PRJ-001 | Create project | Click "Add Project" → fill form → save → verify card |
| PRJ-002 | View project list | Verify title, cards, Add button visible |
| PRJ-003 | Update project name | Open settings → change name → save → verify |
| PRJ-004 | Delete project | Open settings → delete → confirm → verify removed |
| PRJ-005 | Navigate between projects | Click different cards → verify URLs differ |
| PRJ-006 | Project status | Verify status badges on project cards |
| PRJ-012 | Settings navigation | Click Settings link → verify form loads |
| PRJ-013 | Breadcrumb/back | Verify back button or breadcrumbs exist |
| PRJ-013b | Back button | Click back → verify URL changes |
| File | Test IDs | Priority | Description |
|---|---|---|---|
artefact-crud.spec.ts |
ART-001–005 | P1 | Full CRUD: create, read, update, delete artefacts; file upload |
content-grid.spec.ts |
CNT-001–005 | P0 | Content grid display, document rows |
content-advanced.spec.ts |
CNT-020–043 | P1 | Containers, PBS hierarchy, filtering |
document-upload.spec.ts |
DOC-001–009 | P0 | Upload various file types (TXT, RTF, DOCX, etc.) |
document-lifecycle.spec.ts |
— | P1 | Document lifecycle management |
ART Test Details:
| ID | Name | What it does |
|---|---|---|
| ART-001 | Create artefact | Add menu → fill name + mandatory fields → save → verify in grid |
| ART-002 | Read artefact details | Open artefact → verify name and fields populated |
| ART-003 | Update artefact name | Open → change name → save → verify new name in grid |
| ART-004 | Delete artefact | Open → extract ID from URL → API delete with XSRF token → verify removed |
| ART-005 | File upload | Create artefact with file → verify file attached |
| File | Test IDs | Priority | Description |
|---|---|---|---|
gantt-crud.spec.ts |
GNT-001–015 | P0 | Task CRUD, hierarchy, WBS, expand/collapse, full lifecycle |
gantt-operations.spec.ts |
GNT-003–021 | P0/P1 | Zoom, scroll, edit, delete, indent operations |
gantt-views.spec.ts |
GNT-030+ | P0 | Format switching (Gantt/Grid/Timeline/Kanban) |
gantt-baselines.spec.ts |
GNT-070 | P1 | Baseline set/display |
gantt-dependencies.spec.ts |
— | P0/P1 | Link creation, rendering, deletion, circular deps |
gantt-dragdrop.spec.ts |
GNT-040 | P0/P1 | Task move, resize, reorder, resources |
gantt-advanced.spec.ts |
— | P0/P1 | AI optimization, export, undo/redo, checklist |
gantt-performance.spec.ts |
— | P2 | Performance benchmarks, edge cases |
gantt-robustness.spec.ts |
— | P2 | Bug-finding UI interactions, race conditions |
gantt-visual.spec.ts |
VIS-001–018 | Visual | Screenshot baselines for all view modes |
GNT Test Details (gantt-crud.spec.ts):
| ID | Name | What it does |
|---|---|---|
| GNT-001 | Insert key creates task | Press Insert → verify count +1, "New Task" exists |
| GNT-002 | Create subtask | Create parent → create child with parent ID → verify relationship |
| GNT-003 | Multiple creation | Create 3 tasks → verify count +3 |
| GNT-004 | Set progress | Create task → set progress 0.75 → verify |
| GNT-005 | Update name | Create → rename via API → verify old gone, new exists |
| GNT-006 | Task bar visible | Create task → verify bar in timeline |
| GNT-007 | Delete via API | Create → delete → verify count -1 |
| GNT-008 | Delete parent cascades | Create parent+child → delete parent → both gone |
| GNT-009 | Delete non-existent safe | Delete fake name → count unchanged |
| GNT-010 | Indent | Create two tasks → indent second → verify parent-child |
| GNT-011 | Outdent | Create parent+child → outdent child → verify root level |
| GNT-012 | WBS numbers | Create parent + 2 children → verify WBS dot notation |
| GNT-013 | Expand/collapse | Create parent+child → toggle → verify visibility |
| GNT-014 | Indent updates WBS | Indent task → verify WBS contains dot |
| GNT-015 | Full lifecycle | Create → read → rename → update progress → delete → verify |
| File | Test IDs | Priority | Description |
|---|---|---|---|
user-management.spec.ts |
USR-001–024 | P1/P2 | Profile, preferences, administration |
project-members.spec.ts |
SC-1–8 | P1 | Project member management screen |
| File | Test IDs | Priority | Description |
|---|---|---|---|
timesheet.spec.ts |
TSH-001–032 | P1/P2 | View, entry, timer, summary |
billing.spec.ts |
BIL-001–020 | P1/P2 | Trial status, upgrade flow, customer portal |
| File | Test IDs | Priority | Description |
|---|---|---|---|
administration.spec.ts |
ADM-001–014 | P2 | System admin, UDC, artefact types |
| File | Test IDs | Priority | Description |
|---|---|---|---|
smoke.spec.ts |
— | P0 | Landing page, basic navigation |
p0-critical-path.spec.ts |
— | P0 | Core MVP critical path |
p0-real-user.spec.ts |
— | P0 | Real user journey simulation |
cross-cutting.spec.ts |
NAV-001–005, PRF-002–004, ERR-001–005, A11-001–004, RSP-001–004 | P0–P3 | Navigation, performance, errors, accessibility, responsive |
integration.spec.ts |
JRN-002–004, INT-001–012 | P0/P1 | Full journeys, document-task integration, OnlyOffice |
header-banner-stability.spec.ts |
— | P1 | Header banner stability (#910) |
pbs-drag-drop.spec.ts |
— | P1 | PBS drag-drop operations |
base.page.ts)Common methods inherited by all pages:
waitForPageLoad() — waits for network idle + loading spinner to hideexpectSuccessToast() — waits for success notificationloadingSpinner, toastSuccess, toastErrordashboard.page.ts)Main navigation hub:
openFirstCompany() — navigate to first companyopenFirstProject() — navigate to first project (creates one if needed)createProject(name) — create project with name, status, datesclickAddCompany() — click Add Company buttongetCompanyNames() / getProjectNames() — read entity names from cardscompanyExists(name) / projectExists(name) — check card visibilityopenCompanyByName(name) — open specific company cardcompany-details.page.ts)Company edit form (signup-screen component):
fillCompanyName(name), fillContactName(name), fillAddress(line1)getCompanyName(), getContactName(), getAddress()save(), delete(), confirmDelete(), goBack()input#company_name, input#contact_name, input#address_line1project-details.page.ts)Project edit form (project-details component):
fillProjectName(name), selectStatus(status), fillDates()selectOwner(index), selectProjectType(index), selectConfidentiality(value)getProjectName(), save(), delete(), confirmDelete(), goBack()input#project_name, select#status, input#start, input#endproject-content.page.ts)File browser / document grid:
expectContentPageVisible(), getDocumentCount(), isEmpty()clickAddArtefact(), clickBulkUpload(), clickAddTemplates()openDocument(name) — double-click row (opens OnlyOffice editor)editDocument(name) — click pencil icon (navigates to artefact-details form)deleteDocument(name), documentExists(name)goToPlanner() — navigate to Gantt viewswitchTab('folders' | 'stage-workstream' | 'pbs')artefact-details.page.ts)Document/artefact detail form:
fillArtefactName(name), fillMandatoryFields(), fillDescription()selectStage(), selectWorkstream(), selectType(), selectStatus()uploadFile(path), createArtefact(name), createArtefactWithFile(name, path)deleteArtefact(), save(), goBack()input[name="artefact_name"], ng-select[name="artefact_type"]gantt.page.ts)DHTMLX Gantt chart:
waitForGanttLoad(), waitForGanttStable()createTaskViaAPI(name, opts), deleteTaskViaAPI(name)indentTaskViaAPI(name), outdentTaskViaAPI(name)getTaskByName(name), getTaskNames(), getTaskCount()expandTask(name), collapseTask(name), isTaskExpanded(name)getWBSCodes(), cleanupE2ETestTasks()selectTask(name), changeFormat(format)timesheet.page.ts)Timesheet operations and time entry.
project-members.page.ts)Project member management screen.
onboarding.page.ts)Onboarding wizard flow.
login.page.ts)Landing page and authentication:
goto(), openLoginModal(), showEmailLoginForm()loginButton, emailInput, passwordInput| Tag | Meaning | When to run |
|---|---|---|
@p0 |
Critical path | Every CI run |
@p1 |
Core functionality | Every merge |
@p2 |
Extended features | Nightly |
@p3 |
Nice-to-have | Weekly |
@visual |
Screenshot baselines | On demand / pre-release |
@smoke |
Smoke tests | Every deployment |
Run by tag: npx playwright test --grep "@p0"
DHTMLX Gantt Zone Issue: Event handlers run outside Angular's zone in production builds. Angular modals don't render when triggered via Playwright. Tests use Gantt API instead of UI modals.
ag-Grid Context Menu Zone Issue: ag-Grid context menu callbacks also run outside Angular's NgZone. Confirmation modals triggered from context menu actions (e.g., delete artefact) render in the DOM but Angular event bindings don't fire. Workaround: use direct API calls with XSRF token extracted from cookies (see ART-004).
Project List View Modes: The project list switches between card view (.prism-project-card) and table view (.prism-table-wrapper) based on project count. Tests handle both views via fallback selectors.
Pagination: Content views require pagination for >100 records. Gantt auto-cleanup triggers at >450 tasks.
Visual Baselines: Date-dependent elements (today marker, timeline headers) are masked to prevent daily drift.
OnlyOffice: Editor integration tests require OnlyOffice Document Server to be running. Frame-based interaction may be flaky.
All production test accounts share the password: TestPassword123
| ID | Name | Company | |
|---|---|---|---|
| 166 | e2e-test@rapidpm.uk |
E2E Test User | E2E Test Company (182) |
| 167 | volumetest@velastra.co.uk |
Volume Test | TEST COMPANY (7) |
| 168 | testuser-a@rapidpm.uk |
Security Test A | E2E Test Company (182) |
| 169 | testuser-b@rapidpm.uk |
Security Test B | DEMONSTRATION COMPANY (129) |
| 171 | stress-test@rapidpm.uk |
Stress Test User | Stress Test Company (189) |
| Resource | ID | Name |
|---|---|---|
| Company | 189 | Stress Test Company |
| Project | 374 | Stress Test Project |
| User | 171 | stress-test@rapidpm.uk (ADMIN) |
| Password | Env File | |
|---|---|---|
test2@velastra.co.uk |
E2eTest2024! |
environments/local.env |
ssh rpm-prod-new "docker exec rapid-pm-app-green-1 python3 -c \"
import bcrypt
from app import create_app, db
from sqlalchemy import text
app = create_app()
with app.app_context():
pw = 'TestPassword123'
hashed = bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
db.session.execute(text('UPDATE user SET password = :pw WHERE email = :e'), {'pw': hashed, 'e': 'stress-test@rapidpm.uk'})
db.session.commit()
\""
Stress tests simulate multiple concurrent users performing document operations against production.
E2E_TEST_EMAIL=stress-test@rapidpm.uk E2E_TEST_PASSWORD=TestPassword123 \
npx playwright test --config=e2e/stress/playwright.config.ts --project=stress
Configuration:
STRESS_NUM_USERS — concurrent users (default: 5)STRESS_PROJECT_ID — target project (default: 374)STRESS_USER_ID — target user (default: 171)Latest results (2026-04-01, 5 users, CX23 Hetzner):
| Test | Result | Time |
|---|---|---|
| 4.1 — Create 3 docs each | PASSED | 9.0m |
| 4.2 — Edit different docs | FAILED | 9.7m |
| 4.3 — Edit/version/restore | PASSED | 9.9m |
| 4.4 — Co-editing pairs | PASSED | 7.3m |
| 4.5 — Rapid-fire lifecycle | PASSED | 8.0m |
| 4.6 — Sustained editing | FAILED | ~10m |
Key findings: Server handles 5 concurrent users reliably. OnlyOffice ops take 15-30s each. 10 concurrent users overwhelms the CX23 (4GB). Co-editing works with no data corruption.
e2e/
├── fixtures/
│ └── test-upload.txt # Test file for upload tests
├── helpers/
│ └── navigation.ts # Navigation utilities
├── pages/
│ ├── base.page.ts # BasePage (common methods)
│ ├── login.page.ts # LoginPage
│ ├── dashboard.page.ts # DashboardPage
│ ├── gantt.page.ts # GanttPage
│ ├── project-content.page.ts # ProjectContentPage
│ ├── artefact-details.page.ts # ArtefactDetailsPage
│ ├── company-details.page.ts # CompanyDetailsPage
│ ├── project-details.page.ts # ProjectDetailsPage
│ ├── onboarding.page.ts # OnboardingPage
│ ├── timesheet.page.ts # TimesheetPage
│ ├── project-members.page.ts # ProjectMembersPage
│ └── index.ts # Barrel exports
└── specs/
├── setup-test-account.spec.ts # Account setup
├── smoke.spec.ts # Smoke tests
├── p0-critical-path.spec.ts # P0 critical path
├── p0-real-user.spec.ts # P0 real user journey
├── user-registration.spec.ts # Registration
├── auth-extended.spec.ts # Auth extended
├── onboarding-extended.spec.ts # Onboarding
├── company-management.spec.ts # Company CRUD
├── project-management.spec.ts # Project CRUD
├── artefact-crud.spec.ts # Artefact CRUD
├── content-grid.spec.ts # Content grid
├── content-advanced.spec.ts # Content advanced
├── document-upload.spec.ts # Document upload
├── document-lifecycle.spec.ts # Document lifecycle
├── gantt-crud.spec.ts # Gantt task CRUD
├── gantt-operations.spec.ts # Gantt operations
├── gantt-views.spec.ts # Gantt view switching
├── gantt-baselines.spec.ts # Gantt baselines
├── gantt-dependencies.spec.ts # Gantt dependencies
├── gantt-dragdrop.spec.ts # Gantt drag-drop
├── gantt-advanced.spec.ts # Gantt advanced
├── gantt-performance.spec.ts # Gantt performance
├── gantt-robustness.spec.ts # Gantt robustness
├── gantt-visual.spec.ts # Gantt visual regression
├── user-management.spec.ts # User management
├── project-members.spec.ts # Project members
├── timesheet.spec.ts # Timesheet
├── billing.spec.ts # Billing
├── administration.spec.ts # System admin
├── cross-cutting.spec.ts # Cross-cutting concerns
├── integration.spec.ts # Integration tests
├── header-banner-stability.spec.ts # Header stability
└── pbs-drag-drop.spec.ts # PBS drag-drop