Skip to main content
Glama
dom-selector-diagnostic.js20 kB
/** * @document DOM Selector Diagnostic Tool - Authenticated Version * @version 1.1.1 * @status active * @author Claude * @created 2025-06-29 * @last_updated 2025-06-29 */ import { chromium } from 'playwright'; import { writeFile } from 'fs/promises'; /** * Authenticated DOM Selector Diagnostic Tool * Uses the same authentication flow as the existing MCP server * to analyze the EuConquisto Composer interface correctly. */ class AuthenticatedDOMDiagnostic { constructor() { this.browser = null; this.page = null; this.baseURL = "https://composer.euconquisto.com/#/embed"; this.orgId = "36c92686-c494-ec11-a22a-dc984041c95d"; this.jwtToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkbWluLmRlc2Vudm9sdmltZW50b0BldWNvbnF1aXN0by5jb20iLCJuYW1lIjoiQWRtaW4gRGV2Iiwib2lkIjoiNWZiM2RlYzYtYzQ5NC1lYzExLWEyMmEtZGM5ODQwNDFjOTVkIiwiZGlyZWN0b3J5IjoiYjBmZWY4NjAtYzQ5NC1lYzExLWEyMmEtZGM5ODQwNDFjOTVkIiwiYXBpbSI6IkVFQzUzQTI0LUVDMEUtNDFCOS05NDA1LTg2QTE3NTAwREIzNCIsImRpcm4iOiJEZXNlbnZvbHZpbWVudG8iLCJyb2xlIjpbIlJldmlld0NvbnRyaWJ1dG9yIiwiQ29udHJpYnV0b3JHbG9iYWwiLCJBZG1pbmlzdHJhdG9yR2xvYmFsIl0sImRjbiI6WyJINHNJQUFBQUFBQUFBMkptWUdBd1pRQ0JaQXVqRkdNREM0dFYyMXRTRS1vZHA5NElpMnVVYTNqMXNxRXl6YWhrN3ZxWlN5MG5sLV94c3cyb0xEN05jT2ppRThWelhyZDBEamF3XzQyVW1fZmxrT1loZzlVN0ZKOGx5N1dWQVFBQUFQX18iLCJINHNJQUFBQUFBQUFBMkptWUdBd1pRQ0JaQXVqRkdNREM0c2xCWS0zTjB5WkU1S1NNSFhKTzhjcm0wNS1remgtYVBzTXhtMnZvOTBxYzhXTjktX08wRzRMbmo5dDRqb2R4bnZNdVkycHJEd1NrYlkyamJOMnpuNXJvemJmR3dBQUFQX18iLCJINHNJQUFBQUFBQUFBMkptWUdBd1pRQ0JaQXVqRkdNREM0dnpkMWU1SDFGTzVhenRyR1RuZXZ6emx0U0djM3VFT1IteHExWXZNVHF5NEstaThXR0gwd292cC1ka3FjNm82d2xSZkM0d3k5MVcxSHFqOFU4aEMtRWZMa0pSQUFBQUFQX18iLCJINHNJQUFBQUFBQUFBMkptWUdBd1pRQ0JaQXVqRkdNREM0dU5XMnUyS3A5dXZhUGhkOFNpOE9KNV8yTmRmZnNTSC0temZHTXVmX084Wm5GbXFXRE5qeE1ubnBWelRkVTBPVF9mUldtNVVhTkxlV2oyTjY5Y3JRS0x1SmJQQUFBQUFQX18iLCJINHNJQUFBQUFBQUFBMkptWUdBd1pRQ0JaQXVqRkdNREM0czY2NE5odVhwck52S3FiV1RuUEtGNTU2UzRycXdXNDJ1SnkzbjNGVkxFVjZ4VlljdU9kTnVrZXliMjdKbjRlOS1GaGNNdW1iOTZ1cUx0aEptSDJrX0h0U29MQUFBQUFQX18iLCJINHNJQUFBQUFBQUFBMkptWUdBd1pRQ0JaQXVqRkdNREN3dWpCWjhyQW81RUxEaHRvQ2ZUdmVQcTVET2hpd1dtM3A2eFktTDdIYkpTb2RFU2I2SUVWQ3BQcXpETTJ2WlR6VzlsMXBrWlBFb3Zkbl8tV3R2LWY0bkt3dmEtU0FBQUFBRF9fdyJdLCJzY3AiOlsiSDRzSUFBQUFBQUFBQTJKbVlHRHdZQUFCMDZRMDA5VFU1RlNKTmZzV1NKNDRWWGZBeHZYeDVEc3lzaFhPNmllVGk0cWVYbTZaVVN2QzZ2djNST0hNRDBkVHBDVHUzekFfVUMtWVVIOHIyMzNlS29mQ0hZR3UwWG83aWswWGROWVVxdmotV0hfclhiU3VVYnJZM1VZQUFBQUFfXzgiLCJINHNJQUFBQUFBQUFBd0RTQWkzOUF3QUFBck1BQUFBQUFHRTVaalF6TkRsa2E0TWozbjdQejY0dmpPSWl5LVlNb3dhRDdlMWpqcDZJakE2SVFZMi14TFNOTWFSZWNrTTdzWEJLamR2SkpoeUF5TnZJVHQ5aUFFWHcybVVzM3lIbGc2UHNtSld1Mm1EU2UwbXlrYnQ0OGl1NTlkYTIxMVBNdkxyRzdxOV9teDlhYWI0SjdZbWlNODg2OTVUclk3WFY1Nmlsb3UwdnQ4VmdBMWptbkdYd0paa0ZDTXhYaUlhcGl5S01TVGlfa3BnREdQdlRlYUNNdE5GSXlIU0gwUTZ3bTFwRkJBYzRMQ1dqRThMeWtnc1ZtbEJxRG9aek1oZTVnY21sZHl0VW1FVFltakIyakVaUHdvd0M2NFRZb1JudDJOWkpmRDdDZXg1T3FqY1ptVU5tNjMtVlRYOFh0UEtza1BpSnpIb3kzcHF5dXVycnVrTndZenducHpwak51OEdKZXQ4V2RoMl85ckNoVnpPUnR4N1N5UXEyb2prUmJfWEphenFtTnEwMEVTcTNSZUVSX0pQZmhGOGg4bGlxcGVWeE1FSG5RUGFmMXp2N3FVQ3JzTWtrVjd3NU5sOURWYU9DMnNLV2Q1TDdBNDYxUlFRSjVUNU5MMGQ2UGZqVWV4dkZTR0RQdW5jcHAxeDA0TlBOM2ttTmdMSlF3eDZLZjRxLVBZbDVXWmFQUTlXU0NLNHBmeHY3ZTJRZEw0X1IwQ1c3NUdQRHh1bEZQZG1tcmRrc0k2RVhqUTFkQTdkZlNFUnJoZHdvOUhCN0xGMzRYSENnMWg3Z2I4UWVKdmhMTXRjUmtlUzVpQkctOGNfNkdCQU9YUDdQR2ptQ1NUZzV2WGVyX0Rud3FkektCOFdtY1p0OXFjMXZpU0ROV1JHZVN0bHc3d250UzFSSXlBN205cS01YU9FLUx0ODB3bE1HTkxOTi1TVFN3OWVDN2dxVmQtSUZZWlhYQ2NZcWNfcWc1dDhodFdSLVJ2cUJsX3Fjbkt2N2xhaHk5elRTaHJfX1BTRWJqdTg0SkhDV3I5SXhGd2lzYVd1eVZiMUp0ZUQzcDk0WnpWZDhnTEJQRGFRVUJMZHc1SVEzWkJIc3daTXExRjJqVGdEcF92RDJLQzZzVnd1dVhaa3RzYWlrc3pMZW5NcTFUVjRUMmF5QUdyVHB3UlUzMFMwc3hfQ3pCRVlLREV0b2s4eDI5TGlnUnlHMnVfZDVpOHRyVEJ1WTNjVFdzSXVIdzRibXNVV1lkTXNwblRMMXpwNE1LWUFBQURfX3ciXSwibmJmIjoxNzQ4ODc2OTU1LCJleHAiOjE3NTE0Njg5NTUsImlhdCI6MTc0ODg3Njk1NSwiaXNzIjoiaHR0cHM6Ly9hcGkuZGlnaXRhbHBhZ2VzLmNvbS5iciIsImF1ZCI6IkV1Q29ucXVpc3RvIn0.iTUfl6-mwLwFaxYYPf6PufRSYbSJlw3tKejmbc5G42g"; this.results = { timestamp: new Date().toISOString(), url: '', authentication: {}, selectors: {}, errors: [], recommendations: [] }; } /** * Initialize browser with same config as MCP server */ async initialize() { try { console.log('🚀 Initializing authenticated browser diagnostic...'); this.browser = await chromium.launch({ headless: false, args: [ '--no-sandbox', '--disable-dev-shm-usage', '--enable-features=VizDisplayCompositor', '--disable-background-timer-throttling', '--disable-backgrounding-occluded-windows', '--disable-renderer-backgrounding', '--disable-features=TranslateUI', '--disable-ipc-flooding-protection', '--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' ], timeout: 60000 }); const context = await this.browser.newContext({ viewport: { width: 1280, height: 720 }, permissions: ['notifications'], colorScheme: 'light', timezoneId: 'America/New_York' }); this.page = await context.newPage(); this.page.setDefaultTimeout(60000); this.page.setDefaultNavigationTimeout(120000); console.log('✅ Browser initialized with MCP server configuration'); return true; } catch (error) { this.results.errors.push(`Initialization failed: ${error.message}`); console.error('❌ Initialization failed:', error.message); return false; } } /** * Navigate using the same authentication flow as MCP server */ async navigateWithAuth() { if (!this.page) { throw new Error('Browser not initialized'); } const embedURL = `${this.baseURL}/auth-with-token/pt_br/home/${this.orgId}/${this.jwtToken}`; try { console.log('🌐 Navigating with JWT authentication...'); console.log(`🔗 URL: ${embedURL.substring(0, 80)}...`); const response = await this.page.goto(embedURL, { waitUntil: 'domcontentloaded', timeout: 120000 }); if (!response || !response.ok()) { throw new Error(`Navigation failed with status: ${response?.status()}`); } this.results.url = this.page.url(); this.results.authentication = { jwtProvided: true, navigationStatus: response.status(), finalURL: this.results.url }; console.log(`📍 Successfully navigated to: ${this.results.url}`); // Wait for SPA to initialize console.log('⏳ Waiting for JavaScript application to initialize...'); let attempts = 0; const maxAttempts = 12; while (attempts < maxAttempts) { attempts++; console.log(` 🔄 Attempt ${attempts}/${maxAttempts}: Checking for app initialization...`); await this.page.waitForTimeout(5000); const hasInteractiveElements = await this.page.evaluate(() => { const buttons = document.querySelectorAll('button, a, [role="button"], input[type="button"]').length; const hasContent = document.body.textContent && document.body.textContent.length > 100 && !document.body.textContent.includes('You need to enable JavaScript'); return buttons > 0 || hasContent; }); if (hasInteractiveElements) { console.log('✅ JavaScript application initialized successfully'); break; } if (attempts === maxAttempts) { console.warn('⚠️ App may not have fully initialized, but continuing...'); } } // Take debug screenshot await this.page.screenshot({ path: `debug-authenticated-${Date.now()}.png`, fullPage: true }); return true; } catch (error) { this.results.errors.push(`Authentication navigation failed: ${error.message}`); console.error('❌ Authentication navigation failed:', error); if (this.page) { await this.page.screenshot({ path: `error-auth-${Date.now()}.png`, fullPage: true }); } return false; } } /** * Test Nova Composição selectors from MCP server code */ async testNovaComposicaoSelectors() { console.log('🔍 Testing Nova Composição selectors from MCP server...'); // These are the exact selectors from the MCP server composition-lifecycle.ts const mcpSelectors = [ 'text=NOVA COMPOSIÇÃO', 'button:has-text("NOVA COMPOSIÇÃO")', '[role="button"]:has-text("NOVA COMPOSIÇÃO")', 'text="Nova Composição"', 'button:has-text("Nova Composição")', 'text="Nova composição"', 'button:has-text("Nova composição")', 'button[class*="btn"]', '.btn-primary', 'button[style*="background"]', 'button:text("NOVA")', 'button:text("Nova")', 'a:text("NOVA COMPOSIÇÃO")', 'button', '[role="button"]' ]; const results = {}; for (const selector of mcpSelectors) { try { const elements = await this.page.$$(selector); const elementDetails = []; for (let i = 0; i < Math.min(elements.length, 3); i++) { const element = elements[i]; const text = await element.textContent(); const isVisible = await element.isVisible(); const isEnabled = await element.isEnabled(); const boundingBox = await element.boundingBox(); elementDetails.push({ text: text?.trim(), visible: isVisible, enabled: isEnabled, boundingBox: boundingBox }); if (isVisible && isEnabled && text && text.includes('Nova')) { console.log(`✅ Found viable Nova Composição selector: ${selector} - "${text.trim()}"`); } } results[selector] = { found: elements.length > 0, count: elements.length, elements: elementDetails }; } catch (error) { results[selector] = { found: false, error: error.message }; } } this.results.selectors.novaComposicao = results; return results; } /** * Scan and catalog ALL page elements for analysis */ async scanAllPageElements() { console.log('🔍 Scanning all page elements...'); try { const pageData = await this.page.evaluate(() => { const results = { allButtons: [], allLinks: [], allInteractive: [], pageText: document.body.textContent?.substring(0, 500) || 'No text content', totalElements: 0 }; // Scan all buttons document.querySelectorAll('button').forEach((el, index) => { const rect = el.getBoundingClientRect(); if (rect.width > 0 && rect.height > 0) { results.allButtons.push({ index, text: el.textContent?.trim() || '', className: el.className || '', id: el.id || '', visible: rect.width > 0 && rect.height > 0, ariaLabel: el.getAttribute('aria-label') || '', title: el.title || '', boundingBox: { x: Math.round(rect.x), y: Math.round(rect.y), width: Math.round(rect.width), height: Math.round(rect.height) } }); } }); // Scan all links document.querySelectorAll('a').forEach((el, index) => { const rect = el.getBoundingClientRect(); if (rect.width > 0 && rect.height > 0) { results.allLinks.push({ index, text: el.textContent?.trim() || '', href: el.href || '', className: el.className || '', id: el.id || '' }); } }); // Scan all interactive elements document.querySelectorAll('[onclick], [role="button"], input, select, textarea').forEach((el, index) => { const rect = el.getBoundingClientRect(); if (rect.width > 0 && rect.height > 0) { results.allInteractive.push({ index, tagName: el.tagName.toLowerCase(), text: el.textContent?.trim().substring(0, 50) || '', className: el.className || '', id: el.id || '', type: el.type || '', role: el.role || '' }); } }); results.totalElements = results.allButtons.length + results.allLinks.length + results.allInteractive.length; return results; }); this.results.pageElements = pageData; console.log(`📊 Found ${pageData.totalElements} interactive elements:`); console.log(` - ${pageData.allButtons.length} buttons`); console.log(` - ${pageData.allLinks.length} links`); console.log(` - ${pageData.allInteractive.length} other interactive elements`); return pageData; } catch (error) { this.results.errors.push(`Page scan failed: ${error.message}`); return null; } } /** * Generate targeted recommendations based on findings */ generateRecommendations() { console.log('💡 Generating targeted recommendations...'); const recommendations = []; // Check authentication if (this.results.authentication.jwtProvided) { recommendations.push({ type: 'success', category: 'Authentication', message: 'JWT authentication successful', details: `Navigation status: ${this.results.authentication.navigationStatus}`, action: 'Authentication working correctly' }); } // Check Nova Composição selectors const novaResults = this.results.selectors.novaComposicao || {}; const workingSelectors = Object.entries(novaResults) .filter(([_, result]) => result.found && result.elements?.some(el => el.visible && el.enabled)) .map(([selector, _]) => selector); if (workingSelectors.length > 0) { recommendations.push({ type: 'success', category: 'Nova Composição Selectors', message: `Found ${workingSelectors.length} working selectors`, selectors: workingSelectors.slice(0, 5), // Top 5 working selectors action: 'Update MCP server to use the most reliable selector' }); } else { recommendations.push({ type: 'warning', category: 'Nova Composição Selectors', message: 'No working selectors found with current strategy', action: 'Review page elements to identify correct button selector' }); } // Check page elements if (this.results.pageElements) { const buttonTexts = this.results.pageElements.allButtons .filter(btn => btn.text && btn.visible) .map(btn => btn.text) .slice(0, 10); if (buttonTexts.length > 0) { recommendations.push({ type: 'info', category: 'Available Page Buttons', message: `Found ${buttonTexts.length} visible buttons`, buttons: buttonTexts, action: 'Review button texts to identify composition creation button' }); } } this.results.recommendations = recommendations; return recommendations; } /** * Run complete authenticated diagnostic */ async runDiagnostic() { try { const initialized = await this.initialize(); if (!initialized) { throw new Error('Failed to initialize browser'); } const authenticated = await this.navigateWithAuth(); if (!authenticated) { throw new Error('Failed to authenticate and navigate'); } // Wait for page to settle await this.page.waitForTimeout(3000); // Run diagnostic tests await this.testNovaComposicaoSelectors(); await this.scanAllPageElements(); // Generate targeted recommendations this.generateRecommendations(); // Create comprehensive report await this.generateReport(); console.log('✅ Authenticated diagnostic complete!'); console.log('📄 Reports generated:'); console.log(' - authenticated-diagnostic-report.json (detailed data)'); console.log(' - authenticated-diagnostic-summary.md (human readable)'); } catch (error) { console.error('❌ Diagnostic failed:', error.message); this.results.errors.push(`Diagnostic failed: ${error.message}`); } finally { if (this.browser) { await this.browser.close(); } } } /** * Generate comprehensive report */ async generateReport() { const report = { ...this.results, summary: { totalSelectors: Object.keys(this.results.selectors).length, totalErrors: this.results.errors.length, totalRecommendations: this.results.recommendations.length, authenticationWorking: !!this.results.authentication.jwtProvided, completedAt: new Date().toISOString() } }; await writeFile( './authenticated-diagnostic-report.json', JSON.stringify(report, null, 2) ); const summary = this.generateHumanReadableSummary(report); await writeFile('./authenticated-diagnostic-summary.md', summary); } /** * Generate human-readable summary */ generateHumanReadableSummary(report) { return `# Authenticated DOM Selector Diagnostic Report **Generated:** ${report.summary.completedAt} **URL:** ${report.url} **Authentication:** ${report.summary.authenticationWorking ? '✅ Working' : '❌ Failed'} ## Executive Summary - Authentication: ${report.summary.authenticationWorking ? 'SUCCESS' : 'FAILED'} - Selectors Tested: ${report.summary.totalSelectors} - Errors: ${report.summary.totalErrors} - Recommendations: ${report.summary.totalRecommendations} ## Key Findings ### Authentication Status ${report.authentication.jwtProvided ? `✅ JWT authentication successful (Status: ${report.authentication.navigationStatus})` : '❌ JWT authentication failed' } ### Nova Composição Selector Analysis ${Object.entries(report.selectors.novaComposicao || {}) .filter(([_, result]) => result.found && result.elements?.some(el => el.visible && el.enabled)) .map(([selector, result]) => `✅ **${selector}** - Found ${result.count} elements`) .join('\n') || '❌ No working selectors identified'} ### Available Page Buttons ${report.pageElements?.allButtons ?.filter(btn => btn.visible && btn.text) ?.slice(0, 10) ?.map(btn => `- "${btn.text}" (${btn.className || 'no class'})`) ?.join('\n') || 'No buttons detected'} ## Recommendations ${report.recommendations.map(rec => ` ### ${rec.category} **Type:** ${rec.type} **Message:** ${rec.message} **Action:** ${rec.action} ${rec.selectors ? `**Working Selectors:** ${rec.selectors.join(', ')}` : ''} ${rec.buttons ? `**Available Buttons:** ${rec.buttons.join(', ')}` : ''} `).join('\n')} ## Next Steps 1. Review working selectors and update MCP server code 2. Test with identified button selectors 3. Verify composition creation workflow 4. Update browser automation error handling --- *This report provides the data needed to fix MCP server browser automation* `; } } // Main execution async function main() { const diagnostic = new AuthenticatedDOMDiagnostic(); await diagnostic.runDiagnostic(); } // Export for use in other modules export { AuthenticatedDOMDiagnostic }; // Run if called directly if (import.meta.url === `file://${process.argv[1]}`) { main().catch(console.error); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rkm097git/euconquisto-composer-mcp-poc'

If you have feedback or need assistance with the MCP directory API, please join our Discord server