PlaywrightPomGenerator 1.5.0
dotnet tool install --global PlaywrightPomGenerator --version 1.5.0
dotnet new tool-manifest
dotnet tool install --local PlaywrightPomGenerator --version 1.5.0
#tool dotnet:?package=PlaywrightPomGenerator&version=1.5.0
nuke :add-package PlaywrightPomGenerator --version 1.5.0
Playwright POM Generator
A powerful .NET CLI tool that automatically generates Playwright Page Object Model (POM) tests for Angular applications. Transform your Angular components into maintainable, type-safe Playwright test infrastructure with a single command.
Features
- Automatic Angular Analysis - Intelligently scans Angular workspaces, applications, and libraries to detect components, templates, and routing
- BasePage Pattern - All generated page objects extend from a common BasePage class with shared functionality
- Page Object Generation - Creates type-safe TypeScript page objects with properly typed locators and methods
- Inline Template Support - Parses both external HTML templates and inline templates in component files
- Angular Material Support - Detects and creates selectors for mat-button, mat-table, mat-form-field, and other Material components
- Click Handler Detection - Automatically generates click methods for elements with
(click)event bindings - Table Accessor Methods - Generates comprehensive table methods (getRows, getHeaders, getRowCount, clickRow, etc.)
- Test Scaffolding - Generates complete Playwright test specs with fixture integration
- SignalR Mock Support - Generates RxJS-based SignalR mock fixtures for real-time application testing
- Workspace Support - Handle complex Angular workspaces with multiple applications and libraries
- Remote Repository Support - Generate tests directly from a git URL (GitHub, GitLab, Bitbucket, Azure DevOps, or any git host) without manually cloning
- Highly Configurable - Customize output via configuration files, environment variables, or command-line options
Installation
Global Tool Installation (Recommended)
dotnet tool install -g PlaywrightPomGenerator
Update Existing Installation
dotnet tool update -g PlaywrightPomGenerator
The tool command is ppg.
Quick Start
1. Generate Tests for Your Angular App
# For a single application
ppg app ./my-angular-app -o ./e2e
# For an Angular workspace
ppg workspace . -o ./e2e-tests
# For an Angular library
ppg lib ./projects/my-lib -o ./e2e
# From a remote git URL (GitHub, GitLab, Bitbucket, Azure DevOps, etc.)
ppg remote https://github.com/owner/repo/blob/main/src/app/my-component/my-component.ts
2. Install Playwright and Run Tests
cd ./e2e
npm install
npx playwright install
npx playwright test
Commands
app - Generate for Single Application
ppg app <path> [options]
# Examples
ppg app ./src/my-app
ppg app ./src/my-app -o ./e2e-tests
ppg app . --test-suffix "test"
workspace - Generate for Angular Workspace
ppg workspace <path> [options]
# Examples
ppg workspace .
ppg workspace . -p my-app
ppg workspace . -o ./tests
Options:
-o, --output <dir>- Output directory-p, --project <name>- Generate for specific project only
lib - Generate for Angular Library
ppg lib <path> [options]
# Examples
ppg lib ./projects/my-lib
ppg lib ./projects/components -o ./e2e
artifacts - Generate Specific Artifacts
ppg artifacts <path> [options]
# Examples
ppg artifacts . --all
ppg artifacts . --fixtures --configs
ppg artifacts . --page-objects --selectors
Options:
-f, --fixtures- Generate test fixtures-c, --configs- Generate Playwright configuration-s, --selectors- Generate selector files--page-objects- Generate page object files--helpers- Generate helper utilities-a, --all- Generate all artifacts
remote - Generate from Remote Git URL
Generate tests directly from a remote git repository URL. The tool clones the repository to a temporary directory, analyzes the Angular components at the specified path, generates tests, and cleans up automatically. Requires git to be installed and available on the system PATH.
ppg remote <url> [options]
# Examples
# GitHub - component file
ppg remote https://github.com/owner/repo/blob/main/src/app/my-component/my-component.ts
# GitHub - component folder
ppg remote https://github.com/owner/repo/tree/main/src/app/components -o ./e2e
# GitLab (including self-hosted)
ppg remote https://gitlab.com/owner/repo/-/blob/develop/src/app/table/table.ts
ppg remote https://gitscm.company.com/team/repo/-/blob/main/src/components/form
# Bitbucket
ppg remote https://bitbucket.org/owner/repo/src/main/src/app/dashboard
# Azure DevOps
ppg remote "https://dev.azure.com/org/project/_git/repo?path=/src/app/login&version=GBmain"
# Commit hash instead of branch
ppg remote https://github.com/owner/repo/blob/a1b2c3d4e5f6/src/app/my-component
Options:
-o, --output <dir>- Output directory (defaults to./e2ein the current working directory)
Supported URL formats:
| Provider | URL Pattern |
|---|---|
| GitHub | https://github.com/{owner}/{repo}/blob/{ref}/{path} |
| GitLab | https://gitlab.com/{owner}/{repo}/-/blob/{ref}/{path} |
| Self-hosted GitLab | https://gitscm.example.com/{owner}/{repo}/-/blob/{ref}/{path} |
| Bitbucket | https://bitbucket.org/{owner}/{repo}/src/{ref}/{path} |
| Azure DevOps | https://dev.azure.com/{org}/{project}/_git/{repo}?path={path}&version=GB{branch} |
| Generic | https://git.example.com/{owner}/{repo}.git |
Notes:
- The ref can be a branch name (e.g.,
main,develop) or a commit hash - When pointing to a file, tests are generated for that single component
- When pointing to a folder, all components in that folder (and subfolders) are scanned
- The tool automatically detects Angular workspaces, applications, and libraries within the cloned repo
- No third-party git libraries are used; only the
gitCLI
signalr-mock - Generate SignalR Mock
ppg signalr-mock <output>
# Examples
ppg signalr-mock ./fixtures
ppg signalr-mock ./e2e/mocks
Global Options
# Custom file header with placeholders
--header "// Copyright 2026\n// File: {FileName}\n// Generated: {GeneratedDate}"
# Custom test file suffix (default: "spec")
--test-suffix "test" # Creates *.test.ts instead of *.spec.ts
# Enable debug mode (includes HTML template as comments in page objects)
--debug
Generated Output
e2e/
├── playwright.config.ts # Playwright configuration
├── helpers.ts # Common utility functions
│
├── configs/ # Configuration files
│ ├── timeout.config.ts # Timeout constants
│ └── urls.config.ts # URL constants and API endpoints
│
├── fixtures/ # Test fixtures
│ └── fixtures.ts # Extended test with page object fixtures
│
├── page-objects/ # Page Object Model classes
│ ├── base.page.ts # Abstract base class for all page objects
│ ├── login.page.ts # Component page objects
│ ├── dashboard.page.ts
│ └── ...
│
├── helpers/ # Selector constants
│ ├── login.selectors.ts
│ ├── dashboard.selectors.ts
│ └── ...
│
└── tests/ # Test specification files
├── login.spec.ts
├── dashboard.spec.ts
└── ...
Generated Code Examples
Base Page Class
All page objects extend from BasePage, providing common functionality:
// page-objects/base.page.ts
import { Page, Locator } from '@playwright/test';
import { TIMEOUTS } from '../configs/timeout.config';
export abstract class BasePage {
constructor(protected page: Page) {}
abstract navigate(): Promise<void>;
abstract waitForLoad(): Promise<void>;
async getPageTitle(): Promise<string> {
return this.page.title();
}
protected async waitForNavigation(): Promise<void> {
await this.page.waitForLoadState('networkidle', { timeout: TIMEOUTS.navigation });
}
protected getLocator(selector: string): Locator {
return this.page.locator(selector);
}
protected async waitForVisible(selector: string, timeout = TIMEOUTS.elementVisible): Promise<void> {
await this.getLocator(selector).waitFor({ state: 'visible', timeout });
}
protected async waitForHidden(selector: string, timeout = TIMEOUTS.elementHidden): Promise<void> {
await this.getLocator(selector).waitFor({ state: 'hidden', timeout });
}
// ... additional utility methods
}
Generated Page Object
// page-objects/login.page.ts
import { Locator, expect } from '@playwright/test';
import { BasePage } from './base.page';
export class LoginPage extends BasePage {
private readonly componentSelector = 'app-login';
readonly usernameInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;
constructor(page: import('@playwright/test').Page) {
super(page);
this.usernameInput = page.locator('[formControlName="username"]');
this.passwordInput = page.locator('[formControlName="password"]');
this.submitButton = page.getByRole('button', { name: 'Login' });
}
async navigate(): Promise<void> {
await this.page.goto('/login');
await this.waitForLoad();
}
async fillUsernameInput(value: string): Promise<void> {
await this.usernameInput.fill(value);
}
async fillPasswordInput(value: string): Promise<void> {
await this.passwordInput.fill(value);
}
async clickSubmitButton(): Promise<void> {
await this.submitButton.click();
}
async expectSubmitButtonVisible(): Promise<void> {
await expect(this.submitButton).toBeVisible();
}
async waitForLoad(): Promise<void> {
await this.page.waitForSelector(this.componentSelector);
}
}
Generated Table Page Object
For components with tables, comprehensive accessor methods are generated:
// page-objects/data-table.page.ts
export class DataTablePage extends BasePage {
readonly dataTable: Locator;
async expectDataTableVisible(): Promise<void> {
await expect(this.dataTable).toBeVisible();
}
getDataTableRows(): Locator {
return this.dataTable.locator('mat-row, tr[mat-row], [mat-row]');
}
getDataTableRow(index: number): Locator {
return this.getDataTableRows().nth(index);
}
getDataTableHeaders(): Locator {
return this.dataTable.locator('mat-header-cell, th[mat-header-cell], [mat-header-cell]');
}
async getDataTableRowCount(): Promise<number> {
return this.getDataTableRows().count();
}
async clickDataTableRow(index: number): Promise<void> {
await this.getDataTableRow(index).click();
}
}
Using Generated Code in Tests
// tests/login.spec.ts
import { test, expect } from '../fixtures/fixtures';
test.describe('Login', () => {
test('should display the login form', async ({ loginPage }) => {
await loginPage.navigate();
await loginPage.expectSubmitButtonVisible();
});
test('should login successfully', async ({ loginPage, dashboardPage }) => {
await loginPage.navigate();
await loginPage.fillUsernameInput('testuser');
await loginPage.fillPasswordInput('password123');
await loginPage.clickSubmitButton();
await dashboardPage.waitForLoad();
await expect(dashboardPage.page).toHaveURL(/dashboard/);
});
});
Configuration
Configuration File (appsettings.json)
{
"Generator": {
"FileHeader": "/**\n * @generated {GeneratedDate}\n * @version {ToolVersion}\n */",
"TestFileSuffix": "spec",
"ToolVersion": "1.3.0",
"OutputDirectoryName": "e2e",
"GenerateJsDocComments": true,
"DefaultTimeout": 30000,
"BaseUrlPlaceholder": "http://localhost:4200"
}
}
Environment Variables
Use the POMGEN_ prefix:
export POMGEN_Generator__TestFileSuffix="test"
export POMGEN_Generator__DefaultTimeout="60000"
export POMGEN_Generator__BaseUrlPlaceholder="http://localhost:3000"
Selector Detection
The tool automatically detects and creates selectors for:
data-testidattributes (highest priority)idattributes- Role-based selectors (buttons with text)
formControlNameattributes- Click handlers
(click)="method()" - Router links (
routerLink,href) - Angular Material components:
mat-button,mat-raised-button,mat-flat-button,mat-stroked-buttonmat-icon-button,mat-fab,mat-mini-fabmat-form-fieldwithmat-labelmat-tablewith rows, headers, and cells
- Tables (
<table>,mat-table) - Input elements with
typeattribute - Custom elements with text content
- Elements with Angular interpolation (
{{ property }}) - Elements with ng-content projection (
<ng-content></ng-content>)
Dynamic Content Detection
The generator detects elements that will render dynamic content:
<h1>{{ title }}</h1>
<p>{{ description }}</p>
<div>{{ content }}</div>
<div><ng-content></ng-content></div>
<span><ng-content select="[header]"></ng-content></span>
For these elements, the generated page object includes:
expect{Element}Visible()- Visibility assertionexpect{Element}HasText(expected)- Exact text assertionexpect{Element}ContainsText(expected)- Partial text assertionget{Element}Text()- Text content retrieval
Requirements
Runtime
- .NET 8.0 SDK or later
- Angular application, library, or workspace
gitCLI (required forppg remotecommand)
For Generated Tests
- Node.js 16+ and npm
- @playwright/test package
Supported Platforms
- Windows 10/11
- macOS 11+
- Linux (Ubuntu 20.04+)
Tested With
- Angular 14, 15, 16, 17, 18
- Playwright 1.40+
- TypeScript 5.0+
Building from Source
# Clone the repository
git clone <repository-url>
cd PageObjectModel
# Build
dotnet build -c Release
# Run tests
dotnet test
# Create NuGet package
dotnet pack src/PlaywrightPomGenerator.Cli -c Release
License
This project is licensed under the MIT License - see the LICENSE file for details.
Made with care for the Angular and Playwright communities
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.