feat: migrate Payload CMS to MDX and harden static infrastructure

This commit is contained in:
2026-05-04 14:48:04 +02:00
parent d51e220802
commit d3141187ee
165 changed files with 7654 additions and 16136 deletions

View File

@@ -0,0 +1,110 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { sendContactFormAction } from '../app/actions/contact';
import * as mailer from '@/lib/mail/mailer';
import { render } from '@mintel/mail';
import React from 'react';
// Mock Next.js headers
vi.mock('next/headers', () => ({
headers: () =>
Promise.resolve(
new Map([
['user-agent', 'test-agent'],
['accept-language', 'de-DE,de;q=0.9'],
['referer', 'http://localhost:3000/de/kontakt'],
['x-forwarded-for', '127.0.0.1'],
]),
),
}));
// Mock services
vi.mock('@/lib/services/create-services.server', () => ({
getServerAppServices: () => ({
logger: {
child: () => ({
info: vi.fn(),
error: vi.fn(),
warn: vi.fn(),
}),
},
analytics: {
track: vi.fn(),
setServerContext: vi.fn(),
},
errors: {
captureException: vi.fn(),
},
notifications: {
notify: vi.fn(),
},
}),
}));
// Mock mailer
vi.mock('@/lib/mail/mailer', () => ({
sendEmail: vi.fn().mockResolvedValue({ success: true, messageId: 'test-id' }),
}));
// Mock env
vi.mock('@/lib/env', () => ({
env: {
MAIL_RECIPIENTS: 'internal@mintel.me',
},
}));
describe('sendContactFormAction', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should successfully process a valid contact form submission', async () => {
const formData = new FormData();
formData.append('name', 'John Doe');
formData.append('email', 'john@example.com');
formData.append('message', 'Hello, this is a test message.');
const result = await sendContactFormAction(formData);
expect(result.success).toBe(true);
// Verify internal notification was sent
expect(mailer.sendEmail).toHaveBeenCalledWith(
expect.objectContaining({
replyTo: 'john@example.com',
subject: 'New Contact Form Submission',
}),
);
// Verify customer confirmation was sent
expect(mailer.sendEmail).toHaveBeenCalledWith(
expect.objectContaining({
to: 'john@example.com',
subject: 'Thank you for your inquiry',
}),
);
});
it('should fail if required fields are missing', async () => {
const formData = new FormData();
formData.append('name', 'John Doe');
// Missing email and message
const result = await sendContactFormAction(formData);
expect(result.success).toBe(false);
expect(result.error).toBe('Missing required fields');
expect(mailer.sendEmail).not.toHaveBeenCalled();
});
it('should skip email sending for test submissions', async () => {
const formData = new FormData();
formData.append('name', 'Tester');
formData.append('email', 'testing@mintel.me');
formData.append('message', 'Test');
const result = await sendContactFormAction(formData);
expect(result.success).toBe(true);
expect(mailer.sendEmail).not.toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,53 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import puppeteer, { Browser, Page } from 'puppeteer';
const BASE_URL = process.env.TEST_URL || 'http://localhost:3000';
describe('Contact Form E2E', () => {
let browser: Browser;
let page: Page;
let isServerUp = false;
beforeAll(async () => {
// Check if server is up
try {
const res = await fetch(`${BASE_URL}/health`);
if (res.ok) isServerUp = true;
} catch (e) {
isServerUp = false;
}
if (isServerUp) {
browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
page = await browser.newPage();
}
});
afterAll(async () => {
if (browser) await browser.close();
});
it('should submit the contact form and show success message', async ({ skip }) => {
if (!isServerUp) skip();
await page.goto(`${BASE_URL}/de/kontakt`);
// Fill the form
await page.waitForSelector('input[name="name"]');
await page.type('input[name="name"]', 'E2E Tester');
await page.type('input[name="email"]', 'testing@mintel.me');
await page.type('textarea[name="message"]', 'This is an automated E2E test message.');
// Submit
await page.click('button[type="submit"]');
// Wait for success message (localized or generic)
await page.waitForSelector('[role="alert"]', { timeout: 10000 });
const text = await page.evaluate(() => document.body.innerText);
expect(text).toMatch(/Gesendet|Sent|Erfolgreich|Success/i);
}, 30000);
});

View File

@@ -0,0 +1,23 @@
import { describe, it, expect } from 'vitest';
import fs from 'fs';
import path from 'path';
describe('Performance Standard: Image Sizes Prop', () => {
it('should ensure the Header logo has a sizes prop to prevent Next.js warnings', () => {
const headerPath = path.resolve(__dirname, '../components/Header.tsx');
const headerContent = fs.readFileSync(headerPath, 'utf-8');
// Check if the logo image has a sizes prop
// The logo uses src={logoSrc} and width={120} height={120}
const hasSizesProp = /<Image[^>]*sizes=[^>]*>/g.test(headerContent);
expect(hasSizesProp).toBe(true);
});
it('should ensure the Footer logo has a sizes prop to prevent Next.js warnings', () => {
const footerPath = path.resolve(__dirname, '../components/Footer.tsx');
const footerContent = fs.readFileSync(footerPath, 'utf-8');
const hasSizesProp = /<Image[^>]*sizes=[^>]*>/g.test(footerContent);
expect(hasSizesProp).toBe(true);
});
});

View File

@@ -0,0 +1,46 @@
import { describe, it, expect } from 'vitest';
import fs from 'fs';
import path from 'path';
const DE_JSON = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '../messages/de.json'), 'utf-8'),
);
const EN_JSON = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '../messages/en.json'), 'utf-8'),
);
describe('Translation Link Parity', () => {
it('should ensure the German termsSlug points to an existing MDX page', () => {
const slug = DE_JSON.Footer.termsSlug;
expect(slug).toBeTruthy();
const pagePath = path.resolve(__dirname, `../content/pages/${slug}.mdx`);
const exists = fs.existsSync(pagePath);
expect(exists).toBe(true);
});
it('should ensure the English termsSlug points to an existing MDX page', () => {
const slug = EN_JSON.Footer.termsSlug;
expect(slug).toBeTruthy();
const pagePath = path.resolve(__dirname, `../content/pages/${slug}.mdx`);
const exists = fs.existsSync(pagePath);
expect(exists).toBe(true);
});
it('should ensure privacyPolicySlug points to an existing MDX page for DE', () => {
const slug = DE_JSON.Footer.privacyPolicySlug;
expect(slug).toBeTruthy();
const pagePath = path.resolve(__dirname, `../content/pages/${slug}.mdx`);
expect(fs.existsSync(pagePath)).toBe(true);
});
it('should ensure privacyPolicySlug points to an existing MDX page for EN', () => {
const slug = EN_JSON.Footer.privacyPolicySlug;
expect(slug).toBeTruthy();
const pagePath = path.resolve(__dirname, `../content/pages/${slug}.mdx`);
expect(fs.existsSync(pagePath)).toBe(true);
});
});