Public pages no longer describe their own pipeline, case and service labels are real accessible names, and the contact briefing writes localized fields without empty optional lines. Co-authored-by: Cursor <cursoragent@cursor.com>
89 lines
3.7 KiB
TypeScript
89 lines
3.7 KiB
TypeScript
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('[aria-live="polite"]')).toBeTruthy();
|
|
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-projectType', 'software');
|
|
setControl(root, 'contact-situation', special);
|
|
setControl(root, 'contact-timeframe', 'Q3');
|
|
fixture.detectChanges();
|
|
|
|
expect(root.textContent).not.toContain(copy.incompleteHint);
|
|
expect(root.querySelector('[aria-live="polite"]')).toBeTruthy();
|
|
|
|
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');
|
|
const projectType = copy.fields.find((field) => field.id === 'projectType');
|
|
const projectTypeLabel = projectType?.options?.find(
|
|
(option) => option.value === 'software',
|
|
)?.label;
|
|
expect(projectTypeLabel).toBeTruthy();
|
|
expect(decodedBody).toContain(`${projectType!.label}: ${projectTypeLabel}`);
|
|
const company = copy.fields.find((field) => field.id === 'company');
|
|
expect(company).toBeTruthy();
|
|
expect(decodedBody).not.toContain(`${company!.label}:`);
|
|
expect(decodeURIComponent(subjectPart.slice('subject='.length))).toBe(copy.mailSubject);
|
|
|
|
expect(fetchSpy).not.toHaveBeenCalled();
|
|
expect(xhrSpy).not.toHaveBeenCalled();
|
|
});
|
|
});
|