Browse Source

Initial commit: Playwright form automation system with page dumping debugging

master
Matteo Benedetto 2 months ago
commit
914e09876f
  1. 371
      .github/copilot-instructions.md
  2. 6
      .gitignore
  3. 248
      README.md
  4. 23
      package.json
  5. 44
      page-dumps/.gitkeep
  6. 270
      template-automation.js

371
.github/copilot-instructions.md

@ -0,0 +1,371 @@
# Playwright Form Automation Workflow
## Overview
Sistema completo per automatizzare submit di form su siti web usando Playwright con debugging intelligente via page dumps.
## Architettura del Sistema
### 1. **Page Dumping System** (Core Feature)
Funzione centrale che estrae informazioni strutturali dalla pagina per debugging:
```javascript
async function dumpPageInfo(page, prefix) {
const timestamp = Date.now();
const dumpDir = './page-dumps';
// 1. Salva HTML completo
const html = await page.content();
fs.writeFileSync(`${dumpDir}/${prefix}-${timestamp}.html`, html);
// 2. Screenshot della pagina
await page.screenshot({
path: `${dumpDir}/${prefix}-${timestamp}.png`,
fullPage: true
});
// 3. Estrai TUTTI i form elements (input, textarea, select)
const elements = await page.$$eval('input, textarea, select', 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 || ''
}))
);
fs.writeFileSync(
`${dumpDir}/${prefix}-${timestamp}-selectors.txt`,
JSON.stringify(elements, null, 2)
);
}
```
**Quando usarlo:**
- Prima di compilare un form (per capire la struttura)
- Dopo errori di selezione/compilazione
- Per analizzare dropdown dinamici (Select2, react-select, etc.)
### 2. **Workflow Iterativo di Debugging**
```
┌─────────────────────────────────────────────┐
│ 1. ESEGUI SCRIPT │
│ node automation-script.js │
└────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ 2. ERRORE? → DUMP AUTOMATICO │
│ - HTML salvato │
│ - Screenshot salvato │
│ - Form selectors estratti │
└────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ 3. ANALISI DUMP │
│ cat page-dumps/*-selectors.txt │
│ xdg-open page-dumps/*.png │
└────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ 4. CORREGGI SELETTORI NELLO SCRIPT │
│ - Usa i nomi/id reali dai selectors.txt │
│ - Adatta logica per dropdown dinamici │
└────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ 5. REITERA (torna a step 1) │
└─────────────────────────────────────────────┘
```
### 3. **Pattern per Form Elements Comuni**
#### A. Input Text Semplici
```javascript
await page.fill('input[name="username"]', 'myuser');
await page.fill('input[name="email"]', 'user@example.com');
```
#### B. CSRF Token (Django/Rails/Laravel)
```javascript
// Automaticamente gestito dal browser, ma visibile nei dumps:
// { "type": "hidden", "name": "csrfmiddlewaretoken", "value": "..." }
// Non serve compilarlo manualmente - submit button lo include
```
#### C. Submit Button
```javascript
// PREFERISCI: click esplicito sul submit button
await page.click('input[type="submit"]');
await page.click('button[type="submit"]');
// EVITA: form.submit() non trigger eventi JavaScript
```
#### D. Select2 Dropdown (jQuery plugin)
Select2 nasconde il `<select>` originale e crea struttura dinamica.
**Problema:** `page.selectOption()` non funziona!
**Soluzione:**
```javascript
// 1. Click sul container Select2 per aprire dropdown
await page.click('span.select2-selection--multiple');
await page.waitForTimeout(1000); // Aspetta apertura
// 2. Click sull'opzione nel dropdown dinamico
await page.click('li.select2-results__option:has-text("Option Name")');
```
**Come identificarlo nei dumps:**
```json
{
"tag": "select",
"name": "field_name",
"id": "id_field_name",
"class": "select2-hidden-accessible" // ← INDICATORE
}
```
#### E. React-Select / Vue-Select
```javascript
// Click sul wrapper
await page.click('[class*="select__control"]');
// Type per filtrare
await page.keyboard.type('Option');
// Enter per selezionare
await page.keyboard.press('Enter');
```
#### F. Checkboxes
```javascript
await page.check('input[name="agree"]');
await page.uncheck('input[name="newsletter"]');
```
#### G. Radio Buttons
```javascript
await page.check('input[name="gender"][value="male"]');
```
#### H. File Upload
```javascript
await page.setInputFiles('input[type="file"]', '/path/to/file.png');
```
### 4. **Login Automation con Fallback**
```javascript
async function handleLogin(page, credentials) {
console.log('🔐 Logging in automatically...');
try {
// Tenta login automatico
await page.fill('input[name="username"]', credentials.username);
await page.fill('input[name="password"]', credentials.password);
await page.click('input[type="submit"]');
// Aspetta redirect post-login (URL cambia)
await page.waitForURL(/.*\/(?!user\/login)/, { timeout: 30000 });
console.log('✅ Login successful!\n');
} catch (loginError) {
console.warn('⚠ Auto-login failed, manual intervention needed');
await dumpPageInfo(page, 'login-page');
// Fallback: attendi login manuale
console.log('Please log in manually in the browser window...');
await page.waitForURL(/.*\/(?!user\/login)/, { timeout: 300000 });
}
}
```
### 5. **Error Handling Best Practices**
```javascript
try {
// Operazioni rischiose
await page.fill('input[name="field"]', 'value');
} catch (error) {
console.error(`❌ Error: ${error.message}`);
// SEMPRE dump in caso di errore
await dumpPageInfo(page, 'error-state');
// Re-throw per fermare esecuzione
throw error;
}
```
### 6. **Waiters Strategici**
```javascript
// ❌ EVITA: Wait fissi generici
await page.waitForTimeout(5000); // Troppo lento
// ✅ PREFERISCI: Wait condizionali
await page.waitForLoadState('networkidle'); // Aspetta caricamento
await page.waitForSelector('button[type="submit"]'); // Aspetta elemento
await page.waitForURL(/expected-url/); // Aspetta navigazione
```
### 7. **Struttura Script Template**
```javascript
const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');
// Config
const SITE_URL = 'https://example.com';
const LOGIN_CREDENTIALS = {
username: 'user',
password: 'pass'
};
const FORM_DATA = {
field1: 'value1',
field2: 'value2'
};
// Page dump function
async function dumpPageInfo(page, prefix) {
// [vedi sezione 1]
}
// Main automation
async function automateFormSubmit() {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
try {
// Step 1: Navigate
await page.goto(SITE_URL);
// Step 2: Login (se necessario)
if (await page.locator('a:has-text("Login")').count() > 0) {
await handleLogin(page, LOGIN_CREDENTIALS);
}
// Step 3: Navigate to form
await page.goto(`${SITE_URL}/submit-form`);
await page.waitForLoadState('networkidle');
// Step 4: Dump iniziale per capire struttura
await dumpPageInfo(page, 'form-initial');
// Step 5: Fill form
await page.fill('input[name="field1"]', FORM_DATA.field1);
console.log('✓ Field1 filled');
await page.fill('input[name="field2"]', FORM_DATA.field2);
console.log('✓ Field2 filled');
// Step 6: Submit
await page.click('button[type="submit"]');
await page.waitForURL(/success-page/);
console.log('✅ Form submitted successfully!');
} catch (error) {
console.error('❌ Error:', error.message);
await dumpPageInfo(page, 'error');
throw error;
} finally {
await browser.close();
}
}
// Run
automateFormSubmit().catch(console.error);
```
### 8. **Setup Progetto**
```json
{
"name": "form-automation",
"version": "1.0.0",
"dependencies": {
"playwright": "^1.40.0"
},
"scripts": {
"install-browsers": "npx playwright install chromium",
"run": "node automation-script.js"
}
}
```
**Comandi setup:**
```bash
npm install
npm run install-browsers
npm run
```
### 9. **Debugging Checklist**
Quando uno script fallisce:
1. ✅ **Controlla selectors.txt** - I nomi/id sono corretti?
2. ✅ **Guarda screenshot** - L'elemento è visibile? Serve scroll?
3. ✅ **Verifica HTML** - Ci sono iframe? Shadow DOM?
4. ✅ **Test manuale** - L'operazione funziona manualmente nel browser?
5. ✅ **Console browser** - Ci sono errori JavaScript che bloccano?
### 10. **Tips Avanzati**
#### Gestione Iframe
```javascript
const frame = page.frameLocator('iframe#myframe');
await frame.locator('input[name="field"]').fill('value');
```
#### Shadow DOM
```javascript
const shadow = await page.locator('my-component')
.evaluateHandle(el => el.shadowRoot);
await shadow.locator('button').click();
```
#### Intercept Network Requests
```javascript
await page.route('**/api/endpoint', route => {
route.fulfill({
status: 200,
body: JSON.stringify({ success: true })
});
});
```
#### Custom Cookies/Headers
```javascript
await page.setExtraHTTPHeaders({
'Authorization': 'Bearer token123'
});
await browser.newContext({
extraHTTPHeaders: { 'X-Custom': 'value' }
});
```
## Caso Studio: Lutris.net Game Submission
Vedi `publish-rats-lutris.js` nella parent folder per esempio completo con:
- Login automation con fallback manuale
- Select2 dropdown handling
- File upload
- Multi-step form navigation
- Comprehensive error handling con dumps
---
**Creato**: 25 ottobre 2025
**Testato su**: Debian GNU/Linux 13, Playwright 1.40.0, Node.js v20.19.2

6
.gitignore vendored

@ -0,0 +1,6 @@
node_modules/
page-dumps/*.html
page-dumps/*.png
page-dumps/*.txt
*.log
.DS_Store

248
README.md

@ -0,0 +1,248 @@
# Playwright Form Automation Toolkit
🤖 Sistema completo per automatizzare form submission su siti web con debugging intelligente.
## Features
**Page Dumping System** - Estrazione automatica di HTML, screenshot e form selectors
🔐 **Smart Login** - Auto-login con fallback manuale
🎯 **Error Recovery** - Dump automatico ad ogni errore
📸 **Visual Debugging** - Screenshot full-page ad ogni step
🔄 **Iterative Workflow** - Ciclo rapido test → dump → fix → retest
## Quick Start
```bash
# 1. Installa dipendenze
npm install
# 2. Installa browser Chromium
npm run install-browsers
# 3. Modifica template-automation.js con i tuoi dati
# - SITE_URL
# - LOGIN_CREDENTIALS
# - FORM_DATA
# - Selettori specifici per il tuo sito
# 4. Esegui
npm test
```
## Structure
```
playwright-form-automation/
├── .github/
│ └── copilot-instructions.md # Guida completa workflow
├── package.json # Dipendenze npm
├── template-automation.js # Template script automation
├── page-dumps/ # Output debug (auto-creata)
│ ├── form-initial-*.html
│ ├── form-initial-*.png
│ ├── form-initial-*-selectors.txt
│ └── ...
└── README.md # Questo file
```
## Core Concepts
### 1. Page Dumping
Ad ogni step importante (e ad ogni errore), lo script salva:
- **HTML completo** - Per analisi struttura DOM
- **Screenshot** - Per vedere visivamente cosa è successo
- **Form selectors** - JSON con tutti gli elementi form (input, select, textarea, button)
```javascript
await dumpPageInfo(page, 'step-name');
// Crea:
// - page-dumps/step-name-<timestamp>.html
// - page-dumps/step-name-<timestamp>.png
// - page-dumps/step-name-<timestamp>-selectors.txt
```
### 2. Iterative Debugging
```bash
# Run 1: Script fallisce
node template-automation.js
# ❌ Error: Timeout waiting for 'input[name="username"]'
# Analizza dump
cat page-dumps/login-error-*-selectors.txt
# Scopri che il campo è 'input[name="user"]' non "username"
# Fix nello script
# Cambia: await page.fill('input[name="username"]', ...)
# In: await page.fill('input[name="user"]', ...)
# Run 2: Riprova
node template-automation.js
# ✅ Success!
```
### 3. Select2 Dropdown Handling
Select2 (jQuery plugin usato da molti siti) nasconde i `<select>` standard.
**Identificazione** (in selectors.txt):
```json
{
"tag": "select",
"name": "category",
"class": "select2-hidden-accessible" // ← Questo indica Select2
}
```
**Soluzione**:
```javascript
// ❌ NON funziona
await page.selectOption('select[name="category"]', 'value');
// ✅ Funziona
await page.click('span.select2-selection--multiple'); // Apre dropdown
await page.waitForTimeout(500);
await page.click('li.select2-results__option:has-text("Option")'); // Seleziona
```
## Common Patterns
### Login con CSRF Token
```javascript
// Il token viene gestito automaticamente dal browser
// Basta compilare username/password e cliccare submit
await page.fill('input[name="username"]', 'user');
await page.fill('input[name="password"]', 'pass');
await page.click('input[type="submit"]'); // Click, NON form.submit()!
```
### File Upload
```javascript
await page.setInputFiles('input[type="file"]', '/path/to/file.png');
```
### Checkbox/Radio
```javascript
await page.check('input[name="agree"]');
await page.check('input[name="gender"][value="male"]');
```
### Wait for Navigation
```javascript
// Dopo submit, aspetta che URL cambi
await page.waitForURL(/success|confirmation/);
```
## Debugging Checklist
Script fallito? Segui questa lista:
1. ✅ **Leggi selectors.txt** - Nomi/ID corretti?
2. ✅ **Guarda screenshot** - Elemento visibile? Serve scroll?
3. ✅ **Controlla HTML** - Iframe? Shadow DOM? JavaScript che modifica DOM?
4. ✅ **Prova manualmente** - L'azione funziona a mano nel browser?
5. ✅ **Console errors** - Apri DevTools nel browser Playwright
## Examples
### Esempio 1: Form Semplice
```javascript
await page.fill('input[name="name"]', 'John Doe');
await page.fill('input[name="email"]', 'john@example.com');
await page.fill('textarea[name="message"]', 'Hello world!');
await page.click('button[type="submit"]');
```
### Esempio 2: Multi-step Form
```javascript
// Step 1
await page.fill('input[name="email"]', 'user@mail.com');
await page.click('button:has-text("Next")');
await page.waitForURL(/step-2/);
// Step 2
await page.fill('input[name="address"]', '123 Main St');
await page.click('button:has-text("Submit")');
```
### Esempio 3: Login + Form
```javascript
// Login
await page.goto('https://site.com/login');
await page.fill('input[name="username"]', 'user');
await page.fill('input[name="password"]', 'pass');
await page.click('button[type="submit"]');
await page.waitForURL(/dashboard/);
// Navigate to form
await page.goto('https://site.com/submit');
// Fill and submit
await page.fill('input[name="title"]', 'My Title');
await page.click('button[type="submit"]');
```
## Tips & Tricks
### Slow Down for Visibility
```javascript
const browser = await chromium.launch({
headless: false,
slowMo: 100 // Rallenta di 100ms ogni azione
});
```
### Custom Viewport
```javascript
const context = await browser.newContext({
viewport: { width: 1920, height: 1080 }
});
```
### Network Interception
```javascript
await page.route('**/api/spam', route => route.abort());
```
### Take Extra Screenshots
```javascript
await page.screenshot({
path: 'debug-step.png',
fullPage: true
});
```
## Troubleshooting
### "Timeout waiting for selector"
→ Elemento non esiste o ha nome diverso. Controlla `selectors.txt`.
### "Element is not visible"
→ Serve scroll: `await page.locator('selector').scrollIntoViewIfNeeded();`
### "Element is covered"
→ Modal/popup sopra. Chiudi: `await page.click('button.close-modal');`
### Select2 non funziona
→ Usa pattern "click container → click option" (vedi sopra).
### Submit non fa niente
→ Usa `page.click()` non `form.submit()`. JavaScript handlers servono click event.
## Real-World Example
Vedi nella parent folder:
- `publish-rats-lutris.js` - Script completo per pubblicare gioco su Lutris.net
- Include: login, Select2, file upload, multi-step form
## License
MIT - Free to use and modify
---
**Created**: October 25, 2025
**Author**: enne2
**Tested on**: Debian GNU/Linux 13, Node.js v20.19.2, Playwright 1.40.0

23
package.json

@ -0,0 +1,23 @@
{
"name": "playwright-form-automation",
"version": "1.0.0",
"description": "Automated form submission with Playwright - intelligent page dumping for debugging",
"main": "template-automation.js",
"scripts": {
"install-browsers": "npx playwright install chromium",
"test": "node template-automation.js",
"clean-dumps": "rm -rf page-dumps/*"
},
"keywords": [
"playwright",
"automation",
"form",
"web-scraping",
"testing"
],
"author": "enne2",
"license": "MIT",
"dependencies": {
"playwright": "^1.40.0"
}
}

44
page-dumps/.gitkeep

@ -0,0 +1,44 @@
# Playwright Form Automation - Page Dumps
This directory is automatically created by the automation scripts.
## Contents
Each dump consists of 3 files with the same timestamp:
1. **`<prefix>-<timestamp>.html`** - Full HTML content of the page
2. **`<prefix>-<timestamp>.png`** - Full-page screenshot
3. **`<prefix>-<timestamp>-selectors.txt`** - JSON array of all form elements
## How to Use
When your script fails or you need to understand page structure:
1. **Check the screenshot** - Visual representation of what the page looked like
2. **Read selectors.txt** - Find the correct selectors (name, id, class) for form elements
3. **Search HTML** - Deep dive into DOM structure if needed
## Example
```bash
# View latest selectors
cat page-dumps/*-selectors.txt | tail -100
# Open latest screenshot
xdg-open $(ls -t page-dumps/*.png | head -1)
# Search for specific field in HTML
grep -i "username" page-dumps/login-*.html
```
## Cleanup
Remove old dumps to save space:
```bash
# From parent directory
npm run clean-dumps
# Or manually
rm -rf page-dumps/*
```

270
template-automation.js

@ -0,0 +1,270 @@
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 };
Loading…
Cancel
Save