content: replace scaffolding with bilingual pages and case studies

Ship the final German and English copy, compose every route from shared page primitives, and cover claims, parity and contact briefing in tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-25 17:34:04 +02:00
parent 9ea2885c7b
commit d954361724
95 changed files with 4161 additions and 419 deletions

View File

@@ -0,0 +1,79 @@
import { TestBed } from '@angular/core/testing';
import { SITE_CONTENT_DATA } from '../../core/content/site-content';
import { SITE_CONFIG } from '../../core/content/site-config';
import { ContactBriefing } from './contact-briefing';
function setControl(root: HTMLElement, id: string, value: string): void {
const control = root.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(
`#${id}`,
);
expect(control).toBeTruthy();
control!.value = value;
control!.dispatchEvent(new Event('input', { bubbles: true }));
}
describe('ContactBriefing', () => {
it('binds labels, builds an encoded mailto link and never talks to a server', async () => {
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(new Response());
const xhrSpy = vi.spyOn(XMLHttpRequest.prototype, 'send').mockImplementation(() => undefined);
await TestBed.configureTestingModule({
imports: [ContactBriefing],
}).compileComponents();
const fixture = TestBed.createComponent(ContactBriefing);
const copy = SITE_CONTENT_DATA.de.contact;
fixture.componentRef.setInput('copy', copy);
await fixture.whenStable();
const root = fixture.nativeElement as HTMLElement;
for (const field of copy.fields) {
const controlId = `contact-${field.id}`;
const label = root.querySelector(`label[for="${controlId}"]`);
const control = root.querySelector(`#${controlId}`);
expect(label?.textContent).toContain(field.label);
expect(control).toBeTruthy();
}
expect(root.querySelector('form[action]')).toBeNull();
expect(root.textContent).toContain(copy.incompleteHint);
expect(root.querySelector(`a[href="${SITE_CONFIG.calendarUrl}"]`)).toBeNull();
expect(root.textContent).not.toContain(copy.calendarLabel);
const special = 'Äpfel & Birnen?\nPreis=100';
setControl(root, 'contact-name', special);
setControl(root, 'contact-email', 'team@example.com');
setControl(root, 'contact-company', 'Rösterei & Co');
setControl(root, 'contact-projectType', 'software');
setControl(root, 'contact-situation', special);
setControl(root, 'contact-timeframe', 'Q3');
fixture.detectChanges();
expect(root.textContent).not.toContain(copy.incompleteHint);
const submit = root.querySelector<HTMLAnchorElement>('a.submit');
expect(submit?.getAttribute('href')?.startsWith('mailto:info@antoniolede.de?subject=')).toBe(
true,
);
const href = submit?.getAttribute('href') ?? '';
expect(href.includes(' ')).toBe(false);
expect(href.includes('\n')).toBe(false);
const query = href.slice(href.indexOf('?') + 1);
const [subjectPart, bodyPart] = query.split('&body=');
expect(subjectPart.startsWith('subject=')).toBe(true);
expect(subjectPart.includes('&')).toBe(false);
expect(bodyPart.includes('&')).toBe(false);
const decodedBody = decodeURIComponent(bodyPart);
expect(decodedBody).toContain(special);
expect(decodedBody).toContain('team@example.com');
expect(decodedBody).toContain('Rösterei & Co');
expect(decodeURIComponent(subjectPart.slice('subject='.length))).toBe(copy.mailSubject);
expect(fetchSpy).not.toHaveBeenCalled();
expect(xhrSpy).not.toHaveBeenCalled();
});
});