Metadata-Version: 2.4
Name: pom-generator
Version: 1.2.0
Summary: Python/Playwright engine to generate, read, validate and maintain YAML Page Object Models
Author: Khaled Limem, Olivier RENAULT
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: License
Requires-Dist: PyYAML>=6.0
Requires-Dist: playwright>=1.45.0
Dynamic: license-file

# pom-generator 1.2.0

[![Python Version](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/)
[![License](https://img.shields.io/badge/license-MIT-green)](License)
[![Status](https://img.shields.io/badge/status-stable-brightgreen)](https://github.com/khaledlim/pom-generator)

📦 **PyPI:** [https://pypi.org/project/pom-generator/](https://pypi.org/project/pom-generator/)  
🌐 **GitHub:** [https://github.com/khaledlim/pom-generator](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:

1. test attributes: `data-testid`, `data-test`, `data-qa`, `data-cy`;
2. `id`;
3. ARIA `role` + accessible name;
4. `aria-label`;
5. `name`;
6. stable `href` for links;
7. `placeholder`;
8. `title`;
9. exact-text XPath;
10. 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

```bash
python -m pip install .
python -m playwright install chromium
```

### From PyPI

Once published on PyPI:

```bash
python -m pip install pom-generator
python -m playwright install chromium
```

Check the installation:

```bash
pom-generator --version
pom-generator --help
```

---

## CLI overview

The general syntax is:

```bash
pom-generator --action <action> [options]
```

Available actions:

```text
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.

```bash
pom-generator \
  --mode record \
  --url "https://example.test/" \
  --pom-file ./pom/example.yml \
  --app-name example
```

Equivalent explicit form:

```bash
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.

```bash
pom-generator \
  --mode crawl \
  --url "https://example.test/" \
  --pom-file ./pom/example.yml \
  --max-pages 30
```

Useful options:

```bash
--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.

```bash
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:

```csv
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:

```bash
pom-generator \
  --mode csv \
  --url "https://example.test/" \
  --csv ./pages.csv \
  --pom-file ./pom/example.yml \
  --csv-interactive
```

An automatic timeout can be added:

```bash
--csv-interactive-timeout 10
```

A delay between pages can also be configured:

```bash
--csv-transition-delay 2
```

---

# Authentication provider

`pom-generator` can execute a **Python login provider** before POM generation.

Example:

```bash
pom-generator \
  --mode record \
  --url "https://example.test/" \
  --login ./login.py \
  --pom-file ./pom/example.yml
```

The provider receives these environment variables:

```text
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:

```python
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:

```bash
--login-timeout 600
```

---

# Browser options

## Headless mode

```bash
pom-generator \
  --mode crawl \
  --url "https://example.test/" \
  --headless
```

## Use a specific Chrome/Chromium executable

```bash
pom-generator \
  --mode record \
  --url "https://example.test/" \
  --executable-path "/path/to/chrome"
```

## Incognito mode

```bash
pom-generator \
  --mode record \
  --url "https://example.test/" \
  --incognito
```

---

# Validate a POM

Validate the POM structure and data integrity:

```bash
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:

```bash
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:

```bash
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:

```bash
--print-diff
```

Specify the diff output file:

```bash
--diff-output ./reports/pom.diff
```

The diff uses standard unified-diff markers:

```text
- 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:

```bash
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:

```bash
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
text
json
html
```

Example HTML report:

```bash
pom-generator \
  --action statistics \
  --pom-file ./pom/example.yml \
  --stats-format html \
  --stats-output ./reports/pom-statistics.html
```

Example JSON report:

```bash
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:

```bash
pom-generator \
  --action export \
  --pom-file ./pom/example.yml \
  --export-format json \
  --export-output ./exports/example.json
```

Export a flattened element list as CSV:

```bash
pom-generator \
  --action export \
  --pom-file ./pom/example.yml \
  --export-format csv \
  --export-output ./exports/example-elements.csv
```

Supported export formats:

```text
json
csv
```

---

# POM structure

A generated POM contains four main sections:

```text
application
runtime
pom
navigation
```

Simplified example:

```yaml
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:

```yaml
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:

```bash
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:

```python
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:

```python
from pathlib import Path

from POMGenerator import LocatorBuilder, YamlManager
```

Build locator candidates directly:

```python
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

```text
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:

```bash
python -m pip install -e .
python -m playwright install chromium
```

Run the tests:

```bash
python -m unittest discover -s tests -p "test_*.py"
```

Build the package:

```bash
python -m build
```

The generated wheel and source archive are written to `dist/`.

---

# Versioning and compatibility

Package version:

```text
1.2.0
```

Current YAML POM format:

```text
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:

```bash
pom-generator --mode record --url https://example.test --pom-file pom.yml
```

Crawl:

```bash
pom-generator --mode crawl --url https://example.test --pom-file pom.yml
```

Generate from CSV:

```bash
pom-generator --mode csv --url https://example.test --csv pages.csv --pom-file pom.yml
```

Validate:

```bash
pom-generator --action validate --pom-file pom.yml
```

Diff:

```bash
pom-generator --action diff --pom-file pom.yml --against baseline.yml --print-diff
```

Update:

```bash
pom-generator --action update --pom-file pom.yml --source-pom incoming.yml
```

Statistics:

```bash
pom-generator --action statistics --pom-file pom.yml --stats-format html
```

Export:

```bash
pom-generator --action export --pom-file pom.yml --export-format json
```
