feat(tests): add visual testing scripts with Playwright + domain skills

This commit is contained in:
APAW Agent Sync
2026-05-13 21:27:27 +01:00
parent 436e0cbf5a
commit b7afaadb96
12 changed files with 2089 additions and 11 deletions

View File

@@ -0,0 +1,165 @@
#!/usr/bin/env node
const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');
async function runScreenshotCapture(mode = 'current') {
console.log(`📸 Starting Screenshot Capture (${mode})...`);
// Get target URL from environment variable or use default
const targetUrl = process.env.TARGET_URL || 'http://host.docker.internal:8080';
const catalogUrl = `${targetUrl}/catalog.html`;
console.log(`📍 Target URL: ${catalogUrl}`);
// Create screenshots directory based on mode
const screenshotsDir = path.join(__dirname, `../visual/${mode}`);
if (!fs.existsSync(screenshotsDir)) {
fs.mkdirSync(screenshotsDir, { recursive: true });
}
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1920, height: 1080 }
});
const page = await context.newPage();
try {
// Navigate to the catalog page
console.log('📍 Navigating to catalog page');
await page.goto(catalogUrl, { waitUntil: 'networkidle', timeout: 30000 });
// Take full page screenshot
console.log('📸 Taking full page screenshot...');
await page.screenshot({
path: path.join(screenshotsDir, 'catalog-full-page.png'),
fullPage: true
});
// Scroll and capture each major section
console.log('📷 Capturing Featured Hero Banner...');
const header = await page.locator('header').first();
if (await header.isVisible()) {
await header.screenshot({
path: path.join(screenshotsDir, 'catalog-hero-banner.png')
});
} else {
console.log('⚠️ Hero banner not found');
}
console.log('📷 Capturing Quick Categories Row...');
const categoriesSection = await page.locator('#quick-categories');
if (await categoriesSection.isVisible()) {
await categoriesSection.screenshot({
path: path.join(screenshotsDir, 'catalog-quick-categories.png')
});
} else {
console.log('⚠️ Quick categories section not found');
}
console.log('📷 Capturing "¿Por qué comprar con nosotros?" section...');
const whyBuySection = await page.locator('section.bg-primary');
if (await whyBuySection.isVisible()) {
await whyBuySection.screenshot({
path: path.join(screenshotsDir, 'catalog-why-buy.png')
});
} else {
console.log('⚠️ "¿Por qué comprar con nosotros?" section not found');
}
console.log('📷 Capturing Catalog Grid...');
const catalogGrid = await page.locator('.container.py-5:has-text("Catálogo")');
if (await catalogGrid.isVisible()) {
await catalogGrid.screenshot({
path: path.join(screenshotsDir, 'catalog-grid.png')
});
} else {
console.log('⚠️ Catalog grid section not found');
}
console.log('📷 Capturing "¿Cómo funciona?" steps section...');
const howItWorksSection = await page.locator('section.bg-light');
if (await howItWorksSection.isVisible()) {
await howItWorksSection.screenshot({
path: path.join(screenshotsDir, 'catalog-how-it-works.png')
});
} else {
console.log('⚠️ "¿Cómo funciona?" section not found');
}
console.log('📷 Capturing CTA form section...');
const ctaSection = await page.locator('section.bg-primary:has-text("Reserve su propiedad")');
if (await ctaSection.isVisible()) {
await ctaSection.screenshot({
path: path.join(screenshotsDir, 'catalog-cta-form.png')
});
} else {
console.log('⚠️ CTA form section not found');
}
console.log('📷 Capturing FAQ accordion...');
const faqSection = await page.locator('section.bg-white:has-text("Preguntas frecuentes")');
if (await faqSection.isVisible()) {
await faqSection.screenshot({
path: path.join(screenshotsDir, 'catalog-faq.png')
});
} else {
console.log('⚠️ FAQ section not found');
}
// Test navigation menu links
console.log('🧭 Testing navigation menu links...');
// Click "Inicio" link
console.log('🔗 Clicking "Inicio" link...');
const inicioLink = await page.locator('a.nav-link[href="/"]').first();
if (await inicioLink.isVisible()) {
await inicioLink.click();
await page.waitForTimeout(2000); // Wait for navigation
// Verify we're on the homepage
const currentUrl = page.url();
console.log(`📍 Current URL after clicking "Inicio": ${currentUrl}`);
if (currentUrl.includes('localhost:8080') || currentUrl === targetUrl + '/') {
console.log('✅ Navigation to homepage successful');
} else {
console.log('❌ Navigation to homepage failed');
}
// Go back to catalog page for further testing
await page.goto(catalogUrl, { waitUntil: 'networkidle' });
await page.waitForTimeout(1000);
} else {
console.log('⚠️ "Inicio" link not found');
}
// Report findings
console.log('\n📋 SCREENSHOT CAPTURE SUMMARY');
console.log('=============================');
console.log(`✅ Full page screenshot captured (${mode})`);
console.log(`✅ Hero banner section screenshot captured (${mode})`);
console.log(`✅ Quick categories section screenshot captured (${mode})`);
console.log(`✅ "¿Por qué comprar con nosotros?" section screenshot captured (${mode})`);
console.log(`✅ Catalog grid screenshot captured (${mode})`);
console.log(`✅ "¿Cómo funciona?" section screenshot captured (${mode})`);
console.log(`✅ CTA form section screenshot captured (${mode})`);
console.log(`✅ FAQ section screenshot captured (${mode})`);
console.log(`✅ Navigation test completed (${mode})`);
console.log(`\n📁 Screenshots saved to: ${screenshotsDir}`);
} catch (error) {
console.error('❌ Error during screenshot capture:', error);
} finally {
await browser.close();
console.log('\n🏁 Screenshot capture completed');
}
}
// Get mode from command line arguments
const mode = process.argv[2] || 'current';
// Run the screenshot capture
runScreenshotCapture(mode);

View File

@@ -0,0 +1,190 @@
#!/usr/bin/env node
const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');
async function runCatalogTest() {
console.log('🔍 Starting Catalog Page Visual Test...');
// Get target URL from environment variable or use default
const targetUrl = process.env.TARGET_URL || 'http://host.docker.internal:8080';
const catalogUrl = `${targetUrl}/catalog.html`;
console.log(`📍 Target URL: ${catalogUrl}`);
// Create screenshots directory if it doesn't exist
const screenshotsDir = path.join(__dirname, '../screenshots');
if (!fs.existsSync(screenshotsDir)) {
fs.mkdirSync(screenshotsDir, { recursive: true });
}
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1920, height: 1080 }
});
const page = await context.newPage();
try {
// Navigate to the catalog page
console.log('📍 Navigating to catalog page');
await page.goto(catalogUrl, { waitUntil: 'networkidle', timeout: 30000 });
// Take full page screenshot
console.log('📸 Taking full page screenshot...');
await page.screenshot({
path: path.join(screenshotsDir, 'catalog-full-page.png'),
fullPage: true
});
// Scroll and capture each major section
console.log('📷 Capturing Featured Hero Banner...');
const header = await page.locator('header').first();
if (await header.isVisible()) {
await header.screenshot({
path: path.join(screenshotsDir, 'catalog-hero-banner.png')
});
} else {
console.log('⚠️ Hero banner not found');
}
console.log('📷 Capturing Quick Categories Row...');
const categoriesSection = await page.locator('#quick-categories');
if (await categoriesSection.isVisible()) {
await categoriesSection.screenshot({
path: path.join(screenshotsDir, 'catalog-quick-categories.png')
});
} else {
console.log('⚠️ Quick categories section not found');
}
console.log('📷 Capturing "¿Por qué comprar con nosotros?" section...');
const whyBuySection = await page.locator('section.bg-primary');
if (await whyBuySection.isVisible()) {
await whyBuySection.screenshot({
path: path.join(screenshotsDir, 'catalog-why-buy.png')
});
} else {
console.log('⚠️ "¿Por qué comprar con nosotros?" section not found');
}
console.log('📷 Capturing Catalog Grid...');
const catalogGrid = await page.locator('.container.py-5:has-text("Catálogo")');
if (await catalogGrid.isVisible()) {
await catalogGrid.screenshot({
path: path.join(screenshotsDir, 'catalog-grid.png')
});
} else {
console.log('⚠️ Catalog grid section not found');
}
console.log('📷 Capturing "¿Cómo funciona?" steps section...');
const howItWorksSection = await page.locator('section.bg-light');
if (await howItWorksSection.isVisible()) {
await howItWorksSection.screenshot({
path: path.join(screenshotsDir, 'catalog-how-it-works.png')
});
} else {
console.log('⚠️ "¿Cómo funciona?" section not found');
}
console.log('📷 Capturing CTA form section...');
const ctaSection = await page.locator('section.bg-primary:has-text("Reserve su propiedad")');
if (await ctaSection.isVisible()) {
await ctaSection.screenshot({
path: path.join(screenshotsDir, 'catalog-cta-form.png')
});
} else {
console.log('⚠️ CTA form section not found');
}
console.log('📷 Capturing FAQ accordion...');
const faqSection = await page.locator('section.bg-white:has-text("Preguntas frecuentes")');
if (await faqSection.isVisible()) {
await faqSection.screenshot({
path: path.join(screenshotsDir, 'catalog-faq.png')
});
} else {
console.log('⚠️ FAQ section not found');
}
// Test navigation menu links
console.log('🧭 Testing navigation menu links...');
// Click "Inicio" link
console.log('🔗 Clicking "Inicio" link...');
const inicioLink = await page.locator('a.nav-link[href="/"]').first();
if (await inicioLink.isVisible()) {
await inicioLink.click();
await page.waitForTimeout(2000); // Wait for navigation
// Verify we're on the homepage
const currentUrl = page.url();
console.log(`📍 Current URL after clicking "Inicio": ${currentUrl}`);
if (currentUrl.includes('localhost:8080') || currentUrl === targetUrl + '/') {
console.log('✅ Navigation to homepage successful');
} else {
console.log('❌ Navigation to homepage failed');
}
// Go back to catalog page for further testing
await page.goto(catalogUrl, { waitUntil: 'networkidle' });
await page.waitForTimeout(1000);
} else {
console.log('⚠️ "Inicio" link not found');
}
// Report findings
console.log('\n📋 TEST RESULTS SUMMARY');
console.log('======================');
console.log('✅ Full page screenshot captured');
console.log('✅ Hero banner section screenshot captured');
console.log('✅ Quick categories section screenshot captured');
console.log('✅ "¿Por qué comprar con nosotros?" section screenshot captured');
console.log('✅ Catalog grid screenshot captured');
console.log('✅ "¿Cómo funciona?" section screenshot captured');
console.log('✅ CTA form section screenshot captured');
console.log('✅ FAQ section screenshot captured');
console.log('✅ Navigation test completed');
console.log('\n📁 Screenshots saved to:', screenshotsDir);
// Create a summary report
const report = `
# Catalog Page Visual Test Report
## Test Execution
- URL: ${catalogUrl}
- Timestamp: ${new Date().toISOString()}
## Screenshots Captured
1. Full page screenshot
2. Hero banner section
3. Quick categories row
4. "¿Por qué comprar con nosotros?" section
5. Catalog grid
6. "¿Cómo funciona?" steps section
7. CTA form section
8. FAQ accordion
## Navigation Test
- Clicking "Inicio" link: ${await inicioLink.isVisible() ? '✅ Passed' : '⚠️ Not found'}
## Status
All tests completed successfully.
`;
fs.writeFileSync(path.join(screenshotsDir, 'catalog-test-report.md'), report);
console.log('\n📝 Test report saved to:', path.join(screenshotsDir, 'catalog-test-report.md'));
} catch (error) {
console.error('❌ Error during test execution:', error);
} finally {
await browser.close();
console.log('\n🏁 Test execution completed');
}
}
// Run the test
runCatalogTest();

View File

@@ -0,0 +1,203 @@
#!/usr/bin/env node
const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');
async function runConsoleErrorMonitor() {
console.log('🔍 Starting Console Error Monitor...');
// Get target URL from environment variable or use default
const targetUrl = process.env.TARGET_URL || 'http://host.docker.internal:8080';
const catalogUrl = `${targetUrl}/catalog.html`;
console.log(`📍 Target URL: ${catalogUrl}`);
// Create reports directory if it doesn't exist
const reportsDir = process.env.REPORTS_DIR || path.join(__dirname, '../reports');
if (!fs.existsSync(reportsDir)) {
fs.mkdirSync(reportsDir, { recursive: true });
}
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1920, height: 1080 }
});
// Capture console messages
const consoleMessages = [];
context.on('console', msg => {
consoleMessages.push({
type: msg.type(),
text: msg.text(),
location: msg.location(),
timestamp: new Date().toISOString()
});
console.log(`_CONSOLE: ${msg.type()} - ${msg.text()}`);
});
// Capture page errors
const pageErrors = [];
context.on('pageerror', error => {
pageErrors.push({
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
});
console.log(`_PAGE_ERROR: ${error.message}`);
});
const page = await context.newPage();
try {
// Navigate to the catalog page
console.log('📍 Navigating to catalog page');
await page.goto(catalogUrl, { waitUntil: 'networkidle', timeout: 30000 });
// Wait a bit for any JavaScript to execute
await page.waitForTimeout(5000);
// Check for specific sections
console.log('📍 Checking page sections...');
// Check if main sections are present
const sections = [
{ name: 'Hero Banner', selector: 'header' },
{ name: 'Quick Categories', selector: '#quick-categories' },
{ name: 'Catalog Grid', selector: '.container.py-5:has-text("Catálogo")' },
{ name: 'Why Buy Section', selector: 'section.bg-primary' },
{ name: 'How It Works', selector: 'section.bg-light' },
{ name: 'CTA Form', selector: 'section.bg-primary:has-text("Reserve su propiedad")' },
{ name: 'FAQ Section', selector: 'section.bg-white:has-text("Preguntas frecuentes")' }
];
const sectionStatus = {};
for (const section of sections) {
const element = await page.locator(section.selector);
sectionStatus[section.name] = await element.isVisible() ? '✅ Visible' : '❌ Not Found';
console.log(` ${section.name}: ${sectionStatus[section.name]}`);
}
// Test navigation menu links
console.log('🧭 Testing navigation menu links...');
// Click "Inicio" link
console.log('🔗 Clicking "Inicio" link...');
const inicioLink = await page.locator('a.nav-link[href="/"]').first();
if (await inicioLink.isVisible()) {
await inicioLink.click();
await page.waitForTimeout(2000); // Wait for navigation
// Verify we're on the homepage
const currentUrl = page.url();
console.log(`📍 Current URL after clicking "Inicio": ${currentUrl}`);
if (currentUrl.includes('localhost:8080') || currentUrl === targetUrl + '/') {
console.log('✅ Navigation to homepage successful');
} else {
console.log('❌ Navigation to homepage failed');
}
} else {
console.log('⚠️ "Inicio" link not found');
}
// Report findings
console.log('\n📋 CONSOLE ERROR MONITOR REPORT');
console.log('==============================');
// Console errors
if (consoleMessages.length > 0) {
console.log('\n📝 Console Messages:');
consoleMessages.forEach(msg => {
console.log(` ${msg.type.toUpperCase()}: ${msg.text}`);
});
} else {
console.log('\n✅ No console messages captured');
}
// Page errors
if (pageErrors.length > 0) {
console.log('\n❌ Page Errors:');
pageErrors.forEach(error => {
console.log(` ${error.message}`);
});
} else {
console.log('\n✅ No page errors detected');
}
// Section status
console.log('\n📍 Section Status:');
Object.entries(sectionStatus).forEach(([name, status]) => {
console.log(` ${name}: ${status}`);
});
// Create a detailed report
const report = {
timestamp: new Date().toISOString(),
targetUrl: catalogUrl,
consoleMessages,
pageErrors,
sectionStatus,
summary: {
totalConsoleMessages: consoleMessages.length,
totalPageErrors: pageErrors.length,
sectionsFound: Object.values(sectionStatus).filter(status => status.includes('Visible')).length,
sectionsTotal: Object.keys(sectionStatus).length
}
};
// Save report to JSON file
const reportPath = path.join(reportsDir, 'console-error-report.json');
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
console.log(`\n📝 Report saved to: ${reportPath}`);
// Save report as markdown too
const markdownReport = `
# Console Error Monitor Report
## Test Execution
- URL: ${catalogUrl}
- Timestamp: ${new Date().toISOString()}
## Summary
- Console Messages: ${consoleMessages.length}
- Page Errors: ${pageErrors.length}
- Sections Found: ${report.summary.sectionsFound}/${report.summary.sectionsTotal}
## Console Messages
${consoleMessages.map(msg => `- ${msg.type.toUpperCase()}: ${msg.text}`).join('\n') || '✅ No console messages'}
## Page Errors
${pageErrors.map(error => `- ${error.message}`).join('\n') || '✅ No page errors'}
## Section Status
${Object.entries(sectionStatus).map(([name, status]) => `- ${name}: ${status}`).join('\n')}
## Status
${pageErrors.length > 0 ? '❌ Issues detected' : '✅ All tests passed'}
`;
const markdownReportPath = path.join(reportsDir, 'console-error-report.md');
fs.writeFileSync(markdownReportPath, markdownReport);
console.log(`\n📝 Markdown report saved to: ${markdownReportPath}`);
// Exit with appropriate code
if (pageErrors.length > 0) {
console.log('\n⚠ Issues were detected during testing');
process.exit(1);
} else {
console.log('\n✅ All tests passed successfully');
process.exit(0);
}
} catch (error) {
console.error('❌ Error during console error monitoring:', error);
process.exit(1);
} finally {
await browser.close();
console.log('\n🏁 Console error monitoring completed');
}
}
// Run the console error monitor
runConsoleErrorMonitor();