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
JavaScript
;
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;