page-object-generator·codegen·active

Page Object Generator

Inspects DOM (live or via spec) and emits typed Page Objects with stable selectors and high-level actions. Enforces single-responsibility per object.

EVANcursorclaude-codetrigger · ide
Page Object Generator
Inspects the DOM (live or via a spec) and emits typed Page Object classes with stable selectors and high-level domain actions.
When this skill fires
·An engineer points EVAN at a URL: evan generate-page-object --url /login
·EVAN encounters a Gherkin step that implies UI interaction with no existing page object
·An engineer asks Claude Code:"add a page object for the checkout flow"
What gets generated
A class file with:
·Private locator properties (typed as Locator)
·Public action methods at the domain level (not the click level)
·Public assertion helpers that read state without modifying it
·A constructor that takes the test Page as its only argument
Single-responsibility enforcement
Each page object covers exactly one logical screen. If a generated POM exceeds ~12 actions, EVAN suggests splitting it (e.g. CheckoutPageShippingStep + PaymentStep + ReviewStep).
This is non-negotiable. Multi-responsibility POMs are the single biggest cause of test maintenance churn.
Domain-level methods
Bad:
·async clickEmailField()
·async typeIntoEmailField(text)
·async clickSubmitButton()
Good:
·async login(email, password)
The bad version leaks UI structure into tests. When the form layout changes, every test breaks. The good version contains the change in the page object.
Selector choice
This skill delegates toLocator Strategist for actual selector picking. The page object uses the chosen selectors as-is; if a selector breaks in CI,Locator Healer repairs it without re-generating the entire object.
What it produces
A TypeScript file:
·Imports Page, Locator, expect from @playwright/test
·A class named after the screen (LoginPage, CheckoutShippingStep)
·Locators as private readonly fields
·Public domain actions, fully async
·An optional waitForLoaded() method if the screen has a known loading state
Example
For the URL /login, EVAN might emit:
·class LoginPage
·private emailInput: Locator = page.getByRole('textbox', { name: 'Email' })
·private passwordInput: Locator = page.getByRole('textbox', { name: 'Password' })
·private submitButton: Locator = page.getByRole('button', { name: 'Sign in' })
·async login(email, password): Promise<void> — fills both fields, clicks submit, waits for navigation
·async expectError(message: string): Promise<void> — uses expect() on the alert role