UNPKG

@xtest-cli/cli

Version:

CLI for xtest.ing - AI-powered test generation platform

275 lines • 10.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DualBrowserController = void 0; const playwright_1 = require("playwright"); const events_1 = require("events"); const ws_1 = require("ws"); const axios_1 = __importDefault(require("axios")); class DualBrowserController extends events_1.EventEmitter { constructor(options) { super(); this.options = options; } async start() { console.log('\nšŸš€ Starting Dual Browser Control\n'); // Start local browser await this.startLocalBrowser(); // Start cloud browser await this.startCloudBrowser(); // Connect WebSocket for real-time cloud control await this.connectWebSocket(); console.log('\nāœ… Both browsers ready for independent control!'); console.log('šŸ“ Local browser: Direct Playwright control'); console.log('ā˜ļø Cloud browser: API/WebSocket control\n'); } async startLocalBrowser() { const options = this.options.localBrowserOptions || { browserType: 'chromium', headless: false, devtools: false, slowMo: 100, }; console.log('šŸ–„ļø Starting local browser...'); const browserLauncher = { chromium: playwright_1.chromium, firefox: playwright_1.firefox, webkit: playwright_1.webkit, }[options.browserType] || playwright_1.chromium; this.localBrowser = await browserLauncher.launch({ headless: options.headless, devtools: options.devtools, slowMo: options.slowMo, }); this.localContext = await this.localBrowser.newContext({ viewport: { width: 1280, height: 720 }, }); this.localPage = await this.localContext.newPage(); console.log('āœ… Local browser started'); } async startCloudBrowser() { console.log('ā˜ļø Starting cloud browser...'); try { const response = await axios_1.default.post(`${this.options.serverUrl}/api/enhanced-browser/inspector/create`, { sessionId: `cloud-${this.options.sessionId}`, headless: false, // Cloud browser can be headed for streaming devtools: false, }, { headers: { 'Authorization': `Bearer ${this.options.apiKey}`, 'Content-Type': 'application/json', }, }); if (!response.data.success) { throw new Error(response.data.error || 'Failed to create cloud browser'); } this.cloudSessionId = response.data.sessionId; console.log(`āœ… Cloud browser started: ${this.cloudSessionId}`); // Start streaming await this.startCloudStreaming(); } catch (error) { console.error('Failed to start cloud browser:', error.message); throw error; } } async connectWebSocket() { const wsUrl = this.options.serverUrl.replace('http', 'ws') + '/cli/connect'; this.ws = new ws_1.WebSocket(wsUrl, { headers: { 'Authorization': `Bearer ${this.options.apiKey}`, 'x-session-id': this.options.sessionId, 'x-cli-version': '0.4.0', 'x-mode': 'dual-control', }, }); return new Promise((resolve, reject) => { this.ws.on('open', () => { console.log('āœ… Connected to cloud control'); this.sendMessage({ type: 'dual-control-init', data: { localSessionId: this.options.sessionId, cloudSessionId: this.cloudSessionId, }, }); resolve(undefined); }); this.ws.on('error', reject); }); } async startCloudStreaming() { try { await axios_1.default.post(`${this.options.serverUrl}/api/enhanced-browser/start-stream`, { sessionId: this.cloudSessionId }, { headers: { 'Authorization': `Bearer ${this.options.apiKey}`, 'Content-Type': 'application/json', }, }); console.log('šŸŽ„ Cloud browser streaming started'); } catch (error) { console.error('Failed to start cloud streaming:', error); } } // Navigation methods async navigate(target, url) { if (target.type === 'local' || target.type === 'both') { console.log(`[Local] Navigating to: ${url}`); await this.localPage?.goto(url); } if (target.type === 'cloud' || target.type === 'both') { console.log(`[Cloud] Navigating to: ${url}`); await this.cloudNavigate(url); } } // Click methods async click(target, selector) { if (target.type === 'local' || target.type === 'both') { console.log(`[Local] Clicking: ${selector}`); await this.localPage?.click(selector); } if (target.type === 'cloud' || target.type === 'both') { console.log(`[Cloud] Clicking: ${selector}`); await this.cloudAction('click', { selector }); } } // Type methods async type(target, selector, text) { if (target.type === 'local' || target.type === 'both') { console.log(`[Local] Typing in: ${selector}`); await this.localPage?.fill(selector, text); } if (target.type === 'cloud' || target.type === 'both') { console.log(`[Cloud] Typing in: ${selector}`); await this.cloudAction('type', { selector, text }); } } // Screenshot methods async screenshot(target, filename) { const screenshots = {}; if (target.type === 'local' || target.type === 'both') { console.log('[Local] Taking screenshot'); const localPath = filename ? `local-${filename}` : `local-screenshot-${Date.now()}.png`; screenshots.local = await this.localPage?.screenshot({ path: localPath }); console.log(`[Local] Screenshot saved: ${localPath}`); } if (target.type === 'cloud' || target.type === 'both') { console.log('[Cloud] Taking screenshot'); const cloudPath = filename ? `cloud-${filename}` : `cloud-screenshot-${Date.now()}.png`; await this.cloudAction('screenshot', { filename: cloudPath }); console.log(`[Cloud] Screenshot saved: ${cloudPath}`); } return screenshots; } // Evaluate JavaScript async evaluate(target, script) { const results = {}; if (target.type === 'local' || target.type === 'both') { console.log('[Local] Evaluating script'); results.local = await this.localPage?.evaluate(script); } if (target.type === 'cloud' || target.type === 'both') { console.log('[Cloud] Evaluating script'); results.cloud = await this.cloudAction('evaluate', { script }); } return results; } // Cloud browser control methods async cloudNavigate(url) { try { await axios_1.default.post(`${this.options.serverUrl}/api/enhanced-browser/inspector/navigate`, { sessionId: this.cloudSessionId, url, }, { headers: { 'Authorization': `Bearer ${this.options.apiKey}`, 'Content-Type': 'application/json', }, }); } catch (error) { console.error('Cloud navigation failed:', error); throw error; } } async cloudAction(action, params) { try { const response = await axios_1.default.post(`${this.options.serverUrl}/api/enhanced-browser/inspector/execute`, { sessionId: this.cloudSessionId, action, params, }, { headers: { 'Authorization': `Bearer ${this.options.apiKey}`, 'Content-Type': 'application/json', }, }); return response.data.result; } catch (error) { console.error(`Cloud action ${action} failed:`, error); throw error; } } sendMessage(message) { if (this.ws && this.ws.readyState === ws_1.WebSocket.OPEN) { this.ws.send(JSON.stringify(message)); } } // Get page info async getPageInfo(target) { const info = {}; if (target.type === 'local' || target.type === 'both') { info.local = { url: this.localPage?.url(), title: await this.localPage?.title(), }; } if (target.type === 'cloud' || target.type === 'both') { info.cloud = await this.cloudAction('pageInfo', {}); } return info; } // Wait for selector async waitForSelector(target, selector, options) { if (target.type === 'local' || target.type === 'both') { console.log(`[Local] Waiting for: ${selector}`); await this.localPage?.waitForSelector(selector, options); } if (target.type === 'cloud' || target.type === 'both') { console.log(`[Cloud] Waiting for: ${selector}`); await this.cloudAction('waitForSelector', { selector, options }); } } async stop() { console.log('\nšŸ›‘ Stopping dual browser control...'); // Close WebSocket if (this.ws) { this.ws.close(); } // Close local browser if (this.localBrowser) { await this.localBrowser.close(); console.log('āœ… Local browser closed'); } // Close cloud browser if (this.cloudSessionId) { try { await axios_1.default.delete(`${this.options.serverUrl}/api/enhanced-browser/inspector/${this.cloudSessionId}`, { headers: { 'Authorization': `Bearer ${this.options.apiKey}`, }, }); console.log('āœ… Cloud browser closed'); } catch (error) { console.error('Failed to close cloud browser:', error); } } } } exports.DualBrowserController = DualBrowserController; //# sourceMappingURL=dual-controller.js.map