148 lines
4.8 KiB
TypeScript
148 lines
4.8 KiB
TypeScript
import React from 'react';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
import { render } from '@testing-library/react';
|
|
|
|
const page = {
|
|
url: '/de/kontakt',
|
|
props: {
|
|
locale: 'de',
|
|
supportedLocales: ['de', 'en'],
|
|
appUrl: 'https://fotospiel.app',
|
|
translations: {
|
|
marketing: {
|
|
title: 'Fotospiel',
|
|
description: 'Sammle Gastfotos für Events mit QR-Codes',
|
|
},
|
|
},
|
|
auth: {},
|
|
},
|
|
};
|
|
|
|
vi.mock('react-i18next', () => ({
|
|
useTranslation: () => ({
|
|
t: (key: string, fallback?: string | { defaultValue?: unknown }) => {
|
|
if (typeof fallback === 'string') {
|
|
return fallback;
|
|
}
|
|
if (fallback && typeof fallback === 'object' && 'defaultValue' in fallback) {
|
|
return fallback.defaultValue;
|
|
}
|
|
return key;
|
|
},
|
|
i18n: {
|
|
language: 'de',
|
|
changeLanguage: vi.fn(),
|
|
},
|
|
ready: true,
|
|
}),
|
|
}));
|
|
|
|
vi.mock('@inertiajs/react', async () => {
|
|
const ReactModule = await import('react');
|
|
const ReactDom = await import('react-dom');
|
|
return {
|
|
Head: ({ children }: { children?: React.ReactNode }) =>
|
|
ReactDom.createPortal(children, document.head),
|
|
Link: ({ children, href }: { children: React.ReactNode; href: string }) => (
|
|
<a href={href}>{children}</a>
|
|
),
|
|
router: {
|
|
post: vi.fn(),
|
|
visit: vi.fn(),
|
|
},
|
|
usePage: () => page,
|
|
};
|
|
});
|
|
|
|
vi.mock('@/components/analytics/MatomoTracker', () => ({
|
|
default: () => null,
|
|
}));
|
|
|
|
vi.mock('@/layouts/app/Footer', () => ({
|
|
default: () => null,
|
|
}));
|
|
|
|
vi.mock('@/hooks/use-appearance', () => ({
|
|
useAppearance: () => ({
|
|
appearance: 'light',
|
|
updateAppearance: vi.fn(),
|
|
}),
|
|
}));
|
|
|
|
vi.mock('@/components/ui/button', () => ({
|
|
Button: ({ children }: { children: React.ReactNode }) => <button type="button">{children}</button>,
|
|
}));
|
|
|
|
vi.mock('@/components/ui/dropdown-menu', () => ({
|
|
DropdownMenu: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
DropdownMenuContent: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
DropdownMenuItem: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
DropdownMenuLabel: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
DropdownMenuRadioGroup: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
DropdownMenuRadioItem: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
DropdownMenuSeparator: () => <div />,
|
|
DropdownMenuTrigger: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
}));
|
|
|
|
vi.mock('react-hot-toast', () => ({
|
|
default: {
|
|
success: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock('lucide-react', () => ({
|
|
MoreHorizontal: () => null,
|
|
Sun: () => null,
|
|
Moon: () => null,
|
|
Languages: () => null,
|
|
LayoutDashboard: () => null,
|
|
LogOut: () => null,
|
|
LogIn: () => null,
|
|
}));
|
|
|
|
import MarketingLayout from '../mainWebsite';
|
|
|
|
describe('MarketingLayout SEO tags', () => {
|
|
it('renders canonical and hreflang links for the active locale', () => {
|
|
page.url = '/de/kontakt';
|
|
page.props.locale = 'de';
|
|
|
|
render(
|
|
<MarketingLayout title="Kontakt">
|
|
<div>Test</div>
|
|
</MarketingLayout>
|
|
);
|
|
|
|
const canonical = document.head.querySelector('link[rel="canonical"]');
|
|
const alternateDe = document.head.querySelector('link[rel="alternate"][hreflang="de"]');
|
|
const alternateEn = document.head.querySelector('link[rel="alternate"][hreflang="en"]');
|
|
const alternateDefault = document.head.querySelector('link[rel="alternate"][hreflang="x-default"]');
|
|
|
|
expect(canonical).toHaveAttribute('href', 'https://fotospiel.app/de/kontakt');
|
|
expect(alternateDe).toHaveAttribute('href', 'https://fotospiel.app/de/kontakt');
|
|
expect(alternateEn).toHaveAttribute('href', 'https://fotospiel.app/en/contact');
|
|
expect(alternateDefault).toHaveAttribute('href', 'https://fotospiel.app/de/kontakt');
|
|
});
|
|
|
|
it('preserves query params and rewrites localized slugs across locales', () => {
|
|
page.url = '/en/contact?ref=ad';
|
|
page.props.locale = 'en';
|
|
|
|
render(
|
|
<MarketingLayout title="Contact">
|
|
<div>Test</div>
|
|
</MarketingLayout>
|
|
);
|
|
|
|
const canonical = document.head.querySelector('link[rel="canonical"]');
|
|
const alternateDe = document.head.querySelector('link[rel="alternate"][hreflang="de"]');
|
|
const alternateEn = document.head.querySelector('link[rel="alternate"][hreflang="en"]');
|
|
const alternateDefault = document.head.querySelector('link[rel="alternate"][hreflang="x-default"]');
|
|
|
|
expect(canonical).toHaveAttribute('href', 'https://fotospiel.app/en/contact?ref=ad');
|
|
expect(alternateEn).toHaveAttribute('href', 'https://fotospiel.app/en/contact?ref=ad');
|
|
expect(alternateDe).toHaveAttribute('href', 'https://fotospiel.app/de/kontakt?ref=ad');
|
|
expect(alternateDefault).toHaveAttribute('href', 'https://fotospiel.app/de/kontakt?ref=ad');
|
|
});
|
|
});
|