integration: add route metadata, JSON-LD and harden the public shell

Ship prerendered SEO, crawl files and palette/map a11y so every locale route is indexable, honest and keyboard-usable without inventing claims.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-25 18:28:22 +02:00
parent 2ccac6b28a
commit 46c86447d1
48 changed files with 1597 additions and 207 deletions

View File

@@ -1,9 +1,11 @@
import { ApplicationRef } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { provideRouter } from '@angular/router';
import { provideRouter, TitleStrategy } from '@angular/router';
import { App } from './app';
import { routes } from './app.routes';
import { SITE_CONTENT } from './core/content/content.token';
import { SITE_CONTENT_DATA } from './core/content/site-content';
import { SeoTitleStrategy } from './core/seo/seo-title.strategy';
describe('App', () => {
beforeEach(async () => {
@@ -11,7 +13,11 @@ describe('App', () => {
await TestBed.configureTestingModule({
imports: [App],
providers: [provideRouter(routes), { provide: SITE_CONTENT, useValue: SITE_CONTENT_DATA }],
providers: [
provideRouter(routes),
{ provide: SITE_CONTENT, useValue: SITE_CONTENT_DATA },
{ provide: TitleStrategy, useClass: SeoTitleStrategy },
],
}).compileComponents();
});
@@ -43,4 +49,27 @@ describe('App', () => {
expect(compiled.querySelector('a.language-switch[hreflang]')).toBeTruthy();
expect(compiled.querySelector('footer')).toBeTruthy();
});
it('keeps the palette trigger in the header and the dialog outside .site', async () => {
const fixture = TestBed.createComponent(App);
await fixture.whenStable();
const compiled = fixture.nativeElement as HTMLElement;
const site = compiled.querySelector('.site');
const trigger = compiled.querySelector('header .command-palette-trigger');
expect(site).toBeTruthy();
expect(trigger).toBeTruthy();
expect(site?.contains(trigger)).toBe(true);
(trigger as HTMLButtonElement).click();
fixture.detectChanges();
await fixture.whenStable();
TestBed.inject(ApplicationRef).tick();
fixture.detectChanges();
const dialog = compiled.querySelector('[role="dialog"]');
expect(dialog).toBeTruthy();
expect(site?.contains(dialog)).toBe(false);
expect(site?.hasAttribute('inert')).toBe(true);
});
});