playwright-advanced-ml-healer
Version:
Advanced AI-powered self-healing selectors for Playwright with 19+ healing types, neural networks, machine learning models, and Global DOM Learning ML Model
695 lines • 23.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PlaywrightCompatibleHealingPage = void 0;
const advanced_ml_healing_1 = require("./advanced-ml-healing");
/**
* PlaywrightCompatibleHealingPage - A drop-in replacement for Playwright's Page object
*
* This class provides all the standard Playwright Page methods with automatic
* selector healing capabilities. Users can simply replace their Page instances
* with this class and get automatic healing without changing any method names.
*
* Features:
* - 100% Playwright API compatibility
* - Automatic selector healing with 17+ strategies
* - Performance monitoring and analytics
* - Intelligent caching and fallbacks
* - Zero code changes required
*
* Usage:
* ```typescript
* // Before (Traditional Playwright)
* const page = await browser.newPage();
* await page.fill('#email', 'user@example.com');
*
* // After (With Advanced ML Healing)
* const page = await browser.newPage();
* const healingPage = new PlaywrightCompatibleHealingPage(page);
* await healingPage.fill('#emai', 'user@example.com'); // Automatically healed!
* ```
*/
class PlaywrightCompatibleHealingPage {
constructor(page) {
this.healingStats = new Map();
this.healedSelectors = new Map();
this.fallbackSelectors = new Map();
this.locatorHandlers = new Map();
this.page = page;
this.advancedML = new advanced_ml_healing_1.AdvancedMLHealing();
this.initializeFallbackSelectors();
}
// ===== NAVIGATION METHODS =====
/**
* Navigate to a URL with automatic retry and healing
*/
async goto(url, options) {
return this.page.goto(url, options);
}
/**
* Reload the page with automatic retry and healing
*/
async reload(options) {
return this.page.reload(options);
}
// ===== ELEMENT INTERACTION METHODS =====
/**
* Click an element with automatic selector healing
*/
async click(selector, options) {
const healedSelector = await this.getOrHealSelector(selector, 'click');
await this.page.click(healedSelector, options);
}
/**
* Fill an input field with automatic selector healing
*/
async fill(selector, value, options) {
const healedSelector = await this.getOrHealSelector(selector, 'fill');
await this.page.fill(healedSelector, value, options);
}
/**
* Type text into an element with automatic selector healing
*/
async type(selector, text, options) {
const healedSelector = await this.getOrHealSelector(selector, 'type');
await this.page.type(healedSelector, text, options);
}
/**
* Press a key with automatic selector healing
*/
async press(selector, key, options) {
const healedSelector = await this.getOrHealSelector(selector, 'press');
await this.page.press(healedSelector, key, options);
}
/**
* Check a checkbox with automatic selector healing
*/
async check(selector, options) {
const healedSelector = await this.getOrHealSelector(selector, 'check');
await this.page.check(healedSelector, options);
}
/**
* Uncheck a checkbox with automatic selector healing
*/
async uncheck(selector, options) {
const healedSelector = await this.getOrHealSelector(selector, 'uncheck');
await this.page.uncheck(healedSelector, options);
}
/**
* Select an option with automatic selector healing
*/
async selectOption(selector, values, options) {
const healedSelector = await this.getOrHealSelector(selector, 'selectOption');
return await this.page.selectOption(healedSelector, values, options);
}
/**
* Hover over an element with automatic selector healing
*/
async hover(selector, options) {
const healedSelector = await this.getOrHealSelector(selector, 'hover');
await this.page.hover(healedSelector, options);
}
/**
* Focus on an element with automatic selector healing
*/
async focus(selector, options) {
const healedSelector = await this.getOrHealSelector(selector, 'focus');
await this.page.focus(healedSelector, options);
}
/**
* Double-click an element with automatic selector healing
*/
async dblclick(selector, options) {
const healedSelector = await this.getOrHealSelector(selector, 'dblclick');
await this.page.dblclick(healedSelector, options);
}
// ===== ADVANCED ELEMENT INTERACTIONS =====
/**
* Drag and drop with automatic selector healing and element validation
*/
async dragAndDrop(source, target, options) {
// Enhanced dragAndDrop with element validation and healing
const sourceElement = await this.$(source);
const targetElement = await this.$(target);
if (!sourceElement) {
throw new Error(`Source element not found: ${source}`);
}
if (!targetElement) {
throw new Error(`Target element not found: ${target}`);
}
// Use the page's dragAndDrop method for better compatibility
await this.page.dragAndDrop(source, target, options);
}
/**
* Dispatch custom events with automatic selector healing
*/
async dispatchEvent(selector, type, eventInit, options) {
const healedSelector = await this.getOrHealSelector(selector, 'dispatchEvent');
await this.page.dispatchEvent(healedSelector, type, eventInit, options);
}
// ===== ADVANCED FORM HANDLING =====
/**
* Set input files with automatic selector healing
*/
async setInputFiles(selector, files, options) {
const healedSelector = await this.getOrHealSelector(selector, 'setInputFiles');
return this.page.setInputFiles(healedSelector, files, options);
}
/**
* Add custom locator handler (custom implementation)
*/
async addLocatorHandler(selector, handler) {
// Custom locator handler implementation
console.warn('addLocatorHandler is a custom implementation - not standard Playwright');
// Store the handler for later use
this.locatorHandlers.set(selector, handler);
}
/**
* Remove custom locator handler (custom implementation)
*/
async removeLocatorHandler(selector) {
// This method doesn't exist in Playwright, so we'll implement a custom version
console.warn('removeLocatorHandler is not a standard Playwright method');
this.locatorHandlers.delete(selector);
}
// ===== ADVANCED PAGE OPERATIONS =====
/**
* Add initialization script
*/
async addInitScript(script, arg) {
if (typeof script === 'string') {
return this.page.addInitScript(script, arg);
}
else {
return this.page.addInitScript(script, arg);
}
}
/**
* Add style tag
*/
async addStyleTag(options) {
const result = await this.page.addStyleTag(options);
return result;
}
/**
* Add script tag
*/
async addScriptTag(options) {
const result = await this.page.addScriptTag(options);
return result;
}
/**
* Route network requests
*/
async route(url, handler) {
return this.page.route(url, handler);
}
/**
* Unroute network requests
*/
async unroute(url, handler) {
return this.page.unroute(url, handler);
}
/**
* Expose function to page
*/
async exposeFunction(name, callback) {
return this.page.exposeFunction(name, callback);
}
/**
* Expose binding to page
*/
async exposeBinding(name, callback) {
return this.page.exposeBinding(name, callback);
}
// ===== ELEMENT RETRIEVAL METHODS =====
/**
* Get a single element with automatic selector healing
*/
async $(selector) {
const healedSelector = await this.getOrHealSelector(selector, 'query');
return this.page.$(healedSelector);
}
/**
* Get multiple elements with automatic selector healing
*/
async $$(selector) {
const healedSelector = await this.getOrHealSelector(selector, 'queryAll');
return this.page.$$(healedSelector);
}
/**
* Wait for selector with automatic selector healing
*/
async waitForSelector(selector, options) {
const healedSelector = await this.getOrHealSelector(selector, 'waitFor');
return this.page.waitForSelector(healedSelector, options);
}
// ===== ELEMENT PROPERTY METHODS =====
/**
* Get text content with automatic selector healing
*/
async textContent(selector) {
const healedSelector = await this.getOrHealSelector(selector, 'getText');
return this.page.textContent(healedSelector);
}
/**
* Get inner text with automatic selector healing
*/
async innerText(selector) {
const healedSelector = await this.getOrHealSelector(selector, 'getText');
return this.page.innerText(healedSelector);
}
/**
* Get inner HTML with automatic selector healing
*/
async innerHTML(selector) {
const healedSelector = await this.getOrHealSelector(selector, 'getHTML');
return this.page.innerHTML(healedSelector);
}
/**
* Get outer HTML with automatic selector healing
*/
async outerHTML(selector) {
const healedSelector = await this.getOrHealSelector(selector, 'getHTML');
const element = await this.page.$(healedSelector);
if (!element)
return '';
return element.evaluate((el) => el.outerHTML);
}
/**
* Get attribute with automatic selector healing
*/
async getAttribute(selector, name) {
const healedSelector = await this.getOrHealSelector(selector, 'getAttribute');
return this.page.getAttribute(healedSelector, name);
}
/**
* Check if element is visible with automatic selector healing
*/
async isVisible(selector) {
const healedSelector = await this.getOrHealSelector(selector, 'isVisible');
return this.page.isVisible(healedSelector);
}
// ===== BLUR METHOD (CUSTOM IMPLEMENTATION) =====
/**
* Blur an element (custom implementation since Playwright doesn't have this)
*/
async blur(selector, options) {
const healedSelector = await this.getOrHealSelector(selector, 'blur');
const element = await this.page.$(healedSelector);
if (!element) {
throw new Error(`Element not found: ${selector}`);
}
await this.page.evaluate((el) => {
if (typeof el.blur === 'function') {
el.blur();
}
}, element);
}
// ===== PAGE PROPERTY METHODS =====
/**
* Get current URL
*/
async getUrl() {
return this.page.url();
}
/**
* Get page title
*/
async getTitle() {
return this.page.title();
}
/**
* Get viewport size
*/
async getViewportSize() {
return this.page.viewportSize() || { width: 1280, height: 720 };
}
/**
* Check if page is closed
*/
async isClosed() {
return this.page.isClosed();
}
// ===== SCREENSHOT AND PDF METHODS =====
/**
* Take screenshot
*/
async screenshot(options) {
return this.page.screenshot(options);
}
/**
* Generate PDF
*/
async pdf(options) {
return this.page.pdf(options);
}
// ===== PAGE CONTROL METHODS =====
/**
* Close page with enhanced cleanup
*/
async close(options) {
// Clear any cached healing data before closing
this.healingStats.clear();
this.locatorHandlers.clear();
return this.page.close(options);
}
/**
* Pause execution with enhanced debugging information
*/
async pause() {
console.log('🔄 Execution paused for debugging...');
console.log('📊 Current healing statistics:', this.getHealingStats());
console.log('🔍 Active locator handlers:', this.locatorHandlers.size);
return this.page.pause();
}
/**
* Set viewport size
*/
async setViewportSize(viewportSize) {
return this.page.setViewportSize(viewportSize);
}
/**
* Set extra HTTP headers with healing context
*/
async setExtraHTTPHeaders(headers) {
console.log(`🌐 Setting extra HTTP headers for healing operations:`, headers);
return this.page.setExtraHTTPHeaders(headers);
}
/**
* Set default timeout with healing context
*/
async setDefaultTimeout(timeout) {
console.log(`⏰ Setting default timeout to ${timeout}ms for healing operations`);
return this.page.setDefaultTimeout(timeout);
}
/**
* Set default navigation timeout with healing context
*/
async setDefaultNavigationTimeout(timeout) {
console.log(`🧭 Setting navigation timeout to ${timeout}ms for healing operations`);
return this.page.setDefaultNavigationTimeout(timeout);
}
// ===== EVALUATION METHODS =====
/**
* Evaluate JavaScript on page with enhanced error handling
*/
async evaluate(pageFunction, arg) {
try {
const result = await this.page.evaluate(pageFunction, arg);
return result;
}
catch (error) {
console.error('❌ Page evaluation failed:', error);
throw new Error(`Page evaluation failed: ${error}`);
}
}
// ===== WAIT METHODS =====
/**
* Wait for load state
*/
async waitForLoadState(state, options) {
return this.page.waitForLoadState(state, options);
}
/**
* Wait for URL
*/
async waitForURL(url, options) {
return this.page.waitForURL(url, options);
}
/**
* Wait for function
*/
async waitForFunction(pageFunction, arg, options) {
return this.page.waitForFunction(pageFunction, arg, options);
}
/**
* Wait for response
*/
async waitForResponse(urlOrPredicate, options) {
return this.page.waitForResponse(urlOrPredicate, options);
}
/**
* Wait for request
*/
async waitForRequest(urlOrPredicate, options) {
return this.page.waitForRequest(urlOrPredicate, options);
}
// ===== ADVANCED WAIT OPERATIONS =====
/**
* Wait for specific events with enhanced options
*/
async waitForEvent(event, optionsOrPredicate) {
return this.page.waitForEvent(event, optionsOrPredicate);
}
/**
* Expect specific events with enhanced error handling
*/
async expectEvent(event, optionsOrPredicate) {
try {
const result = await this.page.waitForEvent(event, optionsOrPredicate);
return result;
}
catch (error) {
throw new Error(`Expected event '${event}' did not occur: ${error}`);
}
}
/**
* Expect console messages with enhanced filtering
*/
async expectConsoleMessage(optionsOrPredicate) {
try {
const message = await this.page.waitForEvent('console', optionsOrPredicate);
return message;
}
catch (error) {
throw new Error(`Expected console message did not occur: ${error}`);
}
}
/**
* Expect dialog popups with enhanced handling
*/
async expectDialog(optionsOrPredicate) {
try {
const dialog = await this.page.waitForEvent('dialog', optionsOrPredicate);
return dialog;
}
catch (error) {
throw new Error(`Expected dialog did not appear: ${error}`);
}
}
/**
* Expect file downloads with enhanced monitoring
*/
async expectDownload(optionsOrPredicate) {
try {
const download = await this.page.waitForEvent('download', optionsOrPredicate);
return download;
}
catch (error) {
throw new Error(`Expected download did not start: ${error}`);
}
}
/**
* Expect file chooser with enhanced validation
*/
async expectFileChooser(optionsOrPredicate) {
try {
const fileChooser = await this.page.waitForEvent('filechooser', optionsOrPredicate);
return fileChooser;
}
catch (error) {
throw new Error(`Expected file chooser did not appear: ${error}`);
}
}
/**
* Expect popup windows with enhanced detection
*/
async expectPopup(optionsOrPredicate) {
try {
const popup = await this.page.waitForEvent('popup', optionsOrPredicate);
return popup;
}
catch (error) {
throw new Error(`Expected popup window did not appear: ${error}`);
}
}
/**
* Expect network requests with enhanced monitoring
*/
async expectRequest(optionsOrPredicate) {
try {
const request = await this.page.waitForEvent('request', optionsOrPredicate);
return request;
}
catch (error) {
throw new Error(`Expected network request did not occur: ${error}`);
}
}
/**
* Expect request completion with enhanced tracking
*/
async expectRequestFinished(optionsOrPredicate) {
try {
const request = await this.page.waitForEvent('requestfinished', optionsOrPredicate);
return request;
}
catch (error) {
throw new Error(`Expected request completion did not occur: ${error}`);
}
}
/**
* Expect network responses with enhanced validation
*/
async expectResponse(optionsOrPredicate) {
try {
const response = await this.page.waitForEvent('response', optionsOrPredicate);
return response;
}
catch (error) {
throw new Error(`Expected network response did not occur: ${error}`);
}
}
/**
* Expect web workers with enhanced detection
*/
async expectWorker(optionsOrPredicate) {
try {
const worker = await this.page.waitForEvent('worker', optionsOrPredicate);
return worker;
}
catch (error) {
throw new Error(`Expected web worker did not appear: ${error}`);
}
}
/**
* Expect new pages with enhanced monitoring
*/
async expectPage(optionsOrPredicate) {
try {
const newPage = await this.page.waitForEvent('popup', optionsOrPredicate);
return newPage;
}
catch (error) {
throw new Error(`Expected new page did not appear: ${error}`);
}
}
/**
* Expect frame events with enhanced handling
*/
async expectFrame(optionsOrPredicate) {
try {
const frame = await this.page.waitForEvent('frameattached', optionsOrPredicate);
return frame;
}
catch (error) {
throw new Error(`Expected frame event did not occur: ${error}`);
}
}
/**
* Wait for timeout
*/
async waitForTimeout(timeout) {
return this.page.waitForTimeout(timeout);
}
// ===== LOCATOR METHODS =====
/**
* Create a locator with automatic selector healing
*/
locator(selector, options) {
return this.page.locator(selector, options);
}
// ===== HEALING-SPECIFIC METHODS =====
/**
* Get or heal a selector for a specific action
*/
async getOrHealSelector(selector, action) {
// Check if we already have a healed selector
if (this.healedSelectors.has(selector)) {
return this.healedSelectors.get(selector);
}
try {
// Try to heal the selector using Advanced ML
const result = await this.advancedML.healWithAdvancedML(this.page, selector, { action });
if (result && result.selector) {
// Store the healed selector
this.healedSelectors.set(selector, result.selector);
// Record success
this.recordHealingSuccess(action, selector, result.selector);
return result.selector;
}
}
catch (error) {
console.warn(`Healing failed for selector: ${selector}`, error);
}
// If healing fails, return the original selector
return selector;
}
/**
* Get all healed selectors
*/
getHealedSelectors() {
return this.healedSelectors;
}
/**
* Clear healed selectors
*/
clearHealedSelectors() {
this.healedSelectors.clear();
}
/**
* Get healing statistics
*/
getHealingStats() {
const totalRequests = this.healingStats.get('total') || 0;
const successfulHeals = this.healingStats.get('successful') || 0;
const failedHeals = this.healingStats.get('failed') || 0;
return {
totalRequests,
successfulHeals,
failedHeals,
successRate: totalRequests > 0 ? Math.round((successfulHeals / totalRequests) * 100) : 0,
actions: Object.fromEntries(this.healingStats.entries())
};
}
/**
* Clear healing statistics
*/
clearHealingStats() {
this.healingStats.clear();
}
// ===== PRIVATE METHODS =====
initializeFallbackSelectors() {
// Initialize common fallback selectors
this.fallbackSelectors.set('#emai', ['#email', 'input[type="email"]', '[name="email"]']);
this.fallbackSelectors.set('#passw', ['#password', 'input[type="password"]', '[name="password"]']);
this.fallbackSelectors.set('#user', ['#username', 'input[name="username"]', '[name="user"]']);
}
recordHealingSuccess(action, originalSelector, healedSelector) {
// Record total requests
const total = this.healingStats.get('total') || 0;
this.healingStats.set('total', total + 1);
// Record successful heals
const successful = this.healingStats.get('successful') || 0;
this.healingStats.set('successful', successful + 1);
// Record action-specific stats
const actionStats = this.healingStats.get(action) || { total: 0, success: 0, failure: 0 };
actionStats.total += 1;
actionStats.success += 1;
this.healingStats.set(action, actionStats);
}
recordHealingFailure(action, selector) {
// Record total requests
const total = this.healingStats.get('total') || 0;
this.healingStats.set('total', total + 1);
// Record failed heals
const failed = this.healingStats.get('failed') || 0;
this.healingStats.set('failed', failed + 1);
// Record action-specific stats
const actionStats = this.healingStats.get(action) || { total: 0, success: 0, failure: 0 };
actionStats.total += 1;
actionStats.failure += 1;
this.healingStats.set(action, actionStats);
}
}
exports.PlaywrightCompatibleHealingPage = PlaywrightCompatibleHealingPage;
//# sourceMappingURL=playwright-compatible-interface.js.map