Onboarding / Deployment Scripts / Marketing updates
This commit is contained in:
@@ -1,8 +1,24 @@
|
||||
import { getHomepageContent } from '$lib/server/content';
|
||||
import { isHomepageHowItWorksEnabled } from '$lib/server/feature-flags';
|
||||
|
||||
export async function load() {
|
||||
const onboardingHosts = new Set(['onboarding.goodwalk.co.nz']);
|
||||
|
||||
export async function load({ url }) {
|
||||
const hostname = url.hostname.toLowerCase();
|
||||
const siteVariant =
|
||||
onboardingHosts.has(hostname) || url.searchParams.get('preview') === 'onboarding'
|
||||
? 'onboarding'
|
||||
: 'marketing';
|
||||
|
||||
if (siteVariant === 'onboarding') {
|
||||
return {
|
||||
siteVariant,
|
||||
isPreview: url.searchParams.get('preview') === 'onboarding'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
siteVariant,
|
||||
content: await getHomepageContent(),
|
||||
howItWorksEnabled: isHomepageHowItWorksEnabled()
|
||||
};
|
||||
|
||||
+129
-119
@@ -11,6 +11,7 @@
|
||||
import ServicesSection from '$lib/components/ServicesSection.svelte';
|
||||
import TestimonialsSection from '$lib/components/TestimonialsSection.svelte';
|
||||
import ValuesSection from '$lib/components/ValuesSection.svelte';
|
||||
import OnboardingPage from '$lib/components/OnboardingPage.svelte';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
@@ -25,127 +26,136 @@
|
||||
return `${siteUrl}${value.startsWith('/') ? value : `/${value}`}`;
|
||||
}
|
||||
|
||||
$: 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: absoluteUrl(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}`
|
||||
$: homepageStructuredData =
|
||||
data.siteVariant === 'marketing' && data.content
|
||||
? [
|
||||
{
|
||||
'@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: absoluteUrl(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}`
|
||||
}
|
||||
}))
|
||||
},
|
||||
aggregateRating: {
|
||||
'@type': 'AggregateRating',
|
||||
ratingValue: '5.0',
|
||||
bestRating: '5',
|
||||
worstRating: '1',
|
||||
reviewCount: String(data.content.testimonials.length)
|
||||
},
|
||||
review: data.content.testimonials.map((testimonial) => ({
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Review',
|
||||
reviewRating: {
|
||||
'@type': 'Rating',
|
||||
ratingValue: '5',
|
||||
bestRating: '5'
|
||||
},
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: testimonial.reviewer
|
||||
},
|
||||
reviewBody: testimonial.quote
|
||||
}))
|
||||
},
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'FAQPage',
|
||||
mainEntity: data.content.info.faqs.map((faq) => ({
|
||||
'@type': 'Question',
|
||||
name: faq.question,
|
||||
acceptedAnswer: {
|
||||
'@type': 'Answer',
|
||||
text: faq.answer
|
||||
}
|
||||
}))
|
||||
}
|
||||
}))
|
||||
},
|
||||
aggregateRating: {
|
||||
'@type': 'AggregateRating',
|
||||
ratingValue: '5.0',
|
||||
bestRating: '5',
|
||||
worstRating: '1',
|
||||
reviewCount: String(data.content.testimonials.length)
|
||||
},
|
||||
review: data.content.testimonials.map((testimonial) => ({
|
||||
'@type': 'Review',
|
||||
reviewRating: {
|
||||
'@type': 'Rating',
|
||||
ratingValue: '5',
|
||||
bestRating: '5'
|
||||
},
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: testimonial.reviewer
|
||||
},
|
||||
reviewBody: testimonial.quote
|
||||
}))
|
||||
},
|
||||
{
|
||||
'@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}
|
||||
/>
|
||||
{#if data.siteVariant === 'onboarding'}
|
||||
<OnboardingPage preview={data.isPreview} />
|
||||
{:else}
|
||||
{@const content = data.content!}
|
||||
<SeoHead
|
||||
title={content.seo.title}
|
||||
description={content.seo.description}
|
||||
canonicalPath="/"
|
||||
image={content.hero.imageUrl}
|
||||
imageAlt={content.hero.imageAlt}
|
||||
structuredData={homepageStructuredData}
|
||||
preloadImage={true}
|
||||
/>
|
||||
|
||||
<Header navigation={data.content.navigation} />
|
||||
<HeroSection hero={data.content.hero} reviewCta={data.content.intro.reviewCta} />
|
||||
<PromiseSection promise={data.content.promise} />
|
||||
<ServicesSection services={data.content.services} />
|
||||
{#if data.howItWorksEnabled}
|
||||
<HowItWorksSection content={data.content.howItWorks} />
|
||||
<Header navigation={content.navigation} />
|
||||
<HeroSection hero={content.hero} reviewCta={content.intro.reviewCta} />
|
||||
<PromiseSection promise={content.promise} />
|
||||
<ServicesSection services={content.services} />
|
||||
{#if data.howItWorksEnabled}
|
||||
<HowItWorksSection content={content.howItWorks} />
|
||||
{/if}
|
||||
<TestimonialsSection testimonials={content.testimonials} seedKey="/" />
|
||||
<ValuesSection values={content.values} />
|
||||
<BookingSection booking={content.booking} />
|
||||
<InfoSection info={content.info} />
|
||||
<InstagramSection instagram={content.instagram} />
|
||||
<Footer footer={content.footer} />
|
||||
{/if}
|
||||
<TestimonialsSection testimonials={data.content.testimonials} />
|
||||
<ValuesSection values={data.content.values} />
|
||||
<BookingSection booking={data.content.booking} />
|
||||
<InfoSection info={data.content.info} />
|
||||
<InstagramSection instagram={data.content.instagram} />
|
||||
<Footer footer={data.content.footer} />
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
import { privacyPolicyContent } from '$lib/content/privacy-policy';
|
||||
import { puppyVisitsContent } from '$lib/content/puppy-visits';
|
||||
import { termsAndConditionsContent } from '$lib/content/terms-and-conditions';
|
||||
import { buildAreaServed, buildBreadcrumb, absoluteUrl } from '$lib/seo';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
@@ -22,14 +23,6 @@
|
||||
const defaultSeoImage = '/images/auckland-dog-walking-happy-dog-hero.png';
|
||||
const defaultSeoImageAlt = 'Goodwalk Auckland dog walking services';
|
||||
|
||||
function absoluteUrl(value: string) {
|
||||
if (value.startsWith('http://') || value.startsWith('https://')) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return `${siteUrl}${value.startsWith('/') ? value : `/${value}`}`;
|
||||
}
|
||||
|
||||
function aggregateOfferSchema(plans: { price: string }[]) {
|
||||
const numericPrices = plans
|
||||
.map((plan) => Number(plan.price.replace(/[^0-9.]/g, '')))
|
||||
@@ -52,26 +45,7 @@
|
||||
};
|
||||
}
|
||||
|
||||
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}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
const areaServed = buildAreaServed();
|
||||
|
||||
let seoImage = defaultSeoImage;
|
||||
let seoImageAlt = defaultSeoImageAlt;
|
||||
@@ -90,7 +64,10 @@
|
||||
description: data.page.description,
|
||||
url: `${siteUrl}${data.page.canonicalPath}`
|
||||
},
|
||||
breadcrumbSchema(data.page.title, data.page.canonicalPath)
|
||||
buildBreadcrumb([
|
||||
{ name: 'Home', url: siteUrl },
|
||||
{ name: data.page.title, path: data.page.canonicalPath }
|
||||
])
|
||||
];
|
||||
|
||||
if (data.slug === 'pack-walks') {
|
||||
@@ -109,12 +86,15 @@
|
||||
name: 'Goodwalk',
|
||||
url: siteUrl
|
||||
},
|
||||
areaServed: 'Auckland Central, New Zealand',
|
||||
areaServed,
|
||||
image: absoluteUrl(seoImage),
|
||||
url: `${siteUrl}${data.page.canonicalPath}`,
|
||||
offers: aggregateOfferSchema(packWalksContent.pricing.plans)
|
||||
},
|
||||
breadcrumbSchema('Pack Walks', data.page.canonicalPath)
|
||||
buildBreadcrumb([
|
||||
{ name: 'Home', url: siteUrl },
|
||||
{ name: 'Pack Walks', path: data.page.canonicalPath }
|
||||
])
|
||||
];
|
||||
} else if (data.slug === 'dog-walking') {
|
||||
preloadHeroImage = true;
|
||||
@@ -132,12 +112,15 @@
|
||||
name: 'Goodwalk',
|
||||
url: siteUrl
|
||||
},
|
||||
areaServed: 'Auckland Central, New Zealand',
|
||||
areaServed,
|
||||
image: absoluteUrl(seoImage),
|
||||
url: `${siteUrl}${data.page.canonicalPath}`,
|
||||
offers: aggregateOfferSchema(dogWalkingContent.pricing.plans)
|
||||
},
|
||||
breadcrumbSchema('1:1 Walks', data.page.canonicalPath)
|
||||
buildBreadcrumb([
|
||||
{ name: 'Home', url: siteUrl },
|
||||
{ name: '1:1 Walks', path: data.page.canonicalPath }
|
||||
])
|
||||
];
|
||||
} else if (data.slug === 'puppy-visits') {
|
||||
preloadHeroImage = true;
|
||||
@@ -155,12 +138,15 @@
|
||||
name: 'Goodwalk',
|
||||
url: siteUrl
|
||||
},
|
||||
areaServed: 'Auckland Central, New Zealand',
|
||||
areaServed,
|
||||
image: absoluteUrl(seoImage),
|
||||
url: `${siteUrl}${data.page.canonicalPath}`,
|
||||
offers: aggregateOfferSchema(puppyVisitsContent.pricing.plans)
|
||||
},
|
||||
breadcrumbSchema('Puppy Visits', data.page.canonicalPath)
|
||||
buildBreadcrumb([
|
||||
{ name: 'Home', url: siteUrl },
|
||||
{ name: 'Puppy Visits', path: data.page.canonicalPath }
|
||||
])
|
||||
];
|
||||
} else if (data.slug === 'our-pricing') {
|
||||
pageStructuredData = [
|
||||
@@ -171,7 +157,10 @@
|
||||
description: data.page.description,
|
||||
url: `${siteUrl}${data.page.canonicalPath}`
|
||||
},
|
||||
breadcrumbSchema('Our Pricing', data.page.canonicalPath)
|
||||
buildBreadcrumb([
|
||||
{ name: 'Home', url: siteUrl },
|
||||
{ name: 'Our Pricing', path: data.page.canonicalPath }
|
||||
])
|
||||
];
|
||||
} else if (data.slug === 'about') {
|
||||
seoImage = aboutPageContent.sections[0].imageUrl;
|
||||
@@ -185,7 +174,21 @@
|
||||
url: `${siteUrl}${data.page.canonicalPath}`,
|
||||
image: absoluteUrl(seoImage)
|
||||
},
|
||||
breadcrumbSchema('About Us', data.page.canonicalPath)
|
||||
...(aboutPageContent.faqs && aboutPageContent.faqs.length
|
||||
? [{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'FAQPage',
|
||||
mainEntity: aboutPageContent.faqs.map((faq) => ({
|
||||
'@type': 'Question',
|
||||
name: faq.question,
|
||||
acceptedAnswer: { '@type': 'Answer', text: faq.answer }
|
||||
}))
|
||||
}]
|
||||
: []),
|
||||
buildBreadcrumb([
|
||||
{ name: 'Home', url: siteUrl },
|
||||
{ name: 'About Us', path: data.page.canonicalPath }
|
||||
])
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -212,7 +215,7 @@
|
||||
{:else if data.slug === 'our-pricing'}
|
||||
<PricingPage content={data.content} pageContent={ourPricingContent} />
|
||||
{:else if data.slug === 'about'}
|
||||
<AboutPage content={data.content} pageContent={aboutPageContent} />
|
||||
<AboutPage pageContent={aboutPageContent} />
|
||||
{:else if data.slug === 'terms-and-conditions'}
|
||||
<LegalPage pageContent={termsAndConditionsContent} />
|
||||
{:else if data.slug === 'privacy-policy'}
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
import { render, screen } from '@testing-library/svelte';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { aboutPageContent } from '$lib/content/about';
|
||||
import { dogWalkingContent } from '$lib/content/dog-walking';
|
||||
import { ourPricingContent } from '$lib/content/our-pricing';
|
||||
import { packWalksContent } from '$lib/content/pack-walks';
|
||||
import { puppyVisitsContent } from '$lib/content/puppy-visits';
|
||||
import { staticPages } from '$lib/content/static-pages';
|
||||
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'],
|
||||
['contact-us', "Fill in the form below and we'll be in touch to arrange a free introduction."],
|
||||
['pack-walks', packWalksContent.hero.title],
|
||||
['dog-walking', dogWalkingContent.hero.title],
|
||||
['puppy-visits', puppyVisitsContent.hero.title],
|
||||
['our-pricing', ourPricingContent.subtitle],
|
||||
['about', aboutPageContent.sections[0].title],
|
||||
['contact-us', "Let's meet!"],
|
||||
['terms-and-conditions', '1. Application of Terms'],
|
||||
['privacy-policy', 'How we collect your information']
|
||||
] as const)('renders the %s page branch', (slug, expectedText) => {
|
||||
@@ -18,6 +24,11 @@ describe('static slug route page', () => {
|
||||
data: createStaticRouteData(slug)
|
||||
});
|
||||
|
||||
if (slug === 'contact-us') {
|
||||
expect(screen.getByRole('heading', { name: expectedText })).toBeInTheDocument();
|
||||
return;
|
||||
}
|
||||
|
||||
expect(screen.getByText(expectedText)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -26,7 +37,7 @@ describe('static slug route page', () => {
|
||||
data: createStaticRouteData('about')
|
||||
});
|
||||
|
||||
expect(document.title).toBe('About Us | Dog Walkers | Goodwalk');
|
||||
expect(document.title).toBe(staticPages.about.title);
|
||||
expect(document.head.innerHTML).toContain('AboutPage');
|
||||
});
|
||||
|
||||
@@ -37,10 +48,8 @@ describe('static slug route page', () => {
|
||||
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();
|
||||
expect(screen.queryByText("Let's meet!")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('What our clients say')).not.toBeInTheDocument();
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
const onboardingHosts = new Set(['onboarding.goodwalk.co.nz']);
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const hostname = url.hostname.toLowerCase();
|
||||
const isOnboardingHost =
|
||||
onboardingHosts.has(hostname) || url.searchParams.get('preview') === 'contract';
|
||||
|
||||
if (!isOnboardingHost) {
|
||||
throw error(404, 'Not found');
|
||||
}
|
||||
|
||||
return {
|
||||
isPreview: url.searchParams.get('preview') === 'contract',
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
<script lang="ts">
|
||||
import ContractPage from '$lib/components/ContractPage.svelte';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<ContractPage preview={data.isPreview} />
|
||||
@@ -16,12 +16,19 @@ vi.mock('$lib/server/feature-flags', () => ({
|
||||
|
||||
import { load } from './+page.server';
|
||||
|
||||
function createLoadEvent(url = 'https://www.goodwalk.co.nz/') {
|
||||
return {
|
||||
url: new URL(url)
|
||||
} as Parameters<typeof load>[0];
|
||||
}
|
||||
|
||||
describe('home page server load', () => {
|
||||
it('returns homepage content', async () => {
|
||||
getHomepageContent.mockResolvedValue(homepageContent);
|
||||
isHomepageHowItWorksEnabled.mockReturnValue(false);
|
||||
|
||||
await expect(load()).resolves.toEqual({
|
||||
await expect(load(createLoadEvent())).resolves.toEqual({
|
||||
siteVariant: 'marketing',
|
||||
content: homepageContent,
|
||||
howItWorksEnabled: false
|
||||
});
|
||||
@@ -31,7 +38,8 @@ describe('home page server load', () => {
|
||||
getHomepageContent.mockResolvedValue(homepageContent);
|
||||
isHomepageHowItWorksEnabled.mockReturnValue(true);
|
||||
|
||||
await expect(load()).resolves.toEqual({
|
||||
await expect(load(createLoadEvent())).resolves.toEqual({
|
||||
siteVariant: 'marketing',
|
||||
content: homepageContent,
|
||||
howItWorksEnabled: true
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { render, screen } from '@testing-library/svelte';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { homepageContent } from '$lib/content/homepage';
|
||||
import HomePage from './+page.svelte';
|
||||
import { createHomepageRouteData } from '../test/fixtures';
|
||||
|
||||
@@ -9,11 +10,11 @@ describe('home page route', () => {
|
||||
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(screen.getByText(homepageContent.hero.highlight)).toBeInTheDocument();
|
||||
expect(screen.getByText(homepageContent.howItWorks.title)).toBeInTheDocument();
|
||||
expect(screen.getByText(homepageContent.info.title)).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText(/General enquiry/i)).not.toBeInTheDocument();
|
||||
expect(document.title).toBe('Home | Auckland Dog Walking | Goodwalk');
|
||||
expect(document.title).toBe(homepageContent.seo.title);
|
||||
expect(document.head.innerHTML).toContain('FAQPage');
|
||||
expect(document.head.innerHTML).toContain('https://www.goodwalk.co.nz/images/auckland-dog-walking-happy-dog-hero.png');
|
||||
});
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { locationsBySlug } from '$lib/content/locations';
|
||||
import { getSharedPageContent } from '$lib/server/content';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
export const load: PageServerLoad = async ({ params }) => {
|
||||
const location = locationsBySlug[params.suburb];
|
||||
|
||||
if (!location) {
|
||||
throw error(404, 'Page not found');
|
||||
}
|
||||
|
||||
return {
|
||||
content: await getSharedPageContent(),
|
||||
location
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import Header from '$lib/components/Header.svelte';
|
||||
import Footer from '$lib/components/Footer.svelte';
|
||||
import SeoHead from '$lib/components/SeoHead.svelte';
|
||||
import LocationPage from '$lib/components/LocationPage.svelte';
|
||||
import { buildLocationSeo } from '$lib/seo';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
$: location = data.location;
|
||||
$: seo = buildLocationSeo(location);
|
||||
</script>
|
||||
|
||||
<SeoHead
|
||||
title={seo.title}
|
||||
description={seo.description}
|
||||
canonicalPath={seo.canonicalPath}
|
||||
image={seo.image}
|
||||
imageAlt={seo.imageAlt}
|
||||
structuredData={seo.structuredData}
|
||||
/>
|
||||
|
||||
<Header navigation={data.content.navigation} />
|
||||
<LocationPage {location} testimonials={data.content.testimonials} />
|
||||
<Footer footer={data.content.footer} />
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { RequestHandler } from './$types';
|
||||
import { locationPages } from '$lib/content/locations';
|
||||
|
||||
const siteUrl = 'https://www.goodwalk.co.nz';
|
||||
|
||||
@@ -16,6 +17,11 @@ const routes: SitemapRoute[] = [
|
||||
{ path: '/our-pricing', priority: '0.8', changefreq: 'monthly' },
|
||||
{ path: '/about', priority: '0.7', changefreq: 'monthly' },
|
||||
{ path: '/contact-us', priority: '0.7', changefreq: 'monthly' },
|
||||
...locationPages.map((loc) => ({
|
||||
path: `/locations/${loc.slug}`,
|
||||
priority: '0.8',
|
||||
changefreq: 'monthly'
|
||||
})),
|
||||
{ path: '/terms-and-conditions', priority: '0.3', changefreq: 'yearly' },
|
||||
{ path: '/privacy-policy', priority: '0.3', changefreq: 'yearly' }
|
||||
];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { GET } from './+server';
|
||||
import { locationPages } from '$lib/content/locations';
|
||||
|
||||
describe('sitemap endpoint', () => {
|
||||
afterEach(() => {
|
||||
@@ -18,6 +19,7 @@ describe('sitemap endpoint', () => {
|
||||
expect(body).toContain('<loc>https://www.goodwalk.co.nz/contact-us</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);
|
||||
expect(body).toContain('<loc>https://www.goodwalk.co.nz/locations/mt-eden</loc>');
|
||||
expect(body.match(/<url>/g)).toHaveLength(9 + locationPages.length);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user