UNPKG

claude-playwright

Version:

Seamless integration between Claude Code and Playwright MCP for efficient browser automation and testing

202 lines 5.31 kB
// src/core/selector-cache.ts var SelectorCache = class { constructor(cacheManager) { this.currentUrl = ""; this.cacheManager = cacheManager; } setContext(page, url, profile) { this.page = page; this.currentUrl = url; this.currentProfile = profile; } async getCachedSelector(elementDescription, elementRef) { const cacheKey = { description: elementDescription, ref: elementRef, url: this.currentUrl }; const cached = await this.cacheManager.get( cacheKey, "selector", this.currentProfile ); if (cached) { if (this.page && await this.validateSelector(cached)) { return cached; } else { await this.invalidateSelector(elementDescription, elementRef); return null; } } return null; } async cacheSelector(elementDescription, selector, strategy, elementRef, additionalData) { const cacheKey = { description: elementDescription, ref: elementRef, url: this.currentUrl }; const entry = { selector, strategy, ...additionalData }; await this.cacheManager.set( cacheKey, entry, "selector", { url: this.currentUrl, profile: this.currentProfile } ); } async getCachedElementState(selector) { const cacheKey = { selector, url: this.currentUrl }; return await this.cacheManager.get( cacheKey, "state", this.currentProfile ); } async cacheElementState(selector, state) { const cacheKey = { selector, url: this.currentUrl }; await this.cacheManager.set( cacheKey, state, "state", { url: this.currentUrl, profile: this.currentProfile } ); } async getElementState(locator) { const selector = locator.toString(); const cached = await this.getCachedElementState(selector); if (cached) { return cached; } const [isVisible, isEnabled, isEditable, boundingBox, text, value] = await Promise.all([ locator.isVisible(), locator.isEnabled(), locator.isEditable(), locator.boundingBox(), locator.textContent(), locator.inputValue().catch(() => "") ]); const attributes = await locator.evaluate((el) => { const attrs = {}; for (const attr of el.attributes) { attrs[attr.name] = attr.value; } return attrs; }); const state = { isVisible, isEnabled, isEditable, boundingBox, text: text || "", value, attributes }; await this.cacheElementState(selector, state); return state; } async validateSelector(entry) { if (!this.page) return false; try { let locator; switch (entry.strategy) { case "css": locator = this.page.locator(entry.selector); break; case "xpath": locator = this.page.locator(`xpath=${entry.selector}`); break; case "text": locator = this.page.getByText(entry.selector); break; case "role": locator = this.page.getByRole(entry.selector); break; case "testid": locator = this.page.getByTestId(entry.selector); break; default: return false; } const count = await locator.count(); return count > 0; } catch { return false; } } async invalidateSelector(elementDescription, elementRef) { const cacheKey = { description: elementDescription, ref: elementRef, url: this.currentUrl }; await this.cacheManager.invalidate({ url: this.currentUrl, type: "selector", profile: this.currentProfile }); } async invalidateForUrl(url) { await this.cacheManager.invalidate({ url, profile: this.currentProfile }); } async invalidateAll() { await this.cacheManager.invalidate({ profile: this.currentProfile }); } async batchCacheSelectors(entries) { await Promise.all( entries.map( (entry) => this.cacheSelector( entry.description, entry.selector, entry.strategy, entry.ref ) ) ); } async preloadCommonSelectors() { if (!this.page) return; const commonSelectors = [ { selector: 'input[type="text"]', strategy: "css", description: "text input" }, { selector: 'input[type="email"]', strategy: "css", description: "email input" }, { selector: 'input[type="password"]', strategy: "css", description: "password input" }, { selector: 'button[type="submit"]', strategy: "css", description: "submit button" }, { selector: "form", strategy: "css", description: "form" }, { selector: "a", strategy: "css", description: "link" }, { selector: "select", strategy: "css", description: "dropdown" } ]; const validSelectors = []; for (const entry of commonSelectors) { const locator = this.page.locator(entry.selector); const count = await locator.count(); if (count > 0) { validSelectors.push(entry); } } await this.batchCacheSelectors(validSelectors); } }; export { SelectorCache }; //# sourceMappingURL=selector-cache.js.map