pom-generator 1.2.0
📦 PyPI: https://pypi.org/project/pom-generator/
🌐 GitHub: https://github.com/khaledlim/pom-generator
pom-generator is a Python/Playwright engine for generating and maintaining YAML Page Object Models (POMs) from web applications.
It provides UI scanning, page and element models, robust locator generation, YAML read/write, validation, diff, update, statistics, export, and a command-line interface.
pom-generator is framework-neutral from a test-automation point of view: it has no dependency on a test-automation framework and exposes only Python/Playwright generation and maintenance capabilities.
Authors: Khaled Limem / Olivier RENAULT
Purpose
The primary objective of pom-generator is to build a reusable pom.yml describing an application's pages, elements, locators, and navigation.
A generated POM can then be consumed by any compatible Python tool or test-automation layer without duplicating page discovery or locator-selection logic.
The package owns the POM generation and maintenance logic:
- browser-driven page discovery with Playwright;
- DOM extraction and page scanning;
- Page/Element data models;
- locator candidate generation and robustness ranking;
- YAML persistence and incremental update;
- validation and comparison of POM files;
- statistics and export;
- optional Python authentication provider;
- reusable Python API;
- CLI orchestration.
Main features
POM generation
Three generation modes are available:
csv: scan a controlled list of pages from a CSV file;crawl: browse internal application links automatically and scan discovered pages;record: navigate manually and decide interactively which pages to capture.
All modes write to a single YAML POM file.
When an existing --pom-file is reused, pages already present are preserved and updated according to the current upsert rules instead of resetting the whole file.
Robust locators
Locator candidates are generated in semantic priority order:
- test attributes:
data-testid,data-test,data-qa,data-cy; id;- ARIA
role+ accessible name; aria-label;name;- stable
hreffor links; placeholder;title;- exact-text XPath;
- stable CSS class as a last-resort fallback.
When the live DOM provides match counts, candidates known to be unique are promoted ahead of candidates with unknown uniqueness. Known ambiguous candidates are kept only as late fallbacks, and candidates known to match no element are discarded.
Generated or styling-oriented CSS classes are deliberately deprioritized or rejected when they appear unstable.
POM maintenance
In addition to generation, the CLI supports:
validate: verify POM structure and data integrity;diff: compare two POM files and generate a unified diff;update: merge pages from one POM into another;statistics: compute POM metrics;export: export POM data to JSON or flattened CSV.
Requirements
- Python 3.9 or newer;
- PyYAML 6 or newer;
- Playwright 1.45 or newer.
The package itself has no dependency on a test-automation framework.
Installation
From a source checkout
python -m pip install .
python -m playwright install chromium
From PyPI
Once published on PyPI:
python -m pip install pom-generator
python -m playwright install chromium
Check the installation:
pom-generator --version
pom-generator --help
CLI overview
The general syntax is:
pom-generator --action <action> [options]
Available actions:
generate Generate or enrich a POM
validate Validate a POM
diff Compare two POMs
update Merge another POM into the target POM
statistics Compute statistics about a POM
export Export POM data
generate is the default action, so --action generate may be omitted.
Generate a POM
Record mode
Record mode opens the application in Playwright and lets the user navigate freely.
pom-generator \
--mode record \
--url "https://example.test/" \
--pom-file ./pom/example.yml \
--app-name example
Equivalent explicit form:
pom-generator \
--action generate \
--mode record \
--url "https://example.test/" \
--pom-file ./pom/example.yml \
--app-name example
The interactive dialog provides these actions:
Scan: capture the current page;Wait: temporarily close the dialog, return focus to the browser, wait, then show the dialog again;Skip: do not capture the current page;Quit: stop the session and save safely.
You can also stop execution with Ctrl+C; the current POM is saved before the browser is closed cleanly.
Crawl mode
Crawl mode discovers internal pages from the configured application and scans them automatically.
pom-generator \
--mode crawl \
--url "https://example.test/" \
--pom-file ./pom/example.yml \
--max-pages 30
Useful options:
--max-pages 30
--crawl-settle-timeout 3
--headless
--executable-path /path/to/chrome
--max-pages limits the number of pages visited during the crawl.
--crawl-settle-timeout controls how long the engine waits for page readiness before scanning elements.
CSV mode
CSV mode scans a predefined list of pages.
pom-generator \
--mode csv \
--url "https://example.test/" \
--csv ./pages.csv \
--pom-file ./pom/example.yml
CSV format
The CSV file uses a header followed by name,url rows:
name,url
login,/login
home,/home
customer,/customer
orders,/orders
Relative URLs are resolved against --url.
Interactive CSV mode
For applications that need manual intervention or stabilization between pages:
pom-generator \
--mode csv \
--url "https://example.test/" \
--csv ./pages.csv \
--pom-file ./pom/example.yml \
--csv-interactive
An automatic timeout can be added:
--csv-interactive-timeout 10
A delay between pages can also be configured:
--csv-transition-delay 2
Authentication provider
pom-generator can execute a Python login provider before POM generation.
Example:
pom-generator \
--mode record \
--url "https://example.test/" \
--login ./login.py \
--pom-file ./pom/example.yml
The provider receives these environment variables:
POM_LOGIN_URL
POM_STORAGE_STATE
POM_EXECUTABLE_PATH
The login script must write a Playwright storage-state JSON file to the path contained in POM_STORAGE_STATE.
Minimal example:
import os
from playwright.sync_api import sync_playwright
url = os.environ["POM_LOGIN_URL"]
storage_state = os.environ["POM_STORAGE_STATE"]
executable_path = os.environ.get("POM_EXECUTABLE_PATH") or None
with sync_playwright() as p:
launch_options = {"headless": False}
if executable_path:
launch_options["executable_path"] = executable_path
browser = p.chromium.launch(**launch_options)
context = browser.new_context()
page = context.new_page()
page.goto(url)
# Perform authentication here.
context.storage_state(path=storage_state)
browser.close()
The default login timeout is 300 seconds and can be changed with:
--login-timeout 600
Browser options
Headless mode
pom-generator \
--mode crawl \
--url "https://example.test/" \
--headless
Use a specific Chrome/Chromium executable
pom-generator \
--mode record \
--url "https://example.test/" \
--executable-path "/path/to/chrome"
Incognito mode
pom-generator \
--mode record \
--url "https://example.test/" \
--incognito
Validate a POM
Validate the POM structure and data integrity:
pom-generator \
--action validate \
--pom-file ./pom/example.yml
Validation checks include:
- required root sections;
- page integrity;
- page URLs and element collections;
- locator structure (
type,value); - navigation references to existing pages.
The default output format is text.
Generate JSON output:
pom-generator \
--action validate \
--pom-file ./pom/example.yml \
--validate-format json \
--validate-output ./reports/pom-validation.json
Compare two POMs
Generate a unified diff between two POM files:
pom-generator \
--action diff \
--pom-file ./pom/current.yml \
--against ./pom/reference.yml
By default, only a summary is printed in the terminal.
Display the full patch:
--print-diff
Specify the diff output file:
--diff-output ./reports/pom.diff
The diff uses standard unified-diff markers:
- removed content
+ added content
If there are no differences, no diff file is created.
For richer .diff visualization in VS Code, a diff-viewer extension can be used.
Update / merge a POM
Merge pages from another POM into the target POM:
pom-generator \
--action update \
--pom-file ./pom/current.yml \
--source-pom ./pom/incoming.yml
The update process merges the source pages and rebuilds navigation information.
Statistics
Compute POM metrics:
pom-generator \
--action statistics \
--pom-file ./pom/example.yml
Statistics include information such as:
- page count;
- element count;
- locator count;
- element types;
- top pages.
Supported formats:
text
json
html
Example HTML report:
pom-generator \
--action statistics \
--pom-file ./pom/example.yml \
--stats-format html \
--stats-output ./reports/pom-statistics.html
Example JSON report:
pom-generator \
--action statistics \
--pom-file ./pom/example.yml \
--stats-format json \
--stats-output ./reports/pom-statistics.json
If --stats-format is omitted, text statistics are printed directly to the console.
Export
Export the full POM as JSON:
pom-generator \
--action export \
--pom-file ./pom/example.yml \
--export-format json \
--export-output ./exports/example.json
Export a flattened element list as CSV:
pom-generator \
--action export \
--pom-file ./pom/example.yml \
--export-format csv \
--export-output ./exports/example-elements.csv
Supported export formats:
json
csv
POM structure
A generated POM contains four main sections:
application
runtime
pom
navigation
Simplified example:
application:
name: example
module: frontoffice
version: 1.2.3
context_path: /
base_url: https://example.test/
runtime:
environment: qa
browser: chrome
generated_by: pom-generator
generated_at: '2026-09-08T12:00:00'
generator_version: 1.2.0
pom:
format_version: '2.0'
description: POM for example
generated_at: '2026-09-08T12:00:00'
generation_notes:
- Generated automatically from UI scan (crawl/csv/record mode).
- Locator list is ordered by robustness.
- Review and enrich business-critical flows after baseline generation.
pages:
login:
name: login
url: https://example.test/login
title: Login
entry_type: page
elements:
username_input:
type: input
locators:
- type: css
value: '[data-testid="username"]'
submit_button:
type: button
locators:
- type: role
value: 'role=button[name="Sign in"]'
navigation:
- name: Login
level: 1
entry_type: menu_group
children:
- name: Login
level: 2
entry_type: page
url: https://example.test/login
page: login
Metadata such as application.module, application.version, application.context_path and runtime information are populated from the application/URL when available.
Locator strategy
Each element can contain several locators:
username_input:
type: input
locators:
- type: css
value: '[data-testid="username"]'
- type: id
value: 'id=username'
- type: css
value: '[name="username"]'
The list is ordered by robustness. Consumers should normally use the first compatible locator rather than selecting a strategy independently from the POM ranking.
The locator engine also:
- removes duplicates;
- escapes attribute values safely;
- generates safe XPath literals, including text containing quotes;
- avoids known generated CSS class patterns;
- promotes locators known to be unique in the current DOM;
- retains ambiguous candidates only as fallback when useful.
Incremental generation
Reusing the same --pom-file across several generation runs is supported.
Example:
pom-generator \
--mode csv \
--url "https://example.test/" \
--csv ./public-pages.csv \
--pom-file ./pom/application.yml
pom-generator \
--mode record \
--url "https://example.test/" \
--pom-file ./pom/application.yml
Existing POM sections are loaded before new pages are processed. Pages are updated according to URL/page identity rules, and navigation is rebuilt after relevant changes.
This makes it possible to build the same POM progressively using different generation modes.
Python API
The main engine components are exported directly by the package:
from POMGenerator import (
PomGenerator,
PageScanner,
LocatorBuilder,
YamlManager,
)
This allows Python applications to reuse the same generation rules as the CLI instead of implementing another POM format or locator strategy.
Example imports:
from pathlib import Path
from POMGenerator import LocatorBuilder, YamlManager
Build locator candidates directly:
raw_element = {
"tag": "button",
"data-testid": "save-button",
"role": "button",
"accessible_name": "Save",
}
locators = LocatorBuilder.build_priority_list(raw_element)
print(locators)
The exact construction of PomGenerator depends on the browser lifecycle and the generation flow being embedded. For standard usage, the CLI remains the simplest entry point.
Package layout
src/POMGenerator/
├── __init__.py
├── __main__.py
├── browser_manager.py
├── cli.py
├── dialogs.py
├── dom_extractor.py
├── generator.py
├── locator_builder.py
├── login_runner.py
├── page_scanner.py
├── pom_diff.py
├── pom_export.py
├── pom_statistics.py
├── pom_update.py
├── pom_validate.py
├── yaml_manager.py
├── models/
│ ├── element.py
│ └── page.py
└── modes/
├── crawl_mode.py
├── csv_mode.py
└── record_mode.py
Main responsibilities:
| Module | Responsibility |
|---|---|
browser_manager.py |
Playwright browser lifecycle and navigation |
dom_extractor.py |
Browser-side DOM extraction |
locator_builder.py |
Locator candidates, escaping and robustness ranking |
page_scanner.py |
Convert the current DOM into Page/Element models |
generator.py |
Generation orchestration |
yaml_manager.py |
YAML persistence, page upsert and navigation rebuild |
pom_validate.py |
POM structural validation |
pom_diff.py |
Unified comparison between POM files |
pom_update.py |
POM merge/update |
pom_statistics.py |
Metrics and text/JSON/HTML reports |
pom_export.py |
JSON and flattened CSV export |
login_runner.py |
Optional Python authentication provider |
modes/ |
CSV, crawl and record generation workflows |
models/ |
Page and Element data models |
cli.py |
Command-line entry point |
Development
Install the project in editable mode:
python -m pip install -e .
python -m playwright install chromium
Run the tests:
python -m unittest discover -s tests -p "test_*.py"
Build the package:
python -m build
The generated wheel and source archive are written to dist/.
Versioning and compatibility
Package version:
1.2.0
Current YAML POM format:
2.0
The POM format and public Python API should evolve compatibly within a minor release line whenever possible.
Any incompatible change to the YAML schema should be documented explicitly because generated POM files are intended to remain reusable independently from the generation session that created them.
Quick reference
Generate interactively:
pom-generator --mode record --url https://example.test --pom-file pom.yml
Crawl:
pom-generator --mode crawl --url https://example.test --pom-file pom.yml
Generate from CSV:
pom-generator --mode csv --url https://example.test --csv pages.csv --pom-file pom.yml
Validate:
pom-generator --action validate --pom-file pom.yml
Diff:
pom-generator --action diff --pom-file pom.yml --against baseline.yml --print-diff
Update:
pom-generator --action update --pom-file pom.yml --source-pom incoming.yml
Statistics:
pom-generator --action statistics --pom-file pom.yml --stats-format html
Export:
pom-generator --action export --pom-file pom.yml --export-format json
Release files for pom-generator 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pom_generator-1.2.0.tar.gz | 51.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pom_generator-1.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 103.9 kB
Release files / pom_generator-1.2.0.tar.gz
| Download URL | pom_generator-1.2.0.tar.gz |
|---|---|
| Size | 51.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
45c5e72d73759975ed2a840498382d694310a92e917e4d9916ed1ea290599937
|
|
BLAKE2b-256 checksum How to use checksums |
cf0904ca0eacc630738f96f8fb1e9a985f9368df9ba875bec84c48b46f6d9a4d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.9
|
Release files / pom_generator-1.2.0-py3-none-any.whl
| Download URL | pom_generator-1.2.0-py3-none-any.whl |
|---|---|
| Size | 52.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
effc0aaf379adf8067eb539ae5cd0918d4d10e89859fa8c46d839425ea077b12
|
|
BLAKE2b-256 checksum How to use checksums |
b0ceed94a4ae59e141af3e0bfc3fcfaf1938c2df7c1bf3e5f2d3d75575b6b8a6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.9
|