Files
2025-11-30 08:48:52 +08:00

80 lines
2.2 KiB
JavaScript

#!/usr/bin/env node
/**
* Click an element
* Usage: node click.js --selector ".button" [--url https://example.com] [--wait-for ".result"]
* Supports both CSS and XPath selectors:
* - CSS: node click.js --selector "button.submit"
* - XPath: node click.js --selector "//button[contains(text(),'Submit')]"
*/
import { getBrowser, getPage, closeBrowser, parseArgs, outputJSON, outputError } from './lib/browser.js';
import { parseSelector, waitForElement, clickElement, enhanceError } from './lib/selector.js';
async function click() {
const args = parseArgs(process.argv.slice(2));
if (!args.selector) {
outputError(new Error('--selector is required'));
return;
}
try {
const browser = await getBrowser({
headless: args.headless !== 'false'
});
const page = await getPage(browser);
// Navigate if URL provided
if (args.url) {
await page.goto(args.url, {
waitUntil: args['wait-until'] || 'networkidle2'
});
}
// Parse and validate selector
const parsed = parseSelector(args.selector);
// Wait for element based on selector type
await waitForElement(page, parsed, {
visible: true,
timeout: parseInt(args.timeout || '5000')
});
// Set up navigation promise BEFORE clicking (in case click triggers immediate navigation)
const navigationPromise = page.waitForNavigation({
waitUntil: 'load',
timeout: 5000
}).catch(() => null); // Catch timeout - navigation may not occur
// Click element
await clickElement(page, parsed);
// Wait for optional selector after click
if (args['wait-for']) {
await page.waitForSelector(args['wait-for'], {
timeout: parseInt(args.timeout || '5000')
});
} else {
// Wait for navigation to complete (or timeout if no navigation)
await navigationPromise;
}
outputJSON({
success: true,
url: page.url(),
title: await page.title()
});
if (args.close !== 'false') {
await closeBrowser();
}
} catch (error) {
// Enhance error message with troubleshooting tips
const enhanced = enhanceError(error, args.selector);
outputError(enhanced);
process.exit(1);
}
}
click();