chrometools-mcp 3.3.8 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,57 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [3.4.0] - 2026-02-08
6
+
7
+ ### Added
8
+ - **Adaptive click strategy with elementFromPoint pre-check** — Before each click, verifies the target element is topmost via `document.elementFromPoint()`. If covered by another element (e.g. small button under `<a routerLink>`), uses DOM dispatch to bypass coordinate hit-testing. Fixes clicks on absolutely-positioned elements over links.
9
+ - **Auth redirect detection** — `navigateTo`, `openBrowser`, and `click` now warn when landing on a login page with returnUrl parameter. Broad login detection: password forms, phone/OTP forms, URL path (`/login`, `/signin`, `/auth`), CSS class matching.
10
+ - **Post-click element detachment detection** — Detects when clicked element is removed from DOM during click (Angular `*ngFor` + Zone.js pattern). Shows actionable hint with app fix (trackBy) and executeScript workaround.
11
+ - **Auth redirect in post-click diagnostics** — `formatDiagnosticsForAI` detects navigation to login pages with returnUrl and shows targeted warning.
12
+
13
+ ### Performance
14
+ - **findElementsByText early exit** — Stops DOM traversal at 40 results, preventing 120s timeout on heavy Angular Material pages with CDK overlay.
15
+
16
+ ## [3.3.9] - 2026-02-08
17
+
18
+ ### Added
19
+ - **AI Hints: modal content extraction** — Modals now show title, body text (200 chars), and action buttons
20
+ - Expanded selectors: mat-dialog-container, cdk-overlay-pane, `[class*="dialog"]`
21
+ - Topmost modal dedup for pages with multiple modals
22
+ - Actions extracted from `.modal-footer` / `[mat-dialog-actions]` (limit 5)
23
+
24
+ - **AI Hints: dropdown/menu item extraction** — Overlays now list actual option texts
25
+ - 11 overlay selectors: Angular CDK/Material, PrimeNG, Ant Design, custom `select-options`
26
+ - Menu vs dropdown auto-classification (role="menu", role="listbox", menuitem detection)
27
+ - Item text extraction (limit 10 items, shows total count)
28
+ - Deduplication of nested overlay elements
29
+
30
+ - **AI Hints: page heading in navigation** — `navigateTo` and `openBrowser` now show page heading
31
+ - Extracts h1 or `.page-title` / `[class*="page-title"]` fallback for SPAs
32
+ - Filters sr-only/visually-hidden elements (clip, 1px size, opacity)
33
+ - 500ms SPA render delay for Angular/React/Vue frameworks
34
+
35
+ - **Swagger/OpenAPI tools** — `loadSwagger` and `generateApiModels` (Phase 1)
36
+ - `loadSwagger`: Parse OpenAPI 2.0/3.x specs from URL or file (JSON/YAML)
37
+ - `generateApiModels`: Generate TypeScript interfaces or Python models (dataclass/pydantic/TypedDict)
38
+ - $ref resolution, enum generation, snake_case conversion for Python
39
+ - Supports filtering specific schemas
40
+
41
+ - **Page Object Model integration in exported tests** — `pageObjectMode` parameter
42
+ - `generate-integrated`: Generate POM + test using it
43
+ - `use-existing`: Generate test referencing existing POM file
44
+ - Works with `exportScenarioAsCode` and `appendScenarioToFile`
45
+
46
+ - **Synthetic drag mode** — `drag` tool now supports `mode: 'synthetic'`
47
+ - Better compatibility with JS libraries (frappe-gantt, jQuery UI, Sortable.js)
48
+ - Native mode (default) for standard HTML drag operations
49
+
50
+ - **analyzePage: framework click handler detection** — Detect addEventListener-based handlers
51
+ - APOM IDs returned from `smartFindElement` and `findElementsByText`
52
+
53
+ ### Fixed
54
+ - Swagger Phase 1 code review fixes (error handling, edge cases)
55
+
5
56
  ## [3.3.8] - 2026-02-03
6
57
 
7
58
  ### Added
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  ## Why ChromeTools MCP?
8
8
 
9
9
  **For AI Agents & Developers:**
10
- - 🎯 **54 specialized tools** for browser automation - from simple clicks to Figma comparisons
10
+ - 🎯 **56 specialized tools** for browser automation - from simple clicks to Figma comparisons
11
11
  - 🧠 **APOM (Agent Page Object Model)** - AI-friendly page representation (~8-10k tokens vs 15-25k for screenshots)
12
12
  - 🔄 **Persistent browser sessions** - pages stay open between commands for iterative workflows
13
13
  - ⚡ **Framework-aware** - handles React, Vue, Angular events and state updates automatically
@@ -219,7 +219,7 @@ The Chrome Extension is **required** for scenario recording and other advanced f
219
219
  - [Installation](#installation)
220
220
  - [Chrome Extension Setup](#chrome-extension-setup)
221
221
  - [AI Optimization Features](#ai-optimization-features)- [Scenario Recorder](#scenario-recorder) - Visual UI-based recording with smart optimization
222
- - [Available Tools](#available-tools) - **46+ Tools Total**
222
+ - [Available Tools](#available-tools) - **48+ Tools Total**
223
223
  - [AI-Powered Tools](#ai-powered-tools) - smartFindElement, analyzePage, getElementDetails, findElementsByText
224
224
  - [Core Tools](#1-core-tools) - ping, openBrowser
225
225
  - [Interaction Tools](#2-interaction-tools) - click, type, scrollTo, selectOption, selectFromGroup, drag, scrollHorizontal
@@ -227,6 +227,7 @@ The Chrome Extension is **required** for scenario recording and other advanced f
227
227
  - [Advanced Tools](#4-advanced-tools) - executeScript, getConsoleLogs, listNetworkRequests, getNetworkRequest, filterNetworkRequests, hover, setStyles, setViewport, getViewport, navigateTo
228
228
  - [Tab Management Tools](#5-tab-management-tools) - listTabs, switchTab
229
229
  - [Recorder Tools](#7-recorder-tools) - enableRecorder, executeScenario, listScenarios, searchScenarios, getScenarioInfo, deleteScenario, exportScenarioAsCode, appendScenarioToFile, generatePageObject
230
+ - [API / Swagger Tools](#8-api--swagger-tools) - loadSwagger, generateApiModels
230
231
  - [Typical Workflow Example](#typical-workflow-example)
231
232
  - [Tool Usage Tips](#tool-usage-tips)
232
233
  - [Configuration](#configuration)
@@ -260,7 +261,7 @@ AI: smartFindElement("login button")
260
261
 
261
262
  1. **`analyzePage`** - 🔥 **USE FREQUENTLY** - Get current page state after loads, clicks, submissions (cached, use refresh:true)
262
263
  2. **`smartFindElement`** - Natural language element search with multilingual support
263
- 3. **AI Hints** - Automatic context in all tools (page type, available actions, suggestions)
264
+ 3. **AI Hints** - Automatic context in all tools (page type, page heading, modal content, dropdown/menu items, suggestions)
264
265
  4. **Text search** - `findElementsByText` for finding elements by visible text
265
266
 
266
267
  **Performance:** 3-5x faster, 5-10x fewer requests
@@ -499,6 +500,10 @@ Click an element with optional result screenshot. **PREFERRED**: Use APOM ID fro
499
500
  - **Use case**: Buttons, links, form submissions, Django admin forms
500
501
  - **Returns**: Confirmation text + optional screenshot + network diagnostics
501
502
  - **Performance**: 2-10x faster without screenshot, instant with skipNetworkWait
503
+ - **Click strategy**: Three-tier fallback for maximum compatibility:
504
+ 1. Puppeteer native click (trusted CDP events)
505
+ 2. CDP coordinate click at element center (trusted, bypasses interception check)
506
+ 3. JavaScript `element.click()` (untrusted, last resort)
502
507
  - **Example**:
503
508
  ```javascript
504
509
  // PREFERRED: Using APOM ID
@@ -603,10 +608,16 @@ Drag element by mouse (click-hold-move-release). Simulates real mouse drag, not
603
608
  - `direction` (required): 'up', 'down', 'left', 'right', 'up-left', 'up-right', 'down-left', 'down-right'
604
609
  - `distance` (optional): Distance in pixels (default: 100)
605
610
  - `duration` (optional): Drag duration in milliseconds (default: 500)
611
+ - `mode` (optional): 'native' (default) or 'synthetic'
612
+ - **'native'**: Uses Puppeteer mouse API - faster, works for most cases
613
+ - **'synthetic'**: Dispatches DOM events (pointerdown/pointermove/pointerup) - better compatibility with JS libraries (frappe-gantt, jQuery UI Draggable, custom drag handlers)
606
614
  - **Use case**: Interactive maps (Google Maps, Leaflet), Gantt charts, SVG diagrams, canvas elements, sliders, drag-to-pan interfaces
607
- - **How it works**: Moves mouse to element center, presses mouse button, drags to target position, releases button
615
+ - **How it works**:
616
+ - **Native mode**: Uses Puppeteer's mouse API (mousedown → mousemove → mouseup)
617
+ - **Synthetic mode**: Dispatches PointerEvent/MouseEvent on element with intermediate pointermove events during drag
618
+ - **When to use synthetic mode**: If native drag doesn't trigger JS library event handlers (e.g., frappe-gantt, jQuery UI, React DnD)
608
619
  - **NOT for**: Standard overflow scrollbars (use `scrollTo` or `scrollHorizontal` instead)
609
- - **Returns**: Start/end mouse positions and drag delta
620
+ - **Returns**: Start/end mouse positions, drag delta, and mode used
610
621
 
611
622
  #### scrollHorizontal
612
623
  Scroll element horizontally (for tables, carousels, wide content).
@@ -1118,17 +1129,24 @@ Delete a scenario and its associated secrets. Searches all projects to find the
1118
1129
  - `language` (required): Target framework - `"playwright-typescript"`, `"playwright-python"`, `"selenium-python"`, `"selenium-java"`
1119
1130
  - `cleanSelectors` (optional): Remove unstable CSS classes (default: true)
1120
1131
  - `includeComments` (optional): Include descriptive comments (default: true)
1121
- - `generatePageObject` (optional): Also generate Page Object class for the page (default: false)
1132
+ - `generatePageObject` (optional): Also generate Page Object class for the page (default: false). Legacy - use `pageObjectMode` instead.
1122
1133
  - `pageObjectClassName` (optional): Custom Page Object class name (auto-generated if not provided)
1134
+ - `pageObjectMode` (optional): POM integration mode:
1135
+ - `"none"` (default) - no Page Object
1136
+ - `"generate"` - generate separate POM file (same as `generatePageObject: true`)
1137
+ - `"generate-integrated"` - generate POM + test that **uses** POM methods (imports, instantiates, calls POM methods)
1138
+ - `"use-existing"` - generate test that uses an **existing** POM file (requires `pageObjectFile`)
1139
+ - `pageObjectFile` (optional): Path to existing POM file (required for `"use-existing"` mode)
1123
1140
 
1124
- - **Use case**: Create new test files from recorded scenarios with optional Page Objects
1141
+ - **Use case**: Create new test files from recorded scenarios with optional Page Object integration
1125
1142
 
1126
1143
  - **Returns**: JSON with:
1127
1144
  - `action`: `"create_new_file"`
1128
1145
  - `suggestedFileName`: Suggested test filename
1129
1146
  - `testCode`: Full test code with imports
1130
1147
  - `instruction`: Instructions for Claude Code
1131
- - `pageObject` (if `generatePageObject=true`): Page Object code and metadata
1148
+ - `pageObject` (if POM generated): Page Object code and metadata
1149
+ - `pomIntegration` (if POM integrated): `{ className, mode }` info
1132
1150
 
1133
1151
  - **Example 1 - Test only**:
1134
1152
  ```javascript
@@ -1147,29 +1165,46 @@ Delete a scenario and its associated secrets. Searches all projects to find the
1147
1165
  }
1148
1166
  ```
1149
1167
 
1150
- - **Example 2 - Test + Page Object**:
1168
+ - **Example 2 - Test + separate Page Object** (legacy):
1151
1169
  ```javascript
1152
- // Export with Page Object class
1153
1170
  exportScenarioAsCode({
1154
1171
  scenarioName: "login_test",
1155
1172
  language: "playwright-typescript",
1156
1173
  generatePageObject: true,
1157
1174
  pageObjectClassName: "LoginPage"
1158
1175
  })
1176
+ ```
1159
1177
 
1160
- // Returns JSON with both files:
1161
- {
1162
- "action": "create_new_file",
1163
- "suggestedFileName": "login_test.spec.ts",
1164
- "testCode": "import { test } from '@playwright/test';\nimport { LoginPage } from './LoginPage';\n\ntest('login_test', async ({ page }) => {\n const loginPage = new LoginPage(page);\n await loginPage.goto();\n await loginPage.fillEmailInput('user@test.com');\n await loginPage.clickLoginButton();\n});",
1165
- "pageObject": {
1166
- "code": "import { Page, Locator } from '@playwright/test';\n\nexport class LoginPage { ... }",
1167
- "className": "LoginPage",
1168
- "suggestedFileName": "LoginPage.ts",
1169
- "elementCount": 12
1170
- },
1171
- "instruction": "Create a new test file 'login_test.spec.ts' with the testCode. Also create a Page Object file 'LoginPage.ts' with the pageObject.code."
1172
- }
1178
+ - **Example 3 - Test + integrated Page Object** (recommended):
1179
+ ```javascript
1180
+ // Generate POM and test that USES POM methods (not raw selectors)
1181
+ exportScenarioAsCode({
1182
+ scenarioName: "login_test",
1183
+ language: "playwright-typescript",
1184
+ pageObjectMode: "generate-integrated",
1185
+ pageObjectClassName: "LoginPage"
1186
+ })
1187
+
1188
+ // Returns test code using POM:
1189
+ // import { LoginPage } from './LoginPage';
1190
+ // test('login_test', async ({ page }) => {
1191
+ // const loginPage = new LoginPage(page);
1192
+ // await loginPage.goto();
1193
+ // await loginPage.fillUsername('admin');
1194
+ // await loginPage.clickLoginBtn();
1195
+ // });
1196
+ ```
1197
+
1198
+ - **Example 4 - Test using existing POM file**:
1199
+ ```javascript
1200
+ // Use pre-existing Page Object file
1201
+ exportScenarioAsCode({
1202
+ scenarioName: "login_test",
1203
+ language: "playwright-typescript",
1204
+ pageObjectMode: "use-existing",
1205
+ pageObjectFile: "./pages/LoginPage.ts"
1206
+ })
1207
+ // Test will import and use methods from the existing LoginPage
1173
1208
  ```
1174
1209
 
1175
1210
  - **Selector Cleaning**: Automatically removes unstable patterns:
@@ -1191,8 +1226,10 @@ Append recorded scenario as test code to an **EXISTING** test file. Automaticall
1191
1226
  - `referenceTestName` (optional): Reference test name for 'before'/'after' insertion
1192
1227
  - `cleanSelectors` (optional): Remove unstable CSS classes (default: true)
1193
1228
  - `includeComments` (optional): Include descriptive comments (default: true)
1194
- - `generatePageObject` (optional): Also generate Page Object class for the page (default: false)
1229
+ - `generatePageObject` (optional): Also generate Page Object class for the page (default: false). Legacy - use `pageObjectMode` instead.
1195
1230
  - `pageObjectClassName` (optional): Custom Page Object class name (auto-generated if not provided)
1231
+ - `pageObjectMode` (optional): POM integration mode - `"none"`, `"generate"`, `"generate-integrated"`, `"use-existing"` (see exportScenarioAsCode for details)
1232
+ - `pageObjectFile` (optional): Path to existing POM file (required for `"use-existing"` mode)
1196
1233
 
1197
1234
  - **Use case**: Add tests to existing test files without overwriting current tests
1198
1235
 
@@ -1325,6 +1362,78 @@ Append recorded scenario as test code to an **EXISTING** test file. Automaticall
1325
1362
  - `selenium-python`: Selenium with Python (WebDriver, explicit waits, By locators)
1326
1363
  - `selenium-java`: Selenium with Java (WebDriver, Page Factory compatible)
1327
1364
 
1365
+ ### 8. API / Swagger Tools
1366
+
1367
+ Tools for loading OpenAPI/Swagger specs and generating typed API models.
1368
+
1369
+ #### `loadSwagger`
1370
+
1371
+ Parse an OpenAPI 2.0 (Swagger) or 3.x spec and return a structured summary of endpoints, schemas, and auth.
1372
+
1373
+ | Parameter | Type | Required | Description |
1374
+ |-----------|------|----------|-------------|
1375
+ | `source` | string | Yes | URL (`https://...`) or local file path to `swagger.json` / `openapi.yaml` |
1376
+ | `format` | `'auto'` \| `'json'` \| `'yaml'` | No | Parse format (default: `auto` — detects from content) |
1377
+
1378
+ **Response includes:**
1379
+ - API title, version, base URL
1380
+ - All endpoints with method, path, operationId, parameters, request body, responses
1381
+ - Schema summaries (property names, types, enums)
1382
+ - Auth schemes (Bearer, API key, OAuth2)
1383
+
1384
+ ```javascript
1385
+ // Load from URL
1386
+ loadSwagger({ source: "https://petstore.swagger.io/v2/swagger.json" })
1387
+
1388
+ // Load from local file
1389
+ loadSwagger({ source: "/path/to/openapi.yaml" })
1390
+ ```
1391
+
1392
+ #### `generateApiModels`
1393
+
1394
+ Generate TypeScript interfaces or Python dataclasses/pydantic models from an OpenAPI spec.
1395
+
1396
+ | Parameter | Type | Required | Description |
1397
+ |-----------|------|----------|-------------|
1398
+ | `source` | string | Yes | URL or file path to spec |
1399
+ | `language` | `'typescript'` \| `'python'` | Yes | Target language |
1400
+ | `format` | `'auto'` \| `'json'` \| `'yaml'` | No | Parse format (default: `auto`) |
1401
+ | `style` | `'interface'` \| `'type'` | No | TypeScript style (default: `interface`) |
1402
+ | `pythonStyle` | `'dataclass'` \| `'pydantic'` \| `'typeddict'` | No | Python style (default: `dataclass`) |
1403
+ | `includeEnums` | boolean | No | Generate enum types (default: `true`) |
1404
+ | `schemas` | string[] | No | Filter to specific schema names |
1405
+
1406
+ **Features:**
1407
+ - Topological sort ensures correct declaration order
1408
+ - Enum deduplication (property enums reuse top-level enums)
1409
+ - `allOf` → extends/inheritance, `oneOf`/`anyOf` → union types
1410
+ - Circular reference detection with forward references
1411
+ - Swagger 2.0 automatically normalized to OpenAPI 3.x
1412
+
1413
+ ```javascript
1414
+ // Generate TypeScript interfaces
1415
+ generateApiModels({
1416
+ source: "https://petstore.swagger.io/v2/swagger.json",
1417
+ language: "typescript"
1418
+ })
1419
+ // Returns: { code: "export interface Pet { ... }", suggestedFileName: "pet-store-api.models.ts" }
1420
+
1421
+ // Generate Python pydantic models
1422
+ generateApiModels({
1423
+ source: "/path/to/openapi.yaml",
1424
+ language: "python",
1425
+ pythonStyle: "pydantic"
1426
+ })
1427
+ // Returns: { code: "class Pet(BaseModel): ...", suggestedFileName: "pet_store_api_models.py" }
1428
+
1429
+ // Generate only specific schemas
1430
+ generateApiModels({
1431
+ source: "https://api.example.com/openapi.json",
1432
+ language: "typescript",
1433
+ schemas: ["User", "Order"]
1434
+ })
1435
+ ```
1436
+
1328
1437
  ---
1329
1438
 
1330
1439
  ## Typical Workflow Example
@@ -1817,6 +1926,32 @@ npx chrometools-mcp --install-bridge
1817
1926
  - Close and reopen Chrome
1818
1927
  - Check Extension Service Worker console for errors
1819
1928
 
1929
+ ## Known Limitations
1930
+
1931
+ ### Angular *ngFor with Dynamic Bindings
1932
+
1933
+ In Angular apps using Zone.js, **any** programmatic click (including CDP trusted events) can trigger change detection **between** event listener callbacks. If `*ngFor` iterates over a getter that returns a new array reference each time (e.g., `[options]="getOptions()"`), Angular destroys and recreates all child elements mid-dispatch, causing `@HostListener('click')` on the target element to never fire. Only real hardware mouse events (physical mouse) are immune — CDP events, despite being `isTrusted: true`, are not dispatched through the OS event queue.
1934
+
1935
+ ChromeTools **automatically detects** this: after each click, it checks if the target element was removed from DOM. If so, the `ELEMENT DETACHED` hint is shown with a workaround guide.
1936
+
1937
+ **App fix** (recommended): add `trackBy` to `*ngFor`, or cache the array reference instead of returning a new one each time.
1938
+
1939
+ **Workaround** when app fix is not possible — use `executeScript` to call the Angular component API directly:
1940
+ ```javascript
1941
+ // 1. Find the component instance
1942
+ executeScript({ script: `
1943
+ const comp = ng.getComponent(document.querySelector('my-component'));
1944
+ // 2. Explore available events
1945
+ Object.keys(comp).filter(k => k.includes('Event'));
1946
+ ` })
1947
+
1948
+ // 3. Emit the event directly (bypasses DOM click entirely)
1949
+ executeScript({ script: `
1950
+ const comp = ng.getComponent(document.querySelector('my-component'));
1951
+ comp.selectedOptionChangeEvent.emit(comp.options.find(o => o.name === 'Delete'));
1952
+ ` })
1953
+ ```
1954
+
1820
1955
  ## Architecture
1821
1956
 
1822
1957
  - **Puppeteer** for Chrome automation
@@ -0,0 +1,227 @@
1
+ # Спецификация: Интеграция Page Object Model в экспортируемые тесты
2
+
3
+ ## Контекст
4
+
5
+ QA-автоматизаторы жалуются, что тесты, экспортируемые через `exportScenarioAsCode` / `appendScenarioToFile`, **не используют Page Object**. Даже при `generatePageObject: true` генерируются два несвязанных файла — тест с raw-селекторами и POM-класс отдельно.
6
+
7
+ **Сейчас** (raw-селекторы):
8
+ ```typescript
9
+ test('login', async ({ page }) => {
10
+ await page.goto('https://example.com/login');
11
+ await page.locator('#username').fill('admin');
12
+ await page.locator('#password').fill('secret');
13
+ await page.locator('#login-btn').click();
14
+ });
15
+ ```
16
+
17
+ **Нужно** (POM-интеграция):
18
+ ```typescript
19
+ import { LoginPage } from './LoginPage';
20
+
21
+ test('login', async ({ page }) => {
22
+ const loginPage = new LoginPage(page);
23
+ await loginPage.goto();
24
+ await loginPage.fillUsername('admin');
25
+ await loginPage.fillPassword('secret');
26
+ await loginPage.clickLoginBtn();
27
+ });
28
+ ```
29
+
30
+ ## Подход
31
+
32
+ Добавить новый параметр `pageObjectMode` к тулам экспорта. Два сценария:
33
+
34
+ 1. **`generate-integrated`** — сгенерировать POM + тест, который его использует
35
+ 2. **`use-existing`** — прочитать существующий POM-файл, сгенерировать тест, который его использует
36
+
37
+ Ключевой механизм — **selector matching**: сопоставление селекторов из записанного сценария с локаторами POM-элементов.
38
+
39
+ ## Изменения по файлам
40
+
41
+ ### 1. Новый файл: `utils/code-generators/pom-integrator.js`
42
+
43
+ Модуль связки POM с экшнами сценария:
44
+
45
+ - **`matchActionToPomElement(actionSelector, pomElements)`** — сопоставляет селектор экшна с элементом POM:
46
+ 1. Exact match (`#username` === `#username`)
47
+ 2. Normalized match (strip tag prefix: `input#username` → `#username`)
48
+ 3. Key-based match (извлечь id/name/data-testid и сравнить значения)
49
+ 4. Не найдено → null (fallback на raw-селектор)
50
+
51
+ - **`parsePomFile(fileContent, framework)`** — парсит существующий POM-файл regex'ами, возвращает `{ className, elements: [{name, selector, methodName, methodType}] }`:
52
+ - Playwright TS: ищет `this.X = page.locator('...')` и `async fillX(` / `async clickX(`
53
+ - Playwright Python: ищет `self.X = page.locator('...')` и `def fill_X(` / `def click_X(`
54
+ - Selenium Python: ищет `X = (By.CSS_SELECTOR, '...')` и `def fill_X(` / `def click_X(`
55
+ - Selenium Java: ищет `By X = By.cssSelector("...")` и `void fillX(` / `void clickX(`
56
+
57
+ ### 2. Изменить: `recorder/page-object-generator.js`
58
+
59
+ Добавить в return `generatePageObject()` поле **`elements`** — массив структурированных метаданных:
60
+ ```js
61
+ elements: uniqueElements.map(el => ({
62
+ name: sanitizeIdentifier(el.name, lang),
63
+ selector: el.selector,
64
+ tag: el.tag,
65
+ type: el.type,
66
+ methodName: generateMethodName(el, framework), // "fillUsername" / "clickSubmit"
67
+ methodType: getMethodType(el) // "fill" | "click" | "select"
68
+ }))
69
+ ```
70
+ Добавить вспомогательные функции `generateMethodName()` и `getMethodType()` (логика уже есть в `generateActionMethods()`, нужно её вынести).
71
+
72
+ ### 3. Изменить: `utils/code-generators/code-generator-base.js`
73
+
74
+ Добавить в конструктор опции `pomElements`, `pomClassName`, `pomImportPath`.
75
+
76
+ Добавить методы с дефолтными реализациями (no-op):
77
+ - `generatePomImports(className, importPath)` → `[]`
78
+ - `generatePomInstantiation(className)` → `[]`
79
+ - `generatePomAction(action, pomElement)` → `null` (значит fallback)
80
+
81
+ Модифицировать `generate()` и `generateTestOnly()`:
82
+ ```js
83
+ // После generateImports(), если POM mode:
84
+ if (this.options.pomClassName) {
85
+ lines.push(...this.generatePomImports(this.options.pomClassName, this.options.pomImportPath));
86
+ }
87
+
88
+ // После generateTestHeader(), если POM mode:
89
+ if (this.options.pomClassName) {
90
+ lines.push(...this.generatePomInstantiation(this.options.pomClassName));
91
+ }
92
+
93
+ // В цикле экшнов:
94
+ for (const action of scenario.chain) {
95
+ let actionCode = null;
96
+ if (this.options.pomElements) {
97
+ const match = matchActionToPomElement(this.prepareSelector(action), this.options.pomElements);
98
+ if (match) {
99
+ actionCode = this.generatePomAction(action, match);
100
+ }
101
+ }
102
+ if (!actionCode) {
103
+ actionCode = this.generateAction(action);
104
+ }
105
+ if (actionCode?.length > 0) lines.push(...actionCode);
106
+ }
107
+ ```
108
+
109
+ Специальная обработка `navigate` → если URL совпадает с POM entryUrl, заменить на `pomInstance.goto()`.
110
+
111
+ ### 4. Изменить: `playwright-typescript.js`
112
+
113
+ Реализовать 3 POM-метода:
114
+ ```js
115
+ generatePomImports(className, importPath) {
116
+ return [`import { ${className} } from '${importPath || './' + className}';`];
117
+ }
118
+
119
+ generatePomInstantiation(className) {
120
+ const varName = className.charAt(0).toLowerCase() + className.slice(1);
121
+ return [this.indent(`const ${varName} = new ${className}(page);`), ''];
122
+ }
123
+
124
+ generatePomAction(action, pomElement) {
125
+ const varName = this.options.pomClassName.charAt(0).toLowerCase() + this.options.pomClassName.slice(1);
126
+ // fill → await varName.fillUsername('text');
127
+ // click → await varName.clickSubmit();
128
+ // select → await varName.selectCountry('US');
129
+ // other → await varName.elementName.hover();
130
+ }
131
+ ```
132
+
133
+ ### 5. Изменить: `playwright-python.js`
134
+
135
+ ```python
136
+ # Import: from login_page import LoginPage
137
+ # Instantiation: login_page = LoginPage(page)
138
+ # Fill: login_page.fill_username('text')
139
+ # Click: login_page.click_submit()
140
+ ```
141
+
142
+ ### 6. Изменить: `selenium-python.js`
143
+
144
+ ```python
145
+ # Import: from login_page import LoginPage
146
+ # Instantiation: login_page = LoginPage(driver)
147
+ # Fill: login_page.fill_username('text')
148
+ # Click: login_page.click_submit()
149
+ ```
150
+
151
+ ### 7. Изменить: `selenium-java.js`
152
+
153
+ ```java
154
+ // Import: import pages.LoginPage;
155
+ // Instantiation: LoginPage loginPage = new LoginPage(driver);
156
+ // Fill: loginPage.fillUsername("text");
157
+ // Click: loginPage.clickSubmit();
158
+ ```
159
+
160
+ ### 8. Изменить: `server/tool-schemas.js`
161
+
162
+ Добавить к `ExportScenarioAsCodeSchema` и `AppendScenarioToFileSchema`:
163
+ ```js
164
+ pageObjectMode: z.enum(['none', 'generate', 'generate-integrated', 'use-existing']).optional()
165
+ .describe("POM integration: 'none' (default), 'generate' (separate POM, current behavior), 'generate-integrated' (POM + test using it), 'use-existing' (test uses existing POM file)"),
166
+ pageObjectFile: z.string().optional()
167
+ .describe("Path to existing POM file (for 'use-existing' mode)"),
168
+ ```
169
+
170
+ ### 9. Изменить: `server/tool-definitions.js`
171
+
172
+ Обновить описания тулов `exportScenarioAsCode` и `appendScenarioToFile` — добавить `pageObjectMode` и `pageObjectFile` в `inputSchema.properties`.
173
+
174
+ ### 10. Изменить: `index.js` (экспорт-хендлеры)
175
+
176
+ **`exportScenarioAsCode` (~строка 3321):**
177
+ - При `pageObjectMode === 'generate-integrated'`: сгенерировать POM, передать `pomElements`/`pomClassName` в генератор → получить тест с POM-вызовами
178
+ - При `pageObjectMode === 'use-existing'`: прочитать POM-файл через `FileAppender.readFile()`, распарсить через `parsePomFile()`, передать в генератор
179
+ - При `pageObjectMode === 'generate'` или `generatePageObject: true`: текущее поведение (обратная совместимость)
180
+
181
+ **`appendScenarioToFile` (~строка 3182):**
182
+ - Аналогичная логика для `generateTestOnly()`
183
+
184
+ ### 11. Обновить: `README.md`
185
+
186
+ Документировать `pageObjectMode` и `pageObjectFile` параметры для обеих тул.
187
+
188
+ ## Обратная совместимость
189
+
190
+ - `generatePageObject: true` (bool) — работает как раньше (maps to `pageObjectMode: 'generate'`)
191
+ - Новое поведение только при явном `pageObjectMode: 'generate-integrated'` или `'use-existing'`
192
+ - Fallback: если экшн не сматчился с POM-элементом — raw-селектор (как сейчас)
193
+
194
+ ## Алгоритм Selector Matching
195
+
196
+ | Action selector | POM selector | Результат |
197
+ |---|---|---|
198
+ | `#username` | `#username` | Exact match |
199
+ | `input#username` | `#username` | Normalized (strip tag) |
200
+ | `[name="email"]` | `[name="email"]` | Exact match |
201
+ | `input[name="email"]` | `[name="email"]` | Key match (name=email) |
202
+ | `.complex > path:nth(2)` | `#username` | No match → raw fallback |
203
+
204
+ ## Порядок реализации
205
+
206
+ 1. `page-object-generator.js` — добавить `elements` metadata + helper-функции
207
+ 2. `pom-integrator.js` — создать (matching + parsing)
208
+ 3. `code-generator-base.js` — POM-методы + модифицированный generate-цикл
209
+ 4. `playwright-typescript.js` — имплементация POM-методов (reference)
210
+ 5. `playwright-python.js` — имплементация POM-методов
211
+ 6. `selenium-python.js` — имплементация POM-методов
212
+ 7. `selenium-java.js` — имплементация POM-методов
213
+ 8. `tool-schemas.js` — новые параметры
214
+ 9. `tool-definitions.js` — обновить descriptions + schema
215
+ 10. `index.js` — оркестрация в хендлерах экспорта
216
+ 11. `README.md` — документация
217
+
218
+ ## Верификация
219
+
220
+ 1. Записать сценарий на тестовой странице (например, Google Search)
221
+ 2. Вызвать `exportScenarioAsCode` с `pageObjectMode: 'generate-integrated'`
222
+ 3. Проверить что тест-файл содержит import POM-класса и вызывает его методы
223
+ 4. Вызвать `generatePageObject`, сохранить файл, затем `exportScenarioAsCode` с `pageObjectMode: 'use-existing'`
224
+ 5. Проверить что тест использует существующий POM
225
+ 6. Проверить fallback: экшны без матча → raw-селекторы
226
+ 7. Проверить все 4 фреймворка
227
+ 8. Проверить `appendScenarioToFile` с POM-интеграцией