Sistema completo per automatizzare submit di form su siti web usando Playwright con debugging intelligente via page dumps
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

270 lines
8.7 KiB

const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');
// ========================================
// CONFIGURATION - Customize for your site
// ========================================
const SITE_URL = 'https://example.com';
const LOGIN_REQUIRED = true;
const LOGIN_CREDENTIALS = {
username: 'your_username',
password: 'your_password'
};
const FORM_DATA = {
field1: 'Value 1',
field2: 'Value 2',
description: 'Long description text here...'
};
// ========================================
// CORE UTILITY: Page Dumping System
// ========================================
/**
* Dumps page information for debugging:
* - Full HTML content
* - Screenshot (full page)
* - All form elements with their properties
*
* Use this BEFORE filling forms and AFTER errors
*/
async function dumpPageInfo(page, prefix) {
const timestamp = Date.now();
const dumpDir = path.join(__dirname, 'page-dumps');
// Create dumps directory if not exists
if (!fs.existsSync(dumpDir)) {
fs.mkdirSync(dumpDir, { recursive: true });
}
console.log('🔍 Dumping page for debugging...');
try {
// 1. Save full HTML
const html = await page.content();
const htmlPath = path.join(dumpDir, `${prefix}-${timestamp}.html`);
fs.writeFileSync(htmlPath, html);
console.log(` 📄 HTML saved: ${htmlPath}`);
// 2. Take screenshot
const screenshotPath = path.join(dumpDir, `${prefix}-${timestamp}.png`);
await page.screenshot({
path: screenshotPath,
fullPage: true
});
console.log(` 📸 Screenshot saved: ${screenshotPath}`);
// 3. Extract all form elements
const elements = await page.$$eval('input, textarea, select, button', els =>
els.map(el => ({
tag: el.tagName.toLowerCase(),
type: el.type || '',
name: el.name || '',
id: el.id || '',
placeholder: el.placeholder || '',
value: el.value || '',
class: el.className || ''
}))
);
const selectorsPath = path.join(dumpDir, `${prefix}-${timestamp}-selectors.txt`);
fs.writeFileSync(selectorsPath, JSON.stringify(elements, null, 2));
console.log(` 📋 Form elements saved: ${selectorsPath}`);
} catch (error) {
console.error(`❌ Error during page dump: ${error.message}`);
}
}
// ========================================
// LOGIN HANDLER with Fallback
// ========================================
async function handleLogin(page, credentials) {
console.log('🔐 Attempting automatic login...');
try {
// Dump login page first to understand structure
await dumpPageInfo(page, 'login-page-initial');
// Fill login form
await page.fill('input[name="username"]', credentials.username);
await page.fill('input[name="password"]', credentials.password);
// Submit (click button, don't use form.submit())
await page.click('input[type="submit"], button[type="submit"]');
// Wait for redirect after successful login
// Adjust the URL pattern to match your site's post-login URL
await page.waitForURL(/.*\/(?!login|signin)/, { timeout: 30000 });
console.log('✅ Login successful!\n');
} catch (loginError) {
console.warn('⚠ Auto-login failed, trying manual intervention...');
await dumpPageInfo(page, 'login-error');
console.log('\n⏸ Please log in manually in the browser window.');
console.log(' Waiting up to 5 minutes for manual login...\n');
// Wait for user to login manually
await page.waitForURL(/.*\/(?!login|signin)/, { timeout: 300000 });
console.log('✅ Manual login detected!\n');
}
}
// ========================================
// MAIN AUTOMATION FUNCTION
// ========================================
async function automateFormSubmission() {
console.log('🚀 Starting form automation...\n');
// Launch browser (headless: false to see what's happening)
const browser = await chromium.launch({
headless: false,
slowMo: 100 // Slow down operations for visibility
});
const context = await browser.newContext({
viewport: { width: 1920, height: 1080 }
});
const page = await context.newPage();
try {
// ========================================
// STEP 1: Navigate to site
// ========================================
console.log('📋 Step 1: Navigating to site...');
await page.goto(SITE_URL);
await page.waitForLoadState('networkidle');
// ========================================
// STEP 2: Handle login if required
// ========================================
if (LOGIN_REQUIRED) {
// Check if login is needed (adjust selector to your site)
const needsLogin = await page.locator('a:has-text("Login"), a:has-text("Sign in")').count() > 0;
if (needsLogin) {
console.log('⚠ Login required!\n');
// Navigate to login page (adjust URL)
await page.goto(`${SITE_URL}/login`);
await handleLogin(page, LOGIN_CREDENTIALS);
} else {
console.log('✅ Already logged in!\n');
}
}
// ========================================
// STEP 3: Navigate to form page
// ========================================
console.log('📋 Step 2: Navigating to form page...');
await page.goto(`${SITE_URL}/submit-form`); // Adjust URL
await page.waitForLoadState('networkidle');
// ========================================
// STEP 4: Dump page to understand structure
// ========================================
console.log('📝 Step 3: Analyzing form structure...');
await dumpPageInfo(page, 'form-initial');
// ========================================
// STEP 5: Fill form fields
// ========================================
console.log('📝 Step 4: Filling form...');
try {
// Simple text input
await page.fill('input[name="field1"]', FORM_DATA.field1);
console.log(' ✓ Field1 filled');
// Another text input
await page.fill('input[name="field2"]', FORM_DATA.field2);
console.log(' ✓ Field2 filled');
// Textarea
await page.fill('textarea[name="description"]', FORM_DATA.description);
console.log(' ✓ Description filled');
// Example: Select dropdown (standard)
// await page.selectOption('select[name="category"]', 'option-value');
// console.log(' ✓ Category selected');
// Example: Select2 dropdown (jQuery plugin)
// await page.click('span.select2-selection--multiple');
// await page.waitForTimeout(500);
// await page.click('li.select2-results__option:has-text("Option Name")');
// console.log(' ✓ Select2 option selected');
// Example: Checkbox
// await page.check('input[name="agree"]');
// console.log(' ✓ Checkbox checked');
// Example: File upload
// await page.setInputFiles('input[type="file"]', '/path/to/file.png');
// console.log(' ✓ File uploaded');
} catch (fillError) {
console.error(`❌ Error filling form: ${fillError.message}`);
await dumpPageInfo(page, 'form-fill-error');
throw fillError;
}
// ========================================
// STEP 6: Review before submit
// ========================================
console.log('\n⏸ Form filled! Review in browser and press Enter to submit...');
await new Promise(resolve => {
process.stdin.once('data', resolve);
});
// ========================================
// STEP 7: Submit form
// ========================================
console.log('📤 Step 5: Submitting form...');
await page.click('button[type="submit"], input[type="submit"]');
// Wait for success page (adjust URL pattern)
await page.waitForURL(/success|confirmation|thank-you/, { timeout: 30000 });
console.log('✅ Form submitted successfully!\n');
// Take final screenshot
await page.screenshot({
path: path.join(__dirname, 'page-dumps', 'success.png'),
fullPage: true
});
} catch (error) {
console.error(`\n❌ Automation failed: ${error.message}\n`);
await dumpPageInfo(page, 'fatal-error');
throw error;
} finally {
console.log('🏁 Closing browser...');
await browser.close();
}
}
// ========================================
// RUN AUTOMATION
// ========================================
if (require.main === module) {
automateFormSubmission()
.then(() => {
console.log('✅ Automation completed successfully!');
process.exit(0);
})
.catch(error => {
console.error('❌ Automation failed:', error);
process.exit(1);
});
}
module.exports = { automateFormSubmission, dumpPageInfo };