import { chromium, firefox, webkit } from 'playwright'; import fs from 'fs/promises'; import path from 'path'; /** * PLC Streamer Browser Automation and Testing Suite * This class provides MCP-like functionality for browser automation */ class PLCStreamerBrowserAutomation { constructor() { this.browser = null; this.context = null; this.page = null; this.baseURL = 'http://localhost:5173'; this.backendURL = 'http://localhost:5000'; } /** * Initialize browser instance * @param {string} browserType - 'chromium', 'firefox', or 'webkit' * @param {object} options - Browser launch options */ async initBrowser(browserType = 'chromium', options = {}) { const browserLaunchers = { chromium: chromium, firefox: firefox, webkit: webkit }; const defaultOptions = { headless: false, slowMo: 100, args: ['--disable-web-security', '--disable-features=VizDisplayCompositor'] }; this.browser = await browserLaunchers[browserType].launch({ ...defaultOptions, ...options }); this.context = await this.browser.newContext({ viewport: { width: 1920, height: 1080 }, recordVideo: { dir: 'videos/' } }); this.page = await this.context.newPage(); // Add console logging this.page.on('console', msg => { console.log(`Browser Console [${msg.type()}]: ${msg.text()}`); }); // Add error logging this.page.on('pageerror', error => { console.error(`Browser Error: ${error.message}`); }); return this.page; } /** * Navigate to the PLC Streamer application */ async navigateToApp() { await this.page.goto(`${this.baseURL}/app`); await this.page.waitForLoadState('networkidle'); return this.page; } /** * Check if backend server is running */ async checkBackendStatus() { try { const response = await this.page.request.get(`${this.backendURL}/api/status`); return response.ok(); } catch (error) { console.error('Backend not reachable:', error.message); return false; } } /** * Monitor PLC connection status */ async monitorPLCConnection() { await this.navigateToApp(); const connectionStatus = await this.page.locator('[data-testid="connection-status"]').textContent(); console.log(`PLC Connection Status: ${connectionStatus}`); return connectionStatus; } /** * Automated configuration testing */ async testConfigurationFlow() { await this.navigateToApp(); // Navigate to configuration await this.page.click('text=Configuration'); await this.page.waitForSelector('[data-testid="configuration-panel"]'); // Test PLC configuration const ipInput = this.page.locator('input[name*="ip"]').first(); await ipInput.clear(); await ipInput.fill('192.168.1.100'); // Save configuration await this.page.click('button:has-text("Save")'); // Wait for save confirmation await this.page.waitForSelector('text*=saved', { timeout: 5000 }); console.log('Configuration test completed successfully'); return true; } /** * Automated streaming test */ async testStreamingFlow() { await this.navigateToApp(); // Navigate to streaming await this.page.click('text=Data Streaming'); await this.page.waitForSelector('[data-testid="streaming-panel"]'); // Start streaming await this.page.click('button:has-text("Start Streaming")'); // Wait a bit for streaming to initialize await this.page.waitForTimeout(3000); // Stop streaming await this.page.click('button:has-text("Stop Streaming")'); console.log('Streaming test completed successfully'); return true; } /** * Take screenshots for documentation */ async captureScreenshots() { await this.navigateToApp(); const tabs = ['Dashboard', 'Configuration', 'Data Streaming', 'Plots']; for (const tab of tabs) { await this.page.click(`text=${tab}`); await this.page.waitForTimeout(1000); const screenshot = await this.page.screenshot({ path: `screenshots/${tab.toLowerCase().replace(' ', '_')}.png`, fullPage: true }); console.log(`Screenshot saved: ${tab}`); } return true; } /** * Performance monitoring */ async monitorPerformance() { await this.navigateToApp(); // Start performance monitoring await this.page.evaluate(() => performance.mark('test-start')); // Navigate through all tabs const tabs = ['Configuration', 'Data Streaming', 'Plots', 'Dashboard']; for (const tab of tabs) { const startTime = performance.now(); await this.page.click(`text=${tab}`); await this.page.waitForLoadState('networkidle'); const endTime = performance.now(); console.log(`Tab ${tab} load time: ${endTime - startTime}ms`); } // Get performance metrics const metrics = await this.page.evaluate(() => { return JSON.stringify(performance.getEntriesByType('navigation')); }); await fs.writeFile('performance-metrics.json', metrics); console.log('Performance metrics saved'); return JSON.parse(metrics); } /** * Generate test report */ async generateReport() { const backendStatus = await this.checkBackendStatus(); const plcStatus = await this.monitorPLCConnection(); const report = { timestamp: new Date().toISOString(), backendStatus: backendStatus ? 'Online' : 'Offline', plcConnectionStatus: plcStatus, browserInfo: await this.page.evaluate(() => navigator.userAgent), url: this.page.url() }; await fs.writeFile('automation-report.json', JSON.stringify(report, null, 2)); console.log('Report generated: automation-report.json'); return report; } /** * Cleanup resources */ async cleanup() { if (this.page) await this.page.close(); if (this.context) await this.context.close(); if (this.browser) await this.browser.close(); } } // Export for use in other scripts export default PLCStreamerBrowserAutomation; // CLI usage example if (import.meta.url === `file://${process.argv[1]}`) { const automation = new PLCStreamerBrowserAutomation(); try { await automation.initBrowser('chromium', { headless: false }); console.log('Starting automated testing...'); // Run all tests await automation.testConfigurationFlow(); await automation.testStreamingFlow(); await automation.captureScreenshots(); await automation.monitorPerformance(); await automation.generateReport(); console.log('Automation completed successfully!'); } catch (error) { console.error('Automation failed:', error); } finally { await automation.cleanup(); } }