Initial commit

This commit is contained in:
ponzischeme89
2026-05-02 08:26:18 +12:00
commit b7ea05f150
119 changed files with 13641 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
<script lang="ts">
import { afterNavigate, disableScrollHandling } from '$app/navigation';
import '$lib/styles/variables.css';
import '$lib/styles/base.css';
import '$lib/styles/layout.css';
import '$lib/styles/typography.css';
import '$lib/styles/buttons.css';
import '$lib/styles/forms.css';
import '$lib/styles/sections.css';
import '$lib/styles/responsive.css';
afterNavigate(({ from, to }) => {
if (!from || !to || to.url.hash) {
return;
}
if (from.url.pathname !== to.url.pathname) {
disableScrollHandling();
if (document.activeElement instanceof HTMLElement) {
document.activeElement.blur();
}
requestAnimationFrame(() => {
requestAnimationFrame(() => {
window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
document.documentElement.scrollTop = 0;
document.body.scrollTop = 0;
});
});
}
});
</script>
<slot />
+7
View File
@@ -0,0 +1,7 @@
import { getHomepageContent } from '$lib/server/content';
export async function load() {
return {
content: await getHomepageContent()
};
}
+121
View File
@@ -0,0 +1,121 @@
<script lang="ts">
import SeoHead from '$lib/components/SeoHead.svelte';
import Footer from '$lib/components/Footer.svelte';
import Header from '$lib/components/Header.svelte';
import HeroSection from '$lib/components/HeroSection.svelte';
import InfoSection from '$lib/components/InfoSection.svelte';
import InstagramSection from '$lib/components/InstagramSection.svelte';
import IntroStrip from '$lib/components/IntroStrip.svelte';
import BookingSection from '$lib/components/BookingSection.svelte';
import PromiseSection from '$lib/components/PromiseSection.svelte';
import ServicesSection from '$lib/components/ServicesSection.svelte';
import TestimonialsSection from '$lib/components/TestimonialsSection.svelte';
import ValuesSection from '$lib/components/ValuesSection.svelte';
import type { PageData } from './$types';
export let data: PageData;
const siteUrl = 'https://www.goodwalk.co.nz';
$: homepageStructuredData = [
{
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'Goodwalk',
url: siteUrl,
inLanguage: 'en-NZ'
},
{
'@context': 'https://schema.org',
'@type': 'LocalBusiness',
name: 'Goodwalk',
description:
'Professional dog walking services across Auckland Central, including pack walks, 1:1 walks, and puppy visits.',
url: siteUrl,
logo: `${siteUrl}/images/goodwalk-auckland-dog-walking-logo.png`,
image: data.content.hero.imageUrl,
email: 'info@goodwalk.co.nz',
telephone: '+64-22-642-1011',
sameAs: ['https://www.instagram.com/goodwalk.nz/', 'https://g.page/r/CUsvrWPhkYrAEB0/'],
address: {
'@type': 'PostalAddress',
addressLocality: 'Auckland Central',
addressRegion: 'Auckland',
addressCountry: 'NZ'
},
areaServed: [
'Morningside',
'Kingsland',
'Ponsonby',
'Grey Lynn',
'Mt Albert',
'Mt Eden',
'Sandringham',
'Mt Roskill',
'Arch Hill',
'Freemans Bay',
'Herne Bay',
'Pt Chevalier',
'Avondale',
'Three Kings',
'Hillsborough',
'Eden Terrace',
'Balmoral'
],
openingHoursSpecification: [
{
'@type': 'OpeningHoursSpecification',
dayOfWeek: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
opens: '08:00',
closes: '16:00'
}
],
hasOfferCatalog: {
'@type': 'OfferCatalog',
name: 'Dog Walking Services',
itemListElement: data.content.services.map((service) => ({
'@type': 'Offer',
itemOffered: {
'@type': 'Service',
name: service.title,
url: `${siteUrl}${service.href}`
}
}))
}
},
{
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: data.content.info.faqs.map((faq) => ({
'@type': 'Question',
name: faq.question,
acceptedAnswer: {
'@type': 'Answer',
text: faq.answer
}
}))
}
];
</script>
<SeoHead
title={data.content.seo.title}
description={data.content.seo.description}
canonicalPath="/"
image={data.content.hero.imageUrl}
imageAlt={data.content.hero.imageAlt}
structuredData={homepageStructuredData}
preloadImage={true}
/>
<Header navigation={data.content.navigation} />
<HeroSection hero={data.content.hero} />
<IntroStrip intro={data.content.intro} />
<PromiseSection promise={data.content.promise} />
<ServicesSection services={data.content.services} />
<ValuesSection values={data.content.values} />
<TestimonialsSection testimonials={data.content.testimonials} />
<BookingSection booking={data.content.booking} />
<InfoSection info={data.content.info} />
<InstagramSection instagram={data.content.instagram} />
<Footer footer={data.content.footer} />
+22
View File
@@ -0,0 +1,22 @@
import { error, redirect } from '@sveltejs/kit';
import { staticPages, type StaticPageSlug } from '$lib/content/static-pages';
import { getSharedPageContent } from '$lib/server/content';
export async function load({ params }) {
if (params.slug === 'about-us') {
throw redirect(301, '/about');
}
const slug = params.slug as StaticPageSlug;
const page = staticPages[slug];
if (!page) {
throw error(404, 'Page not found');
}
return {
content: await getSharedPageContent(),
page,
slug
};
}
+238
View File
@@ -0,0 +1,238 @@
<script lang="ts">
import SeoHead from '$lib/components/SeoHead.svelte';
import AboutPage from '$lib/components/AboutPage.svelte';
import Footer from '$lib/components/Footer.svelte';
import Header from '$lib/components/Header.svelte';
import BookingPage from '$lib/components/BookingPage.svelte';
import LegalPage from '$lib/components/LegalPage.svelte';
import PricingPage from '$lib/components/PricingPage.svelte';
import { aboutPageContent } from '$lib/content/about';
import ServiceLandingPage from '$lib/components/ServiceLandingPage.svelte';
import { dogWalkingContent } from '$lib/content/dog-walking';
import { ourPricingContent } from '$lib/content/our-pricing';
import { packWalksContent } from '$lib/content/pack-walks';
import { privacyPolicyContent } from '$lib/content/privacy-policy';
import { puppyVisitsContent } from '$lib/content/puppy-visits';
import { termsAndConditionsContent } from '$lib/content/terms-and-conditions';
import type { PageData } from './$types';
export let data: PageData;
const siteUrl = 'https://www.goodwalk.co.nz';
const defaultSeoImage = '/images/auckland-dog-walking-happy-dog-hero.png';
const defaultSeoImageAlt = 'Goodwalk Auckland dog walking services';
function breadcrumbSchema(name: string, path: string) {
return {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: [
{
'@type': 'ListItem',
position: 1,
name: 'Home',
item: siteUrl
},
{
'@type': 'ListItem',
position: 2,
name,
item: `${siteUrl}${path}`
}
]
};
}
let seoImage = defaultSeoImage;
let seoImageAlt = defaultSeoImageAlt;
let preloadHeroImage = false;
let pageStructuredData: Record<string, unknown>[] = [];
$: {
seoImage = defaultSeoImage;
seoImageAlt = defaultSeoImageAlt;
preloadHeroImage = false;
pageStructuredData = [
{
'@context': 'https://schema.org',
'@type': 'WebPage',
name: data.page.title,
description: data.page.description,
url: `${siteUrl}${data.page.canonicalPath}`
},
breadcrumbSchema(data.page.title, data.page.canonicalPath)
];
if (data.slug === 'pack-walks') {
preloadHeroImage = true;
seoImage = packWalksContent.hero.imageUrl;
seoImageAlt = packWalksContent.hero.imageAlt;
pageStructuredData = [
{
'@context': 'https://schema.org',
'@type': 'Service',
name: packWalksContent.hero.title,
description: data.page.description,
serviceType: 'Pack Walks',
provider: {
'@type': 'LocalBusiness',
name: 'Goodwalk',
url: siteUrl
},
areaServed: 'Auckland Central, New Zealand',
image: seoImage,
url: `${siteUrl}${data.page.canonicalPath}`
},
breadcrumbSchema('Pack Walks', data.page.canonicalPath)
];
} else if (data.slug === 'dog-walking') {
preloadHeroImage = true;
seoImage = dogWalkingContent.hero.imageUrl;
seoImageAlt = dogWalkingContent.hero.imageAlt;
pageStructuredData = [
{
'@context': 'https://schema.org',
'@type': 'Service',
name: dogWalkingContent.hero.title,
description: data.page.description,
serviceType: '1:1 Dog Walking',
provider: {
'@type': 'LocalBusiness',
name: 'Goodwalk',
url: siteUrl
},
areaServed: 'Auckland Central, New Zealand',
image: seoImage,
url: `${siteUrl}${data.page.canonicalPath}`
},
breadcrumbSchema('1:1 Walks', data.page.canonicalPath)
];
} else if (data.slug === 'puppy-visits') {
preloadHeroImage = true;
seoImage = puppyVisitsContent.hero.imageUrl;
seoImageAlt = puppyVisitsContent.hero.imageAlt;
pageStructuredData = [
{
'@context': 'https://schema.org',
'@type': 'Service',
name: puppyVisitsContent.hero.title,
description: data.page.description,
serviceType: 'Puppy Visits',
provider: {
'@type': 'LocalBusiness',
name: 'Goodwalk',
url: siteUrl
},
areaServed: 'Auckland Central, New Zealand',
image: seoImage,
url: `${siteUrl}${data.page.canonicalPath}`
},
breadcrumbSchema('Puppy Visits', data.page.canonicalPath)
];
} else if (data.slug === 'our-pricing') {
pageStructuredData = [
{
'@context': 'https://schema.org',
'@type': 'CollectionPage',
name: data.page.title,
description: data.page.description,
url: `${siteUrl}${data.page.canonicalPath}`
},
breadcrumbSchema('Our Pricing', data.page.canonicalPath)
];
} else if (data.slug === 'about' || data.slug === 'about-us') {
seoImage = aboutPageContent.sections[0].imageUrl;
seoImageAlt = aboutPageContent.sections[0].imageAlt;
pageStructuredData = [
{
'@context': 'https://schema.org',
'@type': 'AboutPage',
name: data.page.title,
description: data.page.description,
url: `${siteUrl}${data.page.canonicalPath}`,
image: seoImage
},
breadcrumbSchema('About Us', data.page.canonicalPath)
];
}
}
</script>
<SeoHead
title={data.page.title}
description={data.page.description}
canonicalPath={data.page.canonicalPath}
image={seoImage}
imageAlt={seoImageAlt}
structuredData={pageStructuredData}
preloadImage={preloadHeroImage}
/>
<Header navigation={data.content.navigation} />
{#if data.slug === 'pack-walks'}
<ServiceLandingPage content={data.content} pageContent={packWalksContent} />
{:else if data.slug === 'dog-walking'}
<ServiceLandingPage content={data.content} pageContent={dogWalkingContent} />
{:else if data.slug === 'puppy-visits'}
<ServiceLandingPage content={data.content} pageContent={puppyVisitsContent} />
{:else if data.slug === 'our-pricing'}
<PricingPage content={data.content} pageContent={ourPricingContent} />
{:else if data.slug === 'about' || data.slug === 'about-us'}
<AboutPage content={data.content} pageContent={aboutPageContent} />
{:else if data.slug === 'terms-and-conditions'}
<LegalPage pageContent={termsAndConditionsContent} />
{:else if data.slug === 'privacy-policy'}
<LegalPage pageContent={privacyPolicyContent} />
{:else if data.slug === 'booking'}
<BookingPage booking={data.content.booking} />
{:else}
<main class="static-page">
<section class="static-page-hero">
<div class="static-page-inner">
<h1>{data.page.title}</h1>
</div>
</section>
</main>
{/if}
<Footer footer={data.content.footer} />
<style>
.static-page {
min-height: 50vh;
background: var(--off-white);
}
.static-page-hero {
padding: 96px 0 120px;
}
.static-page-inner {
max-width: var(--max-w);
margin: 0 auto;
padding: 0 50px;
}
h1 {
font-family: var(--font-head);
font-size: 56px;
line-height: 1.05;
letter-spacing: -0.04em;
color: #000;
}
@media (max-width: 768px) {
.static-page-hero {
padding: 56px 0 72px;
}
.static-page-inner {
padding: 0 24px;
}
h1 {
font-size: 34px;
}
}
</style>
@@ -0,0 +1,42 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { staticPages } from '$lib/content/static-pages';
import { sharedPageContent } from '../../test/fixtures';
const { getSharedPageContent } = vi.hoisted(() => ({
getSharedPageContent: vi.fn()
}));
vi.mock('$lib/server/content', () => ({
getSharedPageContent
}));
import { load } from './+page.server';
describe('static slug page server load', () => {
beforeEach(() => {
getSharedPageContent.mockReset();
});
it('redirects the legacy about-us slug to /about', async () => {
await expect(load({ params: { slug: 'about-us' } } as never)).rejects.toMatchObject({
status: 301,
location: '/about'
});
});
it('throws a 404 for unknown slugs', async () => {
await expect(load({ params: { slug: 'missing-page' } } as never)).rejects.toMatchObject({
status: 404
});
});
it('returns the shared content and page metadata for valid static routes', async () => {
getSharedPageContent.mockResolvedValue(sharedPageContent);
await expect(load({ params: { slug: 'pack-walks' } } as never)).resolves.toEqual({
content: sharedPageContent,
page: staticPages['pack-walks'],
slug: 'pack-walks'
});
});
});
+46
View File
@@ -0,0 +1,46 @@
import { render, screen } from '@testing-library/svelte';
import { describe, expect, it } from 'vitest';
import SlugPage from './+page.svelte';
import { createStaticRouteData } from '../../test/fixtures';
describe('static slug route page', () => {
it.each([
['pack-walks', 'Join our Tiny Gang!'],
['dog-walking', 'Walks for larger breeds, too!'],
['puppy-visits', 'Introducing Puppy Visits: Building strong foundations for our pack walks!'],
['our-pricing', 'Simple, transparent pricing — no lock-in contracts.'],
['about', 'Who we are'],
['booking', "Fill in the form below and we'll be in touch to arrange a free introduction."],
['terms-and-conditions', '1. Application of Terms'],
['privacy-policy', 'How we collect your information']
] as const)('renders the %s page branch', (slug, expectedText) => {
render(SlugPage, {
data: createStaticRouteData(slug)
});
expect(screen.getByText(expectedText)).toBeInTheDocument();
});
it('sets SEO metadata for rendered slug pages', () => {
render(SlugPage, {
data: createStaticRouteData('about')
});
expect(document.title).toBe('About Us | Dog Walkers | Goodwalk');
expect(document.head.innerHTML).toContain('AboutPage');
});
it.each(['terms-and-conditions', 'privacy-policy'] as const)(
'does not render booking or testimonial sections on %s',
(slug) => {
render(SlugPage, {
data: createStaticRouteData(slug)
});
expect(
screen.queryByText("Fill in the form below and we'll be in touch to arrange a free introduction.")
).not.toBeInTheDocument();
expect(screen.queryByText('Why people choose us!')).not.toBeInTheDocument();
}
);
});
@@ -0,0 +1,11 @@
import { saveHomepageContent, getHomepageContent } from '$lib/server/content';
import { json } from '@sveltejs/kit';
export async function GET() {
return json(await getHomepageContent());
}
export async function PUT({ request }) {
const content = await request.json();
return json(await saveHomepageContent(content));
}
@@ -0,0 +1,53 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { homepageContent } from '$lib/content/homepage';
const { getHomepageContent, saveHomepageContent } = vi.hoisted(() => ({
getHomepageContent: vi.fn(),
saveHomepageContent: vi.fn()
}));
vi.mock('$lib/server/content', () => ({
getHomepageContent,
saveHomepageContent
}));
import { GET, PUT } from './+server';
describe('homepage content endpoint', () => {
beforeEach(() => {
getHomepageContent.mockReset();
saveHomepageContent.mockReset();
});
it('returns homepage content for GET requests', async () => {
getHomepageContent.mockResolvedValue(homepageContent);
const response = await GET();
expect(response.status).toBe(200);
await expect(response.json()).resolves.toEqual(homepageContent);
});
it('saves homepage content for PUT requests', async () => {
const updatedContent = {
...homepageContent,
seo: {
...homepageContent.seo,
title: 'Updated home title'
}
};
saveHomepageContent.mockResolvedValue(updatedContent);
const response = await PUT({
request: new Request('https://www.goodwalk.co.nz/api/content/homepage', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updatedContent)
})
} as never);
expect(saveHomepageContent).toHaveBeenCalledWith(updatedContent);
await expect(response.json()).resolves.toEqual(updatedContent);
});
});
+28
View File
@@ -0,0 +1,28 @@
import { json } from '@sveltejs/kit';
import { getPool } from '$lib/server/db';
export async function GET() {
const pool = getPool();
let database: 'disabled' | 'up' | 'down' = 'disabled';
if (pool) {
try {
await pool.query('select 1');
database = 'up';
} catch (error) {
console.error('Health check database query failed.', error);
database = 'down';
}
}
return json(
{
status: database === 'down' ? 'degraded' : 'ok',
database,
timestamp: new Date().toISOString()
},
{
status: database === 'down' ? 503 : 200
}
);
}
+57
View File
@@ -0,0 +1,57 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
vi.mock('$lib/server/db', () => ({
getPool: vi.fn()
}));
import { getPool } from '$lib/server/db';
import { GET } from './+server';
describe('health endpoint', () => {
beforeEach(() => {
vi.mocked(getPool).mockReset();
});
it('reports a disabled database when no connection string is configured', async () => {
vi.mocked(getPool).mockReturnValue(null);
const response = await GET();
const body = await response.json();
expect(response.status).toBe(200);
expect(body).toMatchObject({
status: 'ok',
database: 'disabled'
});
});
it('reports an available database when the query succeeds', async () => {
vi.mocked(getPool).mockReturnValue({
query: vi.fn().mockResolvedValue({})
} as never);
const response = await GET();
const body = await response.json();
expect(response.status).toBe(200);
expect(body).toMatchObject({
status: 'ok',
database: 'up'
});
});
it('reports a degraded status when the database query fails', async () => {
vi.mocked(getPool).mockReturnValue({
query: vi.fn().mockRejectedValue(new Error('connection refused'))
} as never);
const response = await GET();
const body = await response.json();
expect(response.status).toBe(503);
expect(body).toMatchObject({
status: 'degraded',
database: 'down'
});
});
});
+22
View File
@@ -0,0 +1,22 @@
import { describe, expect, it, vi } from 'vitest';
import { homepageContent } from '$lib/content/homepage';
const { getHomepageContent } = vi.hoisted(() => ({
getHomepageContent: vi.fn()
}));
vi.mock('$lib/server/content', () => ({
getHomepageContent
}));
import { load } from './+page.server';
describe('home page server load', () => {
it('returns homepage content', async () => {
getHomepageContent.mockResolvedValue(homepageContent);
await expect(load()).resolves.toEqual({
content: homepageContent
});
});
});
+19
View File
@@ -0,0 +1,19 @@
import { render, screen } from '@testing-library/svelte';
import { describe, expect, it } from 'vitest';
import HomePage from './+page.svelte';
import { createHomepageRouteData } from '../test/fixtures';
describe('home page route', () => {
it('renders the homepage sections and SEO metadata', () => {
render(HomePage, {
data: createHomepageRouteData()
});
expect(screen.getAllByText("Your Dog's Day!").length).toBeGreaterThan(0);
expect(document.body.textContent).toContain('Happy pets,');
expect(screen.getByText('Locations & Hours')).toBeInTheDocument();
expect(document.title).toBe('Home | Auckland Dog Walking | Goodwalk');
expect(document.head.innerHTML).toContain('FAQPage');
expect(document.head.innerHTML).toContain('https://www.goodwalk.co.nz/images/auckland-dog-walking-happy-dog-hero.png');
});
});
+52
View File
@@ -0,0 +1,52 @@
import { render } from '@testing-library/svelte';
import { beforeEach, describe, expect, it, vi } from 'vitest';
const afterNavigate = vi.fn();
const disableScrollHandling = vi.fn();
vi.mock('$app/navigation', () => ({
afterNavigate: (callback: (args: { from: { url: URL } | null; to: { url: URL } | null }) => void) =>
afterNavigate(callback),
disableScrollHandling
}));
describe('root layout navigation behavior', () => {
beforeEach(() => {
afterNavigate.mockClear();
disableScrollHandling.mockClear();
});
it('resets scroll position after route changes without hashes', async () => {
const { default: Layout } = await import('./+layout.svelte');
render(Layout);
const navigateHandler = afterNavigate.mock.calls[0][0];
const scrollToSpy = vi.spyOn(window, 'scrollTo');
vi.spyOn(window, 'requestAnimationFrame').mockImplementation((callback: FrameRequestCallback) => {
callback(0);
return 0;
});
navigateHandler({
from: { url: new URL('https://www.goodwalk.co.nz/about') },
to: { url: new URL('https://www.goodwalk.co.nz/booking') }
});
expect(disableScrollHandling).toHaveBeenCalledTimes(1);
expect(scrollToSpy).toHaveBeenCalledWith({ top: 0, left: 0, behavior: 'auto' });
});
it('does not reset scroll position for hash navigation', async () => {
const { default: Layout } = await import('./+layout.svelte');
render(Layout);
const navigateHandler = afterNavigate.mock.calls[0][0];
navigateHandler({
from: { url: new URL('https://www.goodwalk.co.nz/about') },
to: { url: new URL('https://www.goodwalk.co.nz/about#team') }
});
expect(disableScrollHandling).not.toHaveBeenCalled();
});
});
+16
View File
@@ -0,0 +1,16 @@
import type { RequestHandler } from './$types';
export const GET: RequestHandler = () => {
const body = [
'User-agent: *',
'Allow: /',
'',
'Sitemap: https://www.goodwalk.co.nz/sitemap.xml'
].join('\n');
return new Response(body, {
headers: {
'Content-Type': 'text/plain; charset=utf-8'
}
});
};
+14
View File
@@ -0,0 +1,14 @@
import { describe, expect, it } from 'vitest';
import { GET } from './+server';
describe('robots endpoint', () => {
it('returns the crawl policy and sitemap location', async () => {
const response = GET();
const body = await response.text();
expect(response.headers.get('content-type')).toBe('text/plain; charset=utf-8');
expect(body).toContain('User-agent: *');
expect(body).toContain('Allow: /');
expect(body).toContain('Sitemap: https://www.goodwalk.co.nz/sitemap.xml');
});
});
+37
View File
@@ -0,0 +1,37 @@
import type { RequestHandler } from './$types';
const siteUrl = 'https://www.goodwalk.co.nz';
const routes = [
'/',
'/pack-walks',
'/dog-walking',
'/puppy-visits',
'/our-pricing',
'/about',
'/booking',
'/terms-and-conditions',
'/privacy-policy'
];
export const GET: RequestHandler = () => {
const lastmod = new Date().toISOString().split('T')[0];
const body = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${routes
.map(
(path) => ` <url>
<loc>${siteUrl}${path}</loc>
<lastmod>${lastmod}</lastmod>
<changefreq>${path === '/' ? 'weekly' : 'monthly'}</changefreq>
<priority>${path === '/' ? '1.0' : '0.8'}</priority>
</url>`
)
.join('\n')}
</urlset>`;
return new Response(body, {
headers: {
'Content-Type': 'application/xml; charset=utf-8'
}
});
};
+22
View File
@@ -0,0 +1,22 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import { GET } from './+server';
describe('sitemap endpoint', () => {
afterEach(() => {
vi.useRealTimers();
});
it('returns a sitemap covering the published routes', async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-05-01T09:15:00Z'));
const response = GET();
const body = await response.text();
expect(response.headers.get('content-type')).toBe('application/xml; charset=utf-8');
expect(body).toContain('<loc>https://www.goodwalk.co.nz/</loc>');
expect(body).toContain('<loc>https://www.goodwalk.co.nz/privacy-policy</loc>');
expect(body).toContain('<lastmod>2026-05-01</lastmod>');
expect(body.match(/<url>/g)).toHaveLength(9);
});
});