Files
neuf-www/web/tests/smoke/fixtures.ts
T

442 lines
12 KiB
TypeScript

// Canned GraphQL responses for the smoke-test mock CMS.
//
// Every piece is typed with `satisfies` against the *fragment* types generated
// by codegen (fragment masking makes the query types too weak — their
// `$fragmentRefs` props are optional). When `npm run codegen` regenerates
// after a schema change, `tsc --noEmit` fails here, flagging stale fixtures.
import type {
AllAssociationSlugsQuery,
AllAssociationsQuery,
AllGenericSlugsQuery,
AllVenueSlugsQuery,
AssociationBySlugQuery,
AssociationFragment,
AssociationIndexFragment,
ContactIndexFragment,
ContactsQuery,
EventBySlugQuery,
EventCategoryFragment,
EventFragment,
EventIndexFragment,
EventIndexMetadataQuery,
EventListItemFragment,
EventOrganizerFragment,
EventOverviewItemFragment,
FutureEventsQuery,
GenericFragment,
GenericPageByUrlQuery,
HomeFragment,
HomeQuery,
ImageFragment,
NewsBySlugQuery,
NewsFragment,
NewsIndexFragment,
NewsIndexMetadataQuery,
NewsListItemFragment,
NewsQuery,
OpeningHoursSetFragment,
OpeningHoursSetsQuery,
PreviewPageQuery,
SearchQuery,
SponsorsPageFragment,
SponsorsQuery,
StudioFragment,
StudioQuery,
VenueBySlugQuery,
VenueFragment,
VenueIndexFragment,
VenueIndexQuery,
VenueRentalIndexFragment,
VenueRentalIndexQuery,
} from "@/gql/graphql";
export type MockState = {
newsHeadlineVersion: "v1" | "v2";
};
export const SLUGS = {
genericUrl: "praktisk", // served at /praktisk, wagtail urlPath /home/praktisk/
association: "testforening",
venue: "storsalen",
news: "test-artikkel",
event: "testkonsert",
} as const;
export function newsTitle(state: MockState): string {
return `Testartikkel (${state.newsHeadlineVersion})`;
}
type Resolver = (vars: Record<string, unknown>, state: MockState) => unknown;
export type Fixtures = Record<string, Resolver>;
// Fragment masking types a fragment spread's slot as an opaque ref, but the
// wire format is simply the fragment's fields. Each piece above proves its
// shape with `satisfies <Fragment>`; this cast lets it slot into the masked
// position of the enclosing query/fragment type.
function mask<T>(value: T): { " $fragmentRefs"?: undefined } {
return value as never;
}
export function makeFixtures(cmsBaseUrl: string): Fixtures {
const inAWeek = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
const occurrenceStart = inAWeek.toISOString();
const occurrenceEnd = new Date(
inAWeek.getTime() + 2 * 60 * 60 * 1000
).toISOString();
const image = {
id: "1",
url: `${cmsBaseUrl}/media/pig.png`,
width: 64,
height: 64,
alt: "En gris",
attribution: null,
} satisfies ImageFragment;
const eventCategory = {
__typename: "EventCategory",
name: "Konsert",
slug: "konsert",
pig: "PIG_PINK",
showInFilters: true,
} satisfies EventCategoryFragment;
const eventOrganizer = {
__typename: "EventOrganizer",
id: "1",
name: "Testforeningen",
slug: "testforeningen",
externalUrl: null,
association: null,
} satisfies EventOrganizerFragment;
const eventListItem = {
__typename: "EventPage",
id: "10",
slug: SLUGS.event,
title: "Testkonsert",
subtitle: "En kveld med smoke tests",
featuredImage: mask(image),
occurrences: [
{
__typename: "EventOccurrence",
id: "100",
start: occurrenceStart,
end: occurrenceEnd,
},
],
} satisfies EventListItemFragment;
const venuePageRef = {
__typename: "VenuePage" as const,
id: "30",
slug: SLUGS.venue,
title: "Storsalen",
preposition: "i",
url: `/lokaler/${SLUGS.venue}/`,
};
const eventOverviewItem = {
...eventListItem,
categories: [mask(eventCategory)],
occurrences: [
{
__typename: "EventOccurrence",
id: "100",
start: occurrenceStart,
end: occurrenceEnd,
venueCustom: null,
venue: venuePageRef,
},
],
organizers: [mask(eventOrganizer)],
} satisfies EventOverviewItemFragment;
const eventPage = {
...eventOverviewItem,
seoTitle: "Testkonsert",
searchDescription: null,
lead: "Bli med på testkonsert.",
pig: "PIG_PINK",
facebookUrl: null,
ticketUrl: null,
free: true,
priceRegular: null,
priceMember: null,
priceStudent: null,
body: [],
} satisfies EventFragment;
const eventIndex = {
__typename: "EventIndex",
id: "3",
slug: "arrangementer",
seoTitle: "Arrangementer",
searchDescription: null,
title: "Arrangementer",
} satisfies EventIndexFragment;
const newsListItem = (state: MockState) =>
({
__typename: "NewsPage",
id: "20",
slug: SLUGS.news,
title: newsTitle(state),
firstPublishedAt: "2026-08-01T12:00:00+02:00",
excerpt: "Et utdrag.",
featuredImage: mask(image),
}) satisfies NewsListItemFragment;
const newsPage = (state: MockState) =>
({
...newsListItem(state),
seoTitle: newsTitle(state),
searchDescription: null,
lead: "Ingressen.",
body: [],
}) satisfies NewsFragment;
const newsIndex = {
__typename: "NewsIndex",
id: "2",
slug: "aktuelt",
seoTitle: "Aktuelt",
searchDescription: null,
title: "Aktuelt",
lead: null,
} satisfies NewsIndexFragment;
const home = {
__typename: "HomePage",
featuredEvents: [],
} satisfies HomeFragment;
const genericPage = {
__typename: "GenericPage",
id: "40",
urlPath: `/home/${SLUGS.genericUrl}/`,
seoTitle: "Praktisk info",
searchDescription: null,
title: "Praktisk info",
lead: "Alt du trenger å vite.",
pig: "PIG_PINK",
body: [],
} satisfies GenericFragment;
const association = {
__typename: "AssociationPage",
id: "50",
slug: SLUGS.association,
title: "Testforeningen",
seoTitle: "Testforeningen",
searchDescription: null,
excerpt: "En forening for testing.",
lead: null,
associationType: "Kultur",
websiteUrl: null,
body: [],
logo: null,
} satisfies AssociationFragment;
const associationIndex = {
__typename: "AssociationIndex",
title: "Foreninger",
seoTitle: "Foreninger",
searchDescription: null,
lead: null,
body: [],
} satisfies AssociationIndexFragment;
const venue = {
__typename: "VenuePage",
id: "30",
slug: SLUGS.venue,
title: "Storsalen",
seoTitle: "Storsalen",
searchDescription: null,
showAsBookable: true,
showInOverview: true,
floor: "2",
preposition: "i",
usedFor: "Konserter",
techSpecsUrl: null,
capabilityAudio: null,
capabilityAudioVideo: null,
capabilityBar: null,
capabilityLighting: null,
capacityLegal: "450",
capacityStanding: "450",
capacitySitting: "200",
images: [],
body: [],
featuredImage: null,
} satisfies VenueFragment;
const venueIndex = {
__typename: "VenueIndex",
title: "Lokaler",
seoTitle: "Lokaler",
searchDescription: null,
lead: null,
body: [],
} satisfies VenueIndexFragment;
const venueRentalIndex = {
__typename: "VenueRentalIndex",
title: "Utleie",
seoTitle: "Utleie",
searchDescription: null,
lead: null,
body: [],
} satisfies VenueRentalIndexFragment;
const contactIndex = {
__typename: "ContactIndex",
title: "Kontakt",
seoTitle: "Kontakt",
searchDescription: null,
lead: null,
body: [],
} satisfies ContactIndexFragment;
const sponsorsPage = {
__typename: "SponsorsPage",
title: "Sponsorer",
seoTitle: "Sponsorer",
searchDescription: null,
lead: null,
body: [],
sponsors: [],
} satisfies SponsorsPageFragment;
const studioPage = {
__typename: "StudioPage",
id: "60",
title: "Studio",
seoTitle: "Studio",
searchDescription: null,
lead: null,
pig: "PIG_PINK",
logo: null,
body: [],
} satisfies StudioFragment;
const openingHoursSet = {
name: "Vanlige åpningstider",
effectiveFrom: "2026-01-01",
effectiveTo: null,
announcement: null,
items: [],
} satisfies OpeningHoursSetFragment;
return {
// --- build time: generateStaticParams ---
allGenericSlugs: () =>
({
pages: [{ id: "40", urlPath: `/home/${SLUGS.genericUrl}/` }],
}) satisfies AllGenericSlugsQuery,
allAssociationSlugs: () =>
({
pages: [{ id: "50", slug: SLUGS.association }],
}) satisfies AllAssociationSlugsQuery,
allVenueSlugs: () =>
({
pages: [{ id: "30", slug: SLUGS.venue }],
}) satisfies AllVenueSlugsQuery,
// --- build time: static pages ---
home: (_vars, state) =>
({
events: { futureEvents: [mask(eventListItem)] },
home: mask(home),
news: [mask(newsListItem(state))],
}) satisfies HomeQuery,
news: (_vars, state) =>
({
index: mask(newsIndex),
news: [mask(newsListItem(state))],
}) satisfies NewsQuery,
newsIndexMetadata: () =>
({ index: mask(newsIndex) }) satisfies NewsIndexMetadataQuery,
futureEvents: () =>
({
index: mask(eventIndex),
events: { futureEvents: [mask(eventOverviewItem)] },
eventCategories: [mask(eventCategory)],
eventOrganizers: [mask(eventOrganizer)],
venues: [
{
id: "30",
title: "Storsalen",
slug: SLUGS.venue,
preposition: "i",
},
],
}) satisfies FutureEventsQuery,
eventIndexMetadata: () =>
({ index: mask(eventIndex) }) satisfies EventIndexMetadataQuery,
allAssociations: () =>
({
index: mask(associationIndex),
associations: [mask(association)],
}) satisfies AllAssociationsQuery,
contacts: () => ({ index: mask(contactIndex) }) satisfies ContactsQuery,
venueIndex: () =>
({ index: mask(venueIndex), venues: [mask(venue)] }) satisfies VenueIndexQuery,
venueRentalIndex: () =>
({
index: mask(venueRentalIndex),
venues: [mask(venue)],
}) satisfies VenueRentalIndexQuery,
sponsors: () => ({ page: mask(sponsorsPage) }) satisfies SponsorsQuery,
studio: () => ({ page: mask(studioPage) }) satisfies StudioQuery,
openingHoursSets: () =>
({ openingHoursSets: [mask(openingHoursSet)] }) satisfies OpeningHoursSetsQuery,
// --- build time: prerendered dynamic pages (null for unknown slugs → notFound) ---
genericPageByUrl: (vars) =>
({
page:
vars.urlPath === `/home/${SLUGS.genericUrl}/` ? mask(genericPage) : null,
}) satisfies GenericPageByUrlQuery,
associationBySlug: (vars) =>
({
association: vars.slug === SLUGS.association ? mask(association) : null,
}) satisfies AssociationBySlugQuery,
venueBySlug: (vars) =>
({
venue: vars.slug === SLUGS.venue ? mask(venue) : null,
}) satisfies VenueBySlugQuery,
// --- runtime ---
newsBySlug: (vars, state) =>
({
news: vars.slug === SLUGS.news ? mask(newsPage(state)) : null,
}) satisfies NewsBySlugQuery,
eventBySlug: (vars) =>
({
event: vars.slug === SLUGS.event ? mask(eventPage) : null,
}) satisfies EventBySlugQuery,
search: (vars) =>
({
results:
typeof vars.query === "string" && vars.query.length > 0
? [
{
__typename: "GenericPage",
id: "40",
title: "Praktisk info",
url: `/${SLUGS.genericUrl}/`,
lead: "Alt du trenger å vite.",
},
]
: [],
}) satisfies SearchQuery,
previewPage: (_vars, state) =>
({
page: { __typename: "NewsPage" as const, ...mask(newsPage(state)) },
}) satisfies PreviewPageQuery,
};
}