Align tests and docs with Pitch, the customer Home, and the terminal dock.

Page, crawl, Playwright and Lighthouse coverage now include both pitch locales, and the contributor docs match the new route table and chrome.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-26 12:52:45 +02:00
parent 5921fc5ede
commit 359e887a7a
10 changed files with 133 additions and 42 deletions

View File

@@ -4,7 +4,6 @@ import { RouterTestingHarness } from '@angular/router/testing';
import { routes } from '../app.routes';
import { SITE_CONTENT } from '../core/content/content.token';
import { SITE_CONTENT_DATA } from '../core/content/site-content';
import { SITE_CONFIG } from '../core/content/site-config';
import { CASE_STUDY_IDS } from '../core/content/content.contracts';
import { APP_LOCALES } from '../core/i18n/locale';
import { routePath } from '../core/routing/route-paths';
@@ -67,6 +66,8 @@ describe('page rendering, semantics and accessibility', () => {
const paths = [
'/',
'/en',
'/pitch',
'/en/pitch',
'/leistungen/software',
'/en/services/software',
'/projekte',
@@ -111,7 +112,7 @@ describe('page rendering, semantics and accessibility', () => {
}
});
it('exposes case anchors, audience entries and the CV download', async () => {
it('exposes case anchors and the Rösterei proof on Home', async () => {
TestBed.configureTestingModule({
providers: [provideRouter(routes), { provide: SITE_CONTENT, useValue: SITE_CONTENT_DATA }],
});
@@ -129,37 +130,48 @@ describe('page rendering, semantics and accessibility', () => {
for (const path of ['/', '/en']) {
await harness.navigateByUrl(path);
const root = harness.routeNativeElement as HTMLElement;
expect(root.querySelector('#recruiters')).toBeTruthy();
expect(root.querySelector('#companies')).toBeTruthy();
expect(root.querySelector(`a[href="${SITE_CONFIG.cvAssetPath}"]`)).toBeTruthy();
expect(root.querySelector('#home-proof')).toBeTruthy();
expect(root.querySelector('app-case-card')).toBeTruthy();
expect(root.querySelector('#recruiters')).toBeNull();
expect(root.querySelector('#companies')).toBeNull();
}
expect(SITE_CONTENT_DATA.de.pages.notFound.hero.headline).toBeTruthy();
});
it('places the Systems Map between the profile block and the featured cases', async () => {
it('places the Systems Map on the services overview', async () => {
TestBed.configureTestingModule({
providers: [provideRouter(routes), { provide: SITE_CONTENT, useValue: SITE_CONTENT_DATA }],
});
const harness = await RouterTestingHarness.create();
for (const path of ['/leistungen', '/en/services']) {
await harness.navigateByUrl(path);
const root = harness.routeNativeElement as HTMLElement;
const map = root.querySelector('app-systems-map');
expect(map).toBeTruthy();
assertPageSemantics(root);
}
for (const path of ['/', '/en']) {
await harness.navigateByUrl(path);
const root = harness.routeNativeElement as HTMLElement;
const metrics = root.querySelector('app-metric-list');
const map = root.querySelector('app-systems-map');
const firstCase = root.querySelector('app-case-card');
expect(root.querySelector('app-systems-map')).toBeNull();
}
});
expect(metrics).toBeTruthy();
expect(map).toBeTruthy();
expect(firstCase).toBeTruthy();
it('renders pitch in both locales with a single h1', async () => {
TestBed.configureTestingModule({
providers: [provideRouter(routes), { provide: SITE_CONTENT, useValue: SITE_CONTENT_DATA }],
});
const position = metrics!.compareDocumentPosition(map!);
expect(position & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
const afterMap = map!.compareDocumentPosition(firstCase!);
expect(afterMap & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
const harness = await RouterTestingHarness.create();
for (const path of ['/pitch', '/en/pitch']) {
await harness.navigateByUrl(path);
const root = harness.routeNativeElement as HTMLElement;
expect(root.querySelectorAll('h1')).toHaveLength(1);
assertPageSemantics(root);
}
});

View File

@@ -92,6 +92,51 @@ describe('ContactBriefing', () => {
expect(xhrSpy).not.toHaveBeenCalled();
});
it('treats project type as optional and enables mailto with name, email and situation', async () => {
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;
const projectType = copy.fields.find((field) => field.id === 'projectType');
expect(projectType?.required).toBe(false);
expect(projectType?.options?.some((option) => option.value === 'unsure')).toBe(true);
expect(
SITE_CONTENT_DATA.en.contact.fields
.find((field) => field.id === 'projectType')
?.options?.some((option) => option.value === 'unsure'),
).toBe(true);
const unsure = projectType?.options?.find((option) => option.value === 'unsure');
expect(unsure?.label).toBe('Noch unklar');
expect(
SITE_CONTENT_DATA.en.contact.fields
.find((field) => field.id === 'projectType')
?.options?.find((option) => option.value === 'unsure')?.label,
).toBe('Not sure yet');
setControl(root, 'contact-name', 'Ada');
setControl(root, 'contact-email', 'ada@example.com');
setControl(root, 'contact-situation', 'Need a shop.');
fixture.detectChanges();
const submit = root.querySelector<HTMLAnchorElement>('a.submit');
expect(submit?.getAttribute('href')?.startsWith('mailto:')).toBe(true);
expect(submit?.getAttribute('href')).not.toContain(
encodeURIComponent(`${projectType!.label}:`),
);
setControl(root, 'contact-projectType', 'unsure');
fixture.detectChanges();
const withType = root.querySelector<HTMLAnchorElement>('a.submit')?.getAttribute('href') ?? '';
expect(decodeURIComponent(withType)).toContain(`${projectType!.label}: ${unsure!.label}`);
});
it('marks required fields invalid only after interaction', async () => {
await TestBed.configureTestingModule({
imports: [ContactBriefing],