Files
klz-cables.com/scripts/check-forms.ts
Marc Mintel 089ce13c59
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 6s
Build & Deploy / 🧪 QA (push) Successful in 2m25s
Build & Deploy / 🏗️ Build (push) Successful in 4m56s
Build & Deploy / 🚀 Deploy (push) Successful in 15s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 5m3s
Build & Deploy / 🔔 Notify (push) Successful in 3s
fix: mobile nav close button + CI Gatekeeper auth
- Add explicit close (×) button inside mobile nav overlay
  - Was unreachable because header's hamburger was behind overlay z-index
  - New button lives inside the overlay at full z-index visibility
- Fix check-forms.ts: authenticate via Gatekeeper login form
  - Old approach: inject raw password as session cookie (didn't work)
  - New approach: navigate to protected page, detect Gatekeeper gate,
    fill password form and submit to get a real server-signed session cookie
  - Fixes E2E form tests that failed because pages returned Gatekeeper HTML
2026-02-28 19:25:53 +01:00

184 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import puppeteer, { HTTPResponse } from 'puppeteer';
import axios from 'axios';
import * as cheerio from 'cheerio';
const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026';
async function main() {
console.log(`\n🚀 Starting E2E Form Submission Check for: ${targetUrl}`);
// 1. Fetch Sitemap to discover the contact page and a product page
const sitemapUrl = `${targetUrl.replace(/\/$/, '')}/sitemap.xml`;
let urls: string[] = [];
try {
console.log(`📥 Fetching sitemap from ${sitemapUrl}...`);
const response = await axios.get(sitemapUrl, {
headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` },
});
const $ = cheerio.load(response.data, { xmlMode: true });
urls = $('url loc')
.map((i, el) => $(el).text())
.get();
// Normalize to target URL instance
const urlPattern = /https?:\/\/[^\/]+/;
urls = [...new Set(urls)]
.filter((u) => u.startsWith('http'))
.map((u) => u.replace(urlPattern, targetUrl.replace(/\/$/, '')))
.sort();
} catch (err: any) {
console.error(`❌ Failed to fetch sitemap: ${err.message}`);
process.exit(1);
}
const contactUrl = urls.find((u) => u.includes('/de/kontakt'));
// Ensure we select an actual product page (depth >= 7: http://host/de/produkte/category/product)
const productUrl = urls.find(
(u) =>
u.includes('/de/produkte/') && new URL(u).pathname.split('/').filter(Boolean).length >= 4,
);
if (!contactUrl) {
console.error(`❌ Could not find contact page in sitemap. Ensure /de/kontakt exists.`);
process.exit(1);
}
if (!productUrl) {
console.error(
`❌ Could not find a product page in sitemap. Form testing requires at least one product page.`,
);
process.exit(1);
}
console.log(`✅ Discovered Contact Page: ${contactUrl}`);
console.log(`✅ Discovered Product Page: ${productUrl}`);
// 2. Launch Headless Browser
console.log(`\n🕷 Launching Puppeteer Headless Engine...`);
const browser = await puppeteer.launch({
headless: true,
executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_PATH || undefined,
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
});
const page = await browser.newPage();
// 3. Authenticate through Gatekeeper login form
console.log(`\n🛡 Authenticating through Gatekeeper...`);
try {
// Navigate to a protected page so Gatekeeper redirects us to the login screen
await page.goto(contactUrl, { waitUntil: 'networkidle0', timeout: 30000 });
// Check if we landed on the Gatekeeper login page
const isGatekeeperPage = await page.$('input[name="password"]');
if (isGatekeeperPage) {
await page.type('input[name="password"]', gatekeeperPassword);
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle0', timeout: 30000 }),
page.click('button[type="submit"]'),
]);
console.log(`✅ Gatekeeper authentication successful!`);
} else {
console.log(`✅ Already authenticated (no Gatekeeper gate detected).`);
}
} catch (err: any) {
console.error(`❌ Gatekeeper authentication failed: ${err.message}`);
await browser.close();
process.exit(1);
}
let hasErrors = false;
// 4. Test Contact Form
try {
console.log(`\n🧪 Testing Contact Form on: ${contactUrl}`);
await page.goto(contactUrl, { waitUntil: 'networkidle0', timeout: 30000 });
// Ensure React has hydrated completely
await page.waitForNetworkIdle({ idleTime: 1000, timeout: 15000 }).catch(() => {});
// Ensure form is visible and interactive
try {
// Find the form input by name
await page.waitForSelector('input[name="name"]', { visible: true, timeout: 15000 });
} catch (e) {
console.error('Failed to find Contact Form input. Page Title:', await page.title());
throw e;
}
// Fill form fields
await page.type('input[name="name"]', 'Automated E2E Test');
await page.type('input[name="email"]', 'testing@mintel.me');
await page.type(
'textarea[name="message"]',
'This is an automated test verifying the contact form submission.',
);
console.log(` Submitting Contact Form...`);
// Explicitly click submit and wait for navigation/state-change
await Promise.all([
page.waitForSelector('[role="alert"][aria-live="polite"]', { timeout: 15000 }),
page.click('button[type="submit"]'),
]);
console.log(`✅ Contact Form submitted successfully! (Success state verified)`);
} catch (err: any) {
console.error(`❌ Contact Form Test Failed: ${err.message}`);
hasErrors = true;
}
// 4. Test Product Quote Form
try {
console.log(`\n🧪 Testing Product Quote Form on: ${productUrl}`);
await page.goto(productUrl, { waitUntil: 'networkidle0', timeout: 30000 });
// Ensure React has hydrated completely
await page.waitForNetworkIdle({ idleTime: 1000, timeout: 15000 }).catch(() => {});
// The product form uses dynamic IDs, so we select by input type in the specific form context
try {
await page.waitForSelector('form input[type="email"]', { visible: true, timeout: 15000 });
} catch (e) {
console.error('Failed to find Product Quote Form input. Page Title:', await page.title());
throw e;
}
// In RequestQuoteForm, the email input is type="email" and message is a textarea.
await page.type('form input[type="email"]', 'testing@mintel.me');
await page.type(
'form textarea',
'Automated request for product quote via E2E testing framework.',
);
console.log(` Submitting Product Quote Form...`);
// Submit and wait for success state
await Promise.all([
page.waitForSelector('[role="alert"][aria-live="polite"]', { timeout: 15000 }),
page.click('form button[type="submit"]'),
]);
console.log(`✅ Product Quote Form submitted successfully! (Success state verified)`);
} catch (err: any) {
console.error(`❌ Product Quote Form Test Failed: ${err.message}`);
hasErrors = true;
}
await browser.close();
// 5. Evaluation
if (hasErrors) {
console.error(`\n🚨 IMPORTANT: Form E2E checks failed. The CI build is failing.`);
process.exit(1);
} else {
console.log(`\n🎉 SUCCESS: All form submissions arrived and handled correctly!`);
process.exit(0);
}
}
main();