histrion
v4.6.0
Published
Scaffold a production-grade Playwright + TypeScript project with Page Object Model architecture
Maintainers
Readme
What you get
Run one command. Answer a few questions. Get a fully configured project with:
- Page Object Model architecture — layered separation of concerns
- Reusable components — Table, Modal, Form, Toast out of the box
- Type-safe fixtures — Page Objects injected into tests automatically
- Fluent data builders — chainable test data construction with optional Faker.js for realistic random data
- API helpers — setup/teardown without touching the UI
- Visual regression — screenshot comparison with smart masking
- Custom HTML reporter — dark-mode, filterable, tag-aware, auto-open on failure
- Structured logger — every action traced with color-coded timestamps, fully customizable theme
- Custom expect matchers — domain-specific assertions
- Biome — linting + formatting in one tool, zero config
- GitHub Actions — CI/CD with matrix strategy, manual dispatch
- Starter templates — Page Object, fixture & test boilerplate ready to fill in
- AI instructions — optional rules for Copilot, Claude Code, Cursor, or Windsurf so your AI assistant follows the architecture
- Page scanner — analyze any live page and generate a Page Object automatically, with
--headedand--authsupport for authenticated pages - Element extractor — copy any HTML element from DevTools, get every Playwright locator ranked by stability
- 20 documentation guides — from getting started to best practices, including a glossary
Quick start
npx histrion create # interactive — prompts for project name
npx histrion create my-e2e-tests # create in a new folder
npx histrion create . # scaffold in current directoryThe CLI scaffolds the project, installs dependencies, downloads Playwright browsers, runs a lint check, and initializes git. You walk away with a ready-to-use test suite.
⚡ histrion create — scaffold a production-grade Playwright project
- Project name: my-e2e-tests
- Application base URL: https://your-app.com
- Include starter template files? Yes
- Install Faker.js? Yes
- Include visual regression tests? Yes
- Include API helpers for setup/teardown? Yes
- Include GitHub Actions CI/CD? Yes
- AI coding assistant instructions? GitHub Copilot, Claude Code
✓ Scaffolded 38 files
✓ Dependencies installed
✓ Playwright browsers installed
✓ All files pass lint & format
✓ Git repository initialized with first commit
✓ Project ready!
Get started:
cd my-e2e-tests
code .Page scanner
Don't write Page Objects from scratch. Point the scanner at any page and get a ready-to-use file:
npx histrion scan https://myapp.com/login 🔍 histrion scan — analyze page & generate Page Object
✓ Page loaded — "Login"
✓ Found 5 interactive elements
Scan results:
Element Strategy
────────────────────────────────────
emailInput getByTestId
passwordInput getByTestId
loginButton getByTestId
forgotPasswordLink getByRole
navLogoLink locator(css)
✓ Page Object generated
File: src/pages/login.page.ts
Stable locators: 3/5 (60%)The scanner prioritizes stable, language-independent locators (data-testid, id) over locale-dependent ones (getByRole, getByLabel). Works on any website — no Histrion project required.
Element extractor
See an element in DevTools? Copy it and get every possible Playwright locator ranked by stability:
- Right-click an element in Chrome DevTools > Copy > Copy element
- Run:
npx histrion extract 🔍 Analyzing element...
Root: <div> with text "You logged into a secure area!"
┌─────────────────────────────────────────────────────────────────────────────┐
│ # Strategy Locator Stability│
├─────────────────────────────────────────────────────────────────────────────┤
│ 1 locator#id locator("#flash") 🟢 │
│ 2 getByRole getByRole("alert", { name: "You logged in..." }) 🟡 │
│ 3 getByText getByText("You logged into a secure area!") 🔴 │
└─────────────────────────────────────────────────────────────────────────────┘
Select locator [1]: _
Copy-paste ready:
private readonly flash = this.page.locator("#flash");The extractor reads your clipboard automatically (macOS, Linux, Windows). It also analyzes child elements — if you copy a parent <div>, any child <button> or <input> with meaningful attributes gets its own locator table. Pick any locator by number and get a copy-paste ready private readonly declaration.
Also accepts inline HTML: npx histrion extract '<button id="ok">OK</button>'
Scanning authenticated pages
Need to scan a page behind login? Two options:
# Open a visible browser — log in manually, press Enter to scan
npx histrion scan https://myapp.com/settings --headed
# Use a saved auth state (cookies + localStorage)
npx histrion scan https://myapp.com/settings --auth auth/admin.json
# Both — pre-load auth, but open headed for manual navigation
npx histrion scan https://myapp.com/settings --headed --auth auth/admin.jsonThe golden rule
Tests never contain selectors. They read like specifications.
test('user can log in', async ({ loginPage }) => {
await loginPage.navigate();
await loginPage.fillCredentials({ username: '[email protected]', password: 's3cret' });
await loginPage.submit();
await loginPage.expectDashboard();
});If you see a page.click() or a data-testid in a test file, something went wrong.
Built-in structured logger
Every Page Object, Component, and API action is traced automatically:
14:32:01 ■ LoginPage │ 🔹 Filling credentials for [email protected]
14:32:01 ■ LoginPage │ ▸ Fill "username" with "[email protected]"
14:32:02 ■ LoginPage │ ▸ Fill "password" with "***"
14:32:02 ■ LoginPage │ 🔹 Submitting login form
14:32:03 ■ LoginPage │ ✓ Dashboard visibleFive log levels (step, action, success, warn, error) with customizable colors, icons, and formatting. Add your own levels in one file — see src/utils/logger.ts.
Architecture
src/
├── core/ Abstract base classes (BasePage, BaseComponent, BaseAPI)
├── components/ Reusable UI components (Table, Modal, Form, Toast)
├── pages/ Page Objects — one per page of your app
├── fixtures/ Type-safe dependency injection into tests
├── api/ HTTP clients for test setup/teardown
├── data/
│ ├── builders/ Fluent test data builders (with Faker.js)
│ └── types/ Domain types
├── config/ Environment & user configuration
├── reporters/ Custom HTML reporter
└── utils/ Logger, visual helpers, custom matchers
tests/
├── e2e/ End-to-end test specs
└── visual/ Visual regression specs
docs/ Local documentation (20 guides, gitignored)Dependencies flow down only. Tests → Fixtures → Pages → Components → Core → Config.
Starter templates
The scaffolded project includes ready-to-use boilerplate:
- Page Object —
src/pages/example.page.tswith locators, actions, and assertions sections - Test file —
tests/e2e/example.spec.tswith describe block, beforeEach, and test structure - Type definition —
src/data/types/example.tsfor your page's data shape - Fixture wiring —
src/fixtures/index.tspre-configured
Open example.page.ts and follow docs/15-writing-your-first-test.md to adapt them to your app.
AI coding assistant instructions
During scaffolding, you can generate instruction files for your AI coding assistant. These encode the full POM architecture rules so the AI follows your conventions — private locators, fixtures, logger usage, naming patterns, and more.
Supported tools:
| Tool | Generated file |
|------|---------------|
| GitHub Copilot | .github/copilot-instructions.md |
| Claude Code | CLAUDE.md |
| Cursor | .cursorrules |
| Windsurf | .windsurfrules |
Select one or more during histrion create. The content is identical — same rules, different filenames.
Adding a new page — 3 steps
1. Create the Page Object (src/pages/settings.page.ts):
export class SettingsPage extends BasePage {
readonly path = '/settings';
readonly pageTitle = /Settings/;
private readonly nameInput = this.page.getByTestId('settings-name');
private readonly saveButton = this.page.getByTestId('settings-save');
async updateName(name: string): Promise<void> {
await this.fill(this.nameInput, name, 'name');
await this.click(this.saveButton, 'Save');
}
}2. Register the fixture (src/fixtures/index.ts):
settingsPage: async ({ page }, use) => {
await use(new SettingsPage(page));
},3. Use in tests:
test('can update name', async ({ settingsPage }) => {
await settingsPage.navigate();
await settingsPage.updateName('New Name');
});Available scripts
| Script | Description |
|--------|-------------|
| npm test | Run all tests |
| npm run test:smoke | Run @smoke tagged tests |
| npm run test:regression | Run @regression tagged tests |
| npm run test:visual | Visual regression tests |
| npm run test:ui | Open Playwright UI mode |
| npm run test:debug | Step-by-step debugger |
| npm run test:staging | Run against staging env |
| npm run lint | Check with Biome |
| npm run lint:fix | Auto-fix lint issues |
| npm run codegen | Playwright code generator |
Environment management
TEST_ENV=staging npm test # staging environment
TEST_ENV=dev npm test # dev environment
BASE_URL=https://custom.url npm test # override URLEnvironments are defined in src/config/env.config.ts with per-env timeouts, retries, workers, and headless mode.
Documentation
The scaffolded project includes 20 local documentation guides in docs/ (gitignored). They cover everything from architecture to best practices, written for developers new to the framework.
Open docs/00-index.md in VS Code or Obsidian to browse them.
What gets installed
| Package | Purpose |
|---------|---------|
| @playwright/test | E2E testing framework — browser automation, assertions, test runner |
| typescript | Type safety across Page Objects, builders, and fixtures |
| @biomejs/biome | Linting + formatting in one tool — replaces ESLint and Prettier |
| dotenv | Loads environment variables from .env for local configuration |
| @faker-js/faker | (optional) Generates realistic random test data in builders |
Requirements
- Node.js 18+
- npm 8+
License
MIT
