UNPKG

chromancer

Version:

A powerful command-line interface for automating Chrome browser using Playwright. Perfect for web scraping, automation, testing, and browser workflows.

179 lines (178 loc) 7.25 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const core_1 = require("@oclif/core"); const base_js_1 = require("../base.js"); const selectors_js_1 = require("../utils/selectors.js"); const errors_js_1 = require("../utils/errors.js"); class Wait extends base_js_1.BaseCommand { static description = 'Wait for elements or conditions before proceeding'; static examples = [ '<%= config.bin %> <%= command.id %> --selector "#dynamic-content"', '<%= config.bin %> <%= command.id %> --selector ".loading" --hidden', '<%= config.bin %> <%= command.id %> --condition "document.readyState === \'complete\'"', '<%= config.bin %> <%= command.id %> --page-load', '<%= config.bin %> <%= command.id %> --network-idle', '<%= config.bin %> <%= command.id %> --url "https://example.com"', ]; static flags = { ...base_js_1.BaseCommand.baseFlags, selector: core_1.Flags.string({ char: 's', description: 'CSS selector to wait for', }), condition: core_1.Flags.string({ char: 'c', description: 'JavaScript condition to wait for', }), visible: core_1.Flags.boolean({ description: 'Wait for element to be visible', default: false, }), hidden: core_1.Flags.boolean({ description: 'Wait for element to be hidden', default: false, }), timeout: core_1.Flags.integer({ char: 't', description: 'Maximum time to wait in milliseconds', default: 30000, }), 'page-load': core_1.Flags.boolean({ description: 'Wait for page load to complete', default: false, }), 'network-idle': core_1.Flags.boolean({ description: 'Wait for network to be idle', default: false, }), url: core_1.Flags.string({ description: 'Wait for URL to match (supports partial match)', }), text: core_1.Flags.string({ description: 'Wait for text to appear in the page', }), }; async run() { const { flags } = await this.parse(Wait); await this.connectToChrome(flags.port, flags.host, flags.launch, flags.profile, flags.headless, flags.verbose, flags.keepOpen); if (!this.page) { this.error('Failed to connect to Chrome'); } await this.executeCommand(this.page); } async executeCommand(page) { const { flags } = await this.parse(Wait); const waitPromises = []; const waitDescriptions = []; // Wait for selector with visibility state if (flags.selector) { if (flags.visible) { waitPromises.push((0, selectors_js_1.waitForElement)(page, flags.selector, { state: 'visible', timeout: flags.timeout })); waitDescriptions.push(`element "${flags.selector}" to be visible`); } else if (flags.hidden) { waitPromises.push((0, selectors_js_1.waitForElement)(page, flags.selector, { state: 'hidden', timeout: flags.timeout })); waitDescriptions.push(`element "${flags.selector}" to be hidden`); } else { waitPromises.push((0, selectors_js_1.waitForElement)(page, flags.selector, { timeout: flags.timeout })); waitDescriptions.push(`element "${flags.selector}"`); } } // Wait for custom condition if (flags.condition) { waitPromises.push(page.waitForFunction(flags.condition, { timeout: flags.timeout, })); waitDescriptions.push(`condition "${flags.condition}"`); } // Wait for page load if (flags['page-load']) { waitPromises.push(page.waitForLoadState('load', { timeout: flags.timeout, })); waitDescriptions.push('page load'); } // Wait for network idle if (flags['network-idle']) { waitPromises.push(page.waitForLoadState('networkidle', { timeout: flags.timeout, })); waitDescriptions.push('network idle'); } // Wait for URL if (flags.url) { waitPromises.push(page.waitForURL(flags.url, { timeout: flags.timeout, })); waitDescriptions.push(`URL to match "${flags.url}"`); } // Wait for text if (flags.text) { waitPromises.push(page.waitForFunction((text) => document.body.textContent?.includes(text), flags.text, { timeout: flags.timeout })); waitDescriptions.push(`text "${flags.text}" to appear`); } // If no wait conditions specified, show error if (waitPromises.length === 0) { this.error('No wait condition specified. Use --selector, --condition, --page-load, --network-idle, --url, or --text'); } try { this.log(`⏳ Waiting for ${waitDescriptions.join(' and ')}...`); // Wait for all conditions await Promise.all(waitPromises); if (waitDescriptions.length === 1) { if (flags.selector && flags.visible) { this.log(`✅ Element is visible: ${flags.selector}`); } else if (flags.selector && flags.hidden) { this.log(`✅ Element is hidden: ${flags.selector}`); } else if (flags.selector) { this.log(`✅ Element found: ${flags.selector}`); } else if (flags.condition) { this.log(`✅ Condition met: ${flags.condition}`); } else if (flags['page-load']) { this.log('✅ Page loaded'); } else if (flags['network-idle']) { this.log('✅ Network idle'); } else if (flags.url) { this.log(`✅ URL matched: ${page.url()}`); } else if (flags.text) { this.log(`✅ Text found: "${flags.text}"`); } } else { this.log(`✅ All conditions met`); } // Log additional info if verbose if (flags.verbose) { this.logVerbose('Wait completed', { conditions: waitDescriptions, currentUrl: page.url(), title: await page.title(), }); } } catch (error) { if ((0, errors_js_1.isTimeoutError)(error)) { this.error(`Timeout waiting for ${waitDescriptions.join(' and ')} (${flags.timeout}ms)`); } const commandError = (0, errors_js_1.handleCommandError)(error, 'wait'); this.error(commandError.message); } } } exports.default = Wait;