commit
914e09876f
6 changed files with 962 additions and 0 deletions
@ -0,0 +1,6 @@ |
|||||||
|
node_modules/ |
||||||
|
page-dumps/*.html |
||||||
|
page-dumps/*.png |
||||||
|
page-dumps/*.txt |
||||||
|
*.log |
||||||
|
.DS_Store |
||||||
@ -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 |
||||||
@ -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" |
||||||
|
} |
||||||
|
} |
||||||
@ -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/* |
||||||
|
``` |
||||||
Loading…
Reference in new issue