Playwright, TypeScript, Qase TestOps, service layers, fixtures, parallel account provisioning, and external integration patterns.
This repository is based on a Playwright framework I designed for a real product. Application-specific selectors, URLs, credentials, and business data were replaced before publication. The project is intended to demonstrate framework architecture and integration patterns rather than provide a runnable test suite.
This started as the automation framework for a SaaS product's UI regression suite, with API helpers for setup, teardown, and test-data preparation, spanning customer communication, account and user management, billing, workflow automation, and analytics. It's published here to show how the framework itself is put together: page objects, a service layer, fixtures, parallel-safe test-account provisioning, and Qase TestOps reporting.
- Playwright and TypeScript framework design
- Page Object and fixture composition
- A service layer for API-assisted setup and email verification, kept separate from UI page objects
- Qase TestOps integration (test IDs, run metadata, attachments)
- API-assisted setup and teardown, instead of driving the UI for every precondition
- Parallel account provisioning and concurrency control
- External integration patterns (email, PDF parsing, HAR-based caching)
BasePage holds cross-page helpers (loaders, uploads, balance checks). SettingsPage and the other domain pages extend it. ApiRequests is a service class used for fast setup/teardown via the product's REST API instead of driving the UI for every precondition. EmailService verifies transactional email via IMAP. fixtures.ts wires page objects and services together per test scenario (start, openAuthPage, annuallyUpdateAccountSignIn, etc.).
Two Playwright configs exist because the suite has two phases:
playwright.no-setup.config.tsrunse2e/createAccounts.spec.ts(tagged@START), which provisions one disposable test account per parallel worker and writes them to a shared JSON file, guarded by anasync-mutexlock so concurrent workers don't corrupt the file.playwright.config.tsruns the tagged Qase test cases against those already-provisioned accounts, with aglobalSetupthat logs in once and records a HAR of static assets so every worker skips re-downloading them.
See ARCHITECTURE.md for the full breakdown.
e2e/ Test specs, one file per feature area
pageObjects/ Page Object classes (BasePage + domain pages)
services/ API and email service classes
fixtures/ Playwright fixture composition
constants/ Static test data (emails, receipts, user data)
types/ Shared TypeScript types
utils/ Custom Playwright command wrappers, API request helper
misc/ Global setup (HAR cache warmer)
variables/ Runtime state written by setup tests (gitignored)
npm run test:setupprovisions N test accounts in parallel (@STARTtests), one per worker, saved tovariables/defaultUsers.json.npm testruns the main suite:globalSetupwarms a HAR cache, then each spec picks up its worker's pre-provisioned account viatestInfo.parallelIndexand runs against it.- Each spec composes the fixtures it needs (a page object, a service, or both) rather than depending on a single global context.
Both configs register playwright-qase-reporter, and each test is wrapped with qase(id, title) to attach a Qase test-case ID. The reporter is configured with a project code, run title, and attachment upload, but actually publishing results requires a real Qase TestOps workspace and API token — none are included here, and QASE_MODE defaults to off in .env.example so the framework doesn't attempt to report anywhere by default.
- Email (IMAP) —
EmailServiceconnects to a Gmail inbox to verify signup/invite/export emails, including following links inside the email body. - PDF parsing —
BillingPage.downloadLastReceipt()downloads a receipt and extracts its text withpdf-parsefor assertions. - HAR-based caching —
globalSetuprecords a HAR of static assets once and replays it (page.routeFromHAR) across the suite to cut redundant network time. - Concurrency control —
async-mutexserializes writes to the shared account file during parallel account setup.
See .env.example for the full list with comments. Broadly: application URL/env, basic-auth credentials, a disposable "old account" used as a stable message recipient, Qase TestOps settings, and Gmail IMAP credentials for email verification.
| Command | What it does |
|---|---|
npm run lint |
ESLint |
npm run format / format:check |
Prettier |
npm run typecheck |
TypeScript, no emit |
npm run test:setup |
Provision test accounts (@START, no-setup config) |
npm test |
Full suite against provisioned accounts |
npm run test:headed |
Full suite, headed |
npm run test:debug |
Full suite, Playwright inspector |
npm run test:qase |
Full suite with Qase reporting mode on |
The target application isn't public, selectors and URLs were replaced before publication, and there's no test account, Qase workspace, or mailbox behind this repo. Running npx playwright test here will fail at the first navigation. Use npx playwright test --list to see the test inventory without needing any of that.
Selectors, endpoints, credentials, and business-specific text were replaced with neutral placeholders before this was published. .env, variables/, and cache/ are gitignored and have never been tracked. No real secrets, tokens, credentials, or account data are present in the current published version of the repository.
Stanislav Mokshyn — github.com/testomut