CLI tool for automated Page Object generation from web applications using AI.
Crawls your application, analyzes pages with AI (via OpenAI-compatible API), and generates Playwright Page Objects automatically.
- Automatic crawling - Discovers all URLs in your application
- AI-powered analysis - Uses LLM to identify interactive elements and generate stable selectors
- Multi-framework support - Vuetify, Symfony (Stimulus/Bootstrap), or generic websites
- Modal detection - Automatically finds and analyzes modal dialogs
- TypeScript output - Generates typed Page Objects
- Interactive review - Decide which pages become Page Objects
- Test generation - AI generates Playwright test files from Page Objects
git clone https://github.com/miroslavmyrha/po-generator.git
cd po-generator
bun install
bun linkNow you can use po-gen command anywhere:
po-gen --help
po-gen init
po-gen rungit clone https://github.com/miroslavmyrha/po-generator.git
cd po-generator
bun install
node --import tsx bin/po-gen.ts --helpCopy the example environment file and configure:
cp .env.example .envOr run the interactive setup:
po-gen init# Application
PO_GEN_BASE_URL=http://localhost:5173
PO_GEN_FRAMEWORK=generic # vuetify | symfony | generic
# Authentication (optional)
PO_GEN_AUTH_ENABLED=true
PO_GEN_LOGIN_URL=/login
PO_GEN_USERNAME=your@email.com
PO_GEN_PASSWORD=yourpassword
PO_GEN_SUCCESS_URL=/dashboard
# AI API (OpenAI-compatible, e.g., Open WebUI, Ollama)
PO_GEN_AI_URL=http://localhost:3000/api/v1
PO_GEN_AI_KEY=sk-xxx
PO_GEN_AI_MODEL=llama3
# Output
PO_GEN_OUTPUT_DIR=./outputpo-gen runThis will:
- Crawl - Find all URLs in your application
- Scan - AI analyzes each page for interactive elements
- Review - Interactively decide which pages need Page Objects
- Generate - Create TypeScript/JavaScript Page Objects
- Test-gen - AI generates Playwright test files for each Page Object
# Initialize configuration
po-gen init
# Crawl application and create sitemap
po-gen crawl
# AI scan for elements
po-gen scan
# Interactive review of AI decisions
po-gen review
# Generate Page Objects
po-gen generate
# Generate Playwright tests from Page Objects
po-gen test-gen# Scan with specific framework
po-gen scan --framework symfony
# Skip interactive review
po-gen run --skip-review
# Skip test generation in full workflow
po-gen run --skip-tests
# Generate TypeScript files
po-gen generate --typescript
# Scan specific page only
po-gen scan --page /dashboard
# Show help for any command
po-gen crawl --helpoutput/
├── sitemap.json # All discovered URLs
├── scanned/ # AI analysis per page
│ ├── dashboard.json
│ └── users.json
├── decisions.json # Page Object decisions
├── pages/ # Generated Page Objects
│ ├── dashboard-page.ts
│ ├── users-page.ts
│ └── index.ts
└── tests/ # Generated Playwright tests
├── dashboard.spec.ts
└── users.spec.ts
import { Page, Locator } from '@playwright/test';
/**
* Page Object for /dashboard
* Main dashboard with user management
*
* @generated Automatically generated by po-generator
*/
export class DashboardPage {
page: Page;
url: string;
constructor(page: Page) {
this.page = page;
this.url = '/dashboard';
/** Search input field */
this.searchField = page.locator('.v-text-field:has-text("Search") input');
/** Add new user button */
this.addUserBtn = page.locator('.v-btn:has-text("Add User")');
/** Data table with users */
this.dataTable = page.locator('.v-data-table');
}
async goto() {
await this.page.goto(this.url);
await this.page.waitForLoadState('networkidle');
}
async fillSearchField(value: string) {
await this.searchField.locator('input').fill(value);
}
async clickAddUserBtn() {
await this.addUserBtn.click();
}
}import { test, expect } from '@playwright/test';
import { DashboardPage } from '../pages/dashboard-page';
test.describe('Dashboard Page Tests', () => {
test('should search for users', async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goto();
await dashboardPage.fillSearchField('John');
await expect(page.locator('.v-data-table')).toBeVisible();
});
test('should open add user dialog', async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goto();
await dashboardPage.clickAddUserBtn();
await expect(page.locator('.v-dialog')).toBeVisible();
});
});- Recognizes all Vuetify 3 components (v-btn, v-text-field, v-select, v-dialog, etc.)
- Proper selectors for Vuetify's DOM structure
- Stimulus controllers and actions (
data-controller,data-action) - Turbo frames and streams
- Bootstrap components
- Embedded Vue.js components
- Standard HTML elements (button, input, select, a)
- Common patterns (forms, modals, tables)
- JavaScript event handlers
Works with any OpenAI-compatible API:
- Open WebUI
- Ollama (with OpenAI compatibility)
- LM Studio
- OpenAI API
- Azure OpenAI
# Run tests
bun run test
# Type check
bun run typecheckMIT