Add marketing motion reveals to blog and occasions

This commit is contained in:
Codex Agent
2026-01-21 15:22:39 +01:00
parent 5eb0941512
commit a01a7ec399
16 changed files with 1869 additions and 781 deletions

View File

@@ -0,0 +1,91 @@
import React from 'react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
class IntersectionObserverMock {
observe() {}
unobserve() {}
disconnect() {}
}
Object.defineProperty(globalThis, 'IntersectionObserver', {
writable: true,
value: IntersectionObserverMock,
});
beforeEach(() => {
window.matchMedia = vi.fn().mockImplementation(() => ({
matches: false,
media: '',
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
}));
});
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' },
}),
}));
vi.mock('@inertiajs/react', () => ({
Head: () => null,
Link: ({ children, href }: { children: React.ReactNode; href: string }) => <a href={href}>{children}</a>,
usePage: () => ({ props: { supportedLocales: ['de', 'en'] } }),
}));
vi.mock('@/layouts/mainWebsite', () => ({
default: ({ children, title }: { children: React.ReactNode; title: string }) => (
<div>
<h1>{title}</h1>
{children}
</div>
),
}));
vi.mock('@/hooks/useLocalizedRoutes', () => ({
useLocalizedRoutes: () => ({
localizedPath: (path: string) => path,
}),
}));
import Blog from '../Blog';
describe('Blog', () => {
it('renders the hero title', () => {
render(
<Blog
posts={{
data: [
{
id: 1,
slug: 'hello',
title: 'Hello',
excerpt: 'Excerpt',
published_at: '2024-01-01',
author: 'Team',
},
],
links: [],
current_page: 1,
last_page: 1,
}}
/>
);
expect(screen.getByText('blog.hero_title')).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,76 @@
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
class IntersectionObserverMock {
observe() {}
unobserve() {}
disconnect() {}
}
Object.defineProperty(globalThis, 'IntersectionObserver', {
writable: true,
value: IntersectionObserverMock,
});
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' },
}),
}));
vi.mock('@inertiajs/react', () => ({
Head: () => null,
Link: ({ children, href }: { children: React.ReactNode; href: string }) => <a href={href}>{children}</a>,
}));
vi.mock('@/layouts/mainWebsite', () => ({
default: ({ children, title }: { children: React.ReactNode; title: string }) => (
<div>
<h1>{title}</h1>
{children}
</div>
),
}));
vi.mock('@/hooks/useLocalizedRoutes', () => ({
useLocalizedRoutes: () => ({
localizedPath: (path: string) => path,
}),
}));
import BlogShow from '../BlogShow';
describe('BlogShow', () => {
it('renders the article title', () => {
render(
<BlogShow
post={{
id: 1,
title: 'Hello World',
excerpt: 'Excerpt',
excerpt_html: '<p>Excerpt</p>',
content: 'Content',
content_html: '<p>Content</p>',
headings: [{ text: 'Intro', slug: 'intro', level: 2 }],
featured_image: undefined,
published_at: '2024-01-01',
author: { name: 'Team' },
slug: 'hello-world',
url: '/blog/hello-world',
}}
/>
);
expect(screen.getAllByText('Hello World').length).toBeGreaterThan(0);
});
});

View File

@@ -0,0 +1,67 @@
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string, fallback?: string | { defaultValue?: unknown }) => {
if (key === 'demo_page') {
return {
title: 'Demo Page',
subtitle: 'Subtitle',
primaryCta: 'Primary',
secondaryCta: 'Secondary',
iframeNote: 'Note',
openFull: 'Open',
features: [],
};
}
if (typeof fallback === 'string') {
return fallback;
}
if (fallback && typeof fallback === 'object' && 'defaultValue' in fallback) {
return fallback.defaultValue;
}
return key;
},
}),
}));
vi.mock('@inertiajs/react', () => ({
Head: () => null,
Link: ({ children, href }: { children: React.ReactNode; href: string }) => <a href={href}>{children}</a>,
}));
vi.mock('@/layouts/mainWebsite', () => ({
default: ({ children, title }: { children: React.ReactNode; title: string }) => (
<div>
<h1>{title}</h1>
{children}
</div>
),
}));
vi.mock('@/hooks/useLocalizedRoutes', () => ({
useLocalizedRoutes: () => ({
localizedPath: (path: string) => path,
}),
}));
vi.mock('@/hooks/useLocale', () => ({
useLocale: () => 'de',
}));
vi.mock('@/components/ui/dialog', () => ({
Dialog: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
DialogContent: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));
import DemoPage from '../Demo';
describe('DemoPage', () => {
it('renders the demo headline', () => {
render(<DemoPage demoToken="demo" />);
expect(screen.getAllByText('Demo Page').length).toBeGreaterThan(0);
});
});

View File

@@ -0,0 +1,82 @@
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
class IntersectionObserverMock {
observe() {}
unobserve() {}
disconnect() {}
}
Object.defineProperty(globalThis, 'IntersectionObserver', {
writable: true,
value: IntersectionObserverMock,
});
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;
},
}),
}));
vi.mock('@inertiajs/react', () => ({
Head: () => null,
Link: ({ children, href }: { children: React.ReactNode; href: string }) => <a href={href}>{children}</a>,
useForm: () => ({
data: { name: '', email: '', message: '', nickname: '' },
setData: vi.fn(),
post: vi.fn(),
processing: false,
errors: {},
reset: vi.fn(),
}),
usePage: () => ({ props: { flash: {} } }),
}));
vi.mock('@/layouts/mainWebsite', () => ({
default: ({ children, title }: { children: React.ReactNode; title: string }) => (
<div>
<h1>{title}</h1>
{children}
</div>
),
}));
vi.mock('@/hooks/useLocalizedRoutes', () => ({
useLocalizedRoutes: () => ({
localizedPath: (path: string) => path,
}),
}));
vi.mock('@/hooks/useAnalytics', () => ({
useAnalytics: () => ({ trackEvent: vi.fn() }),
}));
vi.mock('@/hooks/useCtaExperiment', () => ({
useCtaExperiment: () => ({ variant: 'default', trackClick: vi.fn() }),
}));
import Home from '../Home';
describe('Home', () => {
it('renders the hero headline', () => {
render(
<Home
packages={[
{ id: 1, name: 'Starter', description: 'Desc', price: 29 },
{ id: 2, name: 'Standard', description: 'Desc', price: 59 },
]}
/>
);
expect(screen.getByText('home.hero_title')).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,63 @@
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
class IntersectionObserverMock {
observe() {}
unobserve() {}
disconnect() {}
}
Object.defineProperty(globalThis, 'IntersectionObserver', {
writable: true,
value: IntersectionObserverMock,
});
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;
},
ready: true,
}),
}));
vi.mock('@inertiajs/react', () => ({
Head: () => null,
Link: ({ children, href }: { children: React.ReactNode; href: string }) => <a href={href}>{children}</a>,
}));
vi.mock('@/layouts/mainWebsite', () => ({
default: ({ children, title }: { children: React.ReactNode; title: string }) => (
<div>
<h1>{title}</h1>
{children}
</div>
),
}));
vi.mock('@/hooks/useLocalizedRoutes', () => ({
useLocalizedRoutes: () => ({
localizedPath: (path: string) => path,
}),
}));
vi.mock('@/hooks/useLocale', () => ({
useLocale: () => 'de',
}));
import HowItWorks from '../HowItWorks';
describe('HowItWorks', () => {
it('renders the default hero title', () => {
render(<HowItWorks />);
expect(screen.getAllByText('So funktioniert die Fotospiel App').length).toBeGreaterThan(0);
});
});

View File

@@ -0,0 +1,73 @@
import React from 'react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
beforeEach(() => {
window.matchMedia = vi.fn().mockImplementation(() => ({
matches: false,
media: '',
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
}));
if (!window.scrollTo) {
window.scrollTo = vi.fn();
}
});
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;
},
}),
}));
vi.mock('@inertiajs/react', () => ({
Head: () => null,
Link: ({ children, href }: { children: React.ReactNode; href: string }) => <a href={href}>{children}</a>,
useForm: () => ({
data: { name: '', email: '', message: '', nickname: '' },
setData: vi.fn(),
post: vi.fn(),
processing: false,
errors: {},
reset: vi.fn(),
}),
usePage: () => ({ props: { flash: {} } }),
}));
vi.mock('@/layouts/mainWebsite', () => ({
default: ({ children, title }: { children: React.ReactNode; title: string }) => (
<div>
<h1>{title}</h1>
{children}
</div>
),
}));
vi.mock('@/hooks/useLocalizedRoutes', () => ({
useLocalizedRoutes: () => ({
localizedPath: (path: string) => path,
}),
}));
import Kontakt from '../Kontakt';
describe('Kontakt', () => {
it('renders the page title', () => {
render(<Kontakt />);
expect(screen.getAllByText('kontakt.title').length).toBeGreaterThan(0);
});
});

View File

@@ -0,0 +1,72 @@
import React from 'react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
class IntersectionObserverMock {
observe() {}
unobserve() {}
disconnect() {}
}
Object.defineProperty(globalThis, 'IntersectionObserver', {
writable: true,
value: IntersectionObserverMock,
});
beforeEach(() => {
window.matchMedia = vi.fn().mockImplementation(() => ({
matches: false,
media: '',
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
}));
});
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' },
}),
}));
vi.mock('@inertiajs/react', () => ({
Head: () => null,
Link: ({ children, href }: { children: React.ReactNode; href: string }) => <a href={href}>{children}</a>,
}));
vi.mock('@/layouts/mainWebsite', () => ({
default: ({ children, title }: { children: React.ReactNode; title: string }) => (
<div>
<h1>{title}</h1>
{children}
</div>
),
}));
vi.mock('@/hooks/useLocalizedRoutes', () => ({
useLocalizedRoutes: () => ({
localizedPath: (path: string) => path,
}),
}));
import Occasions from '../Occasions';
describe('Occasions', () => {
it('renders the wedding hero content', () => {
render(<Occasions type="hochzeit" />);
expect(screen.getAllByText('occasions.weddings.title').length).toBeGreaterThan(0);
});
});

View File

@@ -0,0 +1,140 @@
import React from 'react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
class IntersectionObserverMock {
observe() {}
unobserve() {}
disconnect() {}
}
Object.defineProperty(globalThis, 'IntersectionObserver', {
writable: true,
value: IntersectionObserverMock,
});
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;
},
}),
}));
vi.mock('@inertiajs/react', () => ({
Link: ({ children, href }: { children: React.ReactNode; href: string }) => <a href={href}>{children}</a>,
usePage: () => ({ props: { flash: {} } }),
}));
vi.mock('@/layouts/mainWebsite', () => ({
default: ({ children, title }: { children: React.ReactNode; title: string }) => (
<div>
<h1>{title}</h1>
{children}
</div>
),
}));
vi.mock('@/hooks/useAnalytics', () => ({
useAnalytics: () => ({ trackEvent: vi.fn() }),
}));
vi.mock('@/hooks/useCtaExperiment', () => ({
useCtaExperiment: () => ({ variant: 'default', trackClick: vi.fn() }),
}));
vi.mock('@/hooks/useLocalizedRoutes', () => ({
useLocalizedRoutes: () => ({
localizedPath: (path: string) => path,
}),
}));
vi.mock('@/hooks/useLocale', () => ({
useLocale: () => 'de',
}));
vi.mock('react-hot-toast', () => ({
default: {
error: vi.fn(),
},
}));
import Packages from '../Packages';
describe('Packages', () => {
beforeEach(() => {
window.matchMedia = vi.fn().mockImplementation(() => ({
matches: false,
media: '',
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
}));
if (!HTMLElement.prototype.scrollTo) {
HTMLElement.prototype.scrollTo = vi.fn();
}
});
it('renders the hero headline', () => {
render(
<Packages
endcustomerPackages={[
{
id: 1,
name: 'Starter',
slug: 'starter',
description: 'Desc',
description_breakdown: [],
price: 29,
events: 1,
features: [],
limits: { max_photos: 600, max_guests: 100, gallery_days: 180 },
branding_allowed: false,
watermark_allowed: false,
},
{
id: 2,
name: 'Standard',
slug: 'standard',
description: 'Desc',
description_breakdown: [],
price: 59,
events: 1,
features: ['no_watermark'],
limits: { max_photos: 4000, max_guests: 250, gallery_days: 365 },
branding_allowed: true,
watermark_allowed: true,
},
]}
resellerPackages={[
{
id: 3,
name: 'Agentur',
slug: 'agency',
description: 'Desc',
description_breakdown: [],
price: 199,
events: null,
features: ['reseller_dashboard'],
limits: { max_events_per_year: 10 },
included_package_slug: 'standard',
branding_allowed: true,
watermark_allowed: true,
},
]}
/>
);
expect(screen.getByText('packages.hero_title')).toBeInTheDocument();
});
});