PlaywrightPomGenerator 1.5.0

dotnet tool install --global PlaywrightPomGenerator --version 1.5.0
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local PlaywrightPomGenerator --version 1.5.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=PlaywrightPomGenerator&version=1.5.0
                    
nuke :add-package PlaywrightPomGenerator --version 1.5.0
                    

Playwright POM Generator

NuGet Version .NET Version License

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

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 ./e2e in 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 git CLI

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-testid attributes (highest priority)
  • id attributes
  • Role-based selectors (buttons with text)
  • formControlName attributes
  • Click handlers (click)="method()"
  • Router links (routerLink, href)
  • Angular Material components:
    • mat-button, mat-raised-button, mat-flat-button, mat-stroked-button
    • mat-icon-button, mat-fab, mat-mini-fab
    • mat-form-field with mat-label
    • mat-table with rows, headers, and cells
  • Tables (<table>, mat-table)
  • Input elements with type attribute
  • 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 assertion
  • expect{Element}HasText(expected) - Exact text assertion
  • expect{Element}ContainsText(expected) - Partial text assertion
  • get{Element}Text() - Text content retrieval

Requirements

Runtime

  • .NET 8.0 SDK or later
  • Angular application, library, or workspace
  • git CLI (required for ppg remote command)

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
1.5.0 98 2/9/2026
1.4.0 96 2/5/2026
1.3.2 89 2/4/2026
1.3.1 94 2/4/2026
1.3.0 92 2/4/2026
1.2.0 95 2/4/2026
1.1.0 89 2/4/2026
1.0.0 93 2/4/2026