UNPKG

healenium-global

Version:

Globally self-healing wrapper for Playwright actions (click, fill, etc.)

97 lines (81 loc) 2.45 kB
import { Page } from 'playwright'; import fs from 'fs'; import path from 'path'; import levenshtein from 'js-levenshtein'; const fallbackPath = path.join(__dirname, 'fallback-selectors.json'); function loadFallbacks(): Record<string, string> { return fs.existsSync(fallbackPath) ? JSON.parse(fs.readFileSync(fallbackPath, 'utf-8')) : {}; } function saveFallback(original: string, healed: string) { const data = loadFallbacks(); data[original] = healed; fs.writeFileSync(fallbackPath, JSON.stringify(data, null, 2)); } async function findHealedSelector( page: Page, selector: string ): Promise<string | null> { const failedText = await page .locator(selector) .textContent() .catch(() => ''); if (!failedText) return null; const elements = await page.$$('body *'); let best = { selector: '', score: Infinity }; for (const el of elements) { const text = await el.textContent(); if (!text) continue; const score = levenshtein(failedText.trim(), text.trim()); if (score < best.score && score < 10) { const tag = await el.evaluate((e) => e.tagName.toLowerCase()); best = { selector: `${tag}:has-text("${text.trim()}")`, score }; } } if (best.selector) { saveFallback(selector, best.selector); return best.selector; } return null; } export async function patchPageGlobally(page: Page) { const actions = [ 'click', 'fill', 'check', 'uncheck', 'hover', 'dblclick', ]; const fallbacks = loadFallbacks(); for (const method of actions) { const original = (page as any)[method].bind(page); (page as any)[method] = async ( selector: string, ...args: any[] ) => { try { return await original(selector, ...args); } catch (err) { console.warn(`[Healenium] ${method} failed on: ${selector}`); const fallbacks = loadFallbacks(); if (fallbacks[selector]) { console.log( `[Healenium] Retrying with fallback: ${fallbacks[selector]}` ); return await original(fallbacks[selector], ...args); } const healed = await findHealedSelector(page, selector); if (healed) { console.log( `[Healenium] Found and saved fallback: ${healed}` ); saveFallback(selector, healed); return await original(healed, ...args); } throw err; } }; } }