Add initial project files
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { resolvePageRenderData, resolvePreviewRenderData } from '#web-runtime/api/render-data';
|
||||
import { createEmptySiteSettings } from '#web-runtime/data/normalize';
|
||||
import type { PageDocument, SiteSettings } from '#web-runtime/types';
|
||||
|
||||
const { resolvePage, getSettings, getPreview } = vi.hoisted(() => ({
|
||||
resolvePage: vi.fn(),
|
||||
getSettings: vi.fn(),
|
||||
getPreview: vi.fn()
|
||||
}));
|
||||
|
||||
vi.mock('#web-runtime/api/web-runtime-api', () => ({
|
||||
getWebRuntimeAPI: () => ({ resolvePage, getSettings, getPreview })
|
||||
}));
|
||||
|
||||
function page(overrides: Partial<PageDocument> = {}): PageDocument {
|
||||
return {
|
||||
id: '7',
|
||||
status: 'published',
|
||||
locale: 'en',
|
||||
path: 'about',
|
||||
groupKey: 'about',
|
||||
layout: 'landing',
|
||||
title: 'About us',
|
||||
seo: { title: 'About us', description: '' },
|
||||
breadcrumbs: [],
|
||||
blocks: [],
|
||||
publishedAt: null,
|
||||
updatedAt: null,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
function settings(locale = 'en'): SiteSettings {
|
||||
return {
|
||||
locale,
|
||||
header: {
|
||||
menus: [],
|
||||
ctaItems: [
|
||||
{
|
||||
linkType: 'external',
|
||||
pageId: '',
|
||||
externalUrl: 'https://example.com',
|
||||
openInNewTab: false,
|
||||
label: 'CTA',
|
||||
variant: 'primary'
|
||||
}
|
||||
]
|
||||
},
|
||||
footer: { copyright: '© Crux', menus: [] },
|
||||
legal: { privacyPath: 'privacy', termsPath: 'terms' }
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
resolvePage.mockReset();
|
||||
getSettings.mockReset();
|
||||
getPreview.mockReset();
|
||||
});
|
||||
|
||||
// resolvePageRenderData is wrapped in React cache(); outside an RSC render
|
||||
// react's cache has no store and calls straight through, but each test still
|
||||
// uses a distinct locale/path pair so results can never leak between tests.
|
||||
|
||||
describe('resolvePageRenderData', () => {
|
||||
it('returns null when the backend cannot resolve the page', async () => {
|
||||
resolvePage.mockResolvedValue(null);
|
||||
|
||||
expect(await resolvePageRenderData('en', 'missing')).toBeNull();
|
||||
expect(getSettings).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('refuses to render a page that is not published (the unpublish gate)', async () => {
|
||||
resolvePage.mockResolvedValue({
|
||||
page: page({ status: 'draft', path: 'draft-page' }),
|
||||
languageAlternatives: []
|
||||
});
|
||||
|
||||
expect(await resolvePageRenderData('en', 'draft-page')).toBeNull();
|
||||
expect(getSettings).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('trims the locale and normalizes the path before asking the backend', async () => {
|
||||
resolvePage.mockResolvedValue(null);
|
||||
|
||||
await resolvePageRenderData(' en ', '/about/team/');
|
||||
|
||||
expect(resolvePage).toHaveBeenCalledWith({ locale: 'en', path: 'about/team' });
|
||||
});
|
||||
|
||||
it('uses the settings bundled into the resolution without a second request', async () => {
|
||||
const bundled = settings();
|
||||
resolvePage.mockResolvedValue({
|
||||
page: page({ path: 'bundled' }),
|
||||
settings: bundled,
|
||||
languageAlternatives: [{ locale: 'de', path: 'gebuendelt' }]
|
||||
});
|
||||
|
||||
const data = await resolvePageRenderData('en', 'bundled');
|
||||
|
||||
expect(data?.settings).toBe(bundled);
|
||||
expect(data?.languageAlternatives).toEqual([{ locale: 'de', path: 'gebuendelt' }]);
|
||||
expect(getSettings).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('fetches settings separately when the resolution has none', async () => {
|
||||
const fetched = settings();
|
||||
resolvePage.mockResolvedValue({ page: page({ path: 'fetched' }), languageAlternatives: [] });
|
||||
getSettings.mockResolvedValue(fetched);
|
||||
|
||||
const data = await resolvePageRenderData('en', 'fetched');
|
||||
|
||||
expect(getSettings).toHaveBeenCalledWith('en');
|
||||
expect(data?.settings).toBe(fetched);
|
||||
});
|
||||
|
||||
it('falls back to empty settings when the backend has none at all', async () => {
|
||||
resolvePage.mockResolvedValue({ page: page({ path: 'bare' }), languageAlternatives: [] });
|
||||
getSettings.mockResolvedValue(null);
|
||||
|
||||
const data = await resolvePageRenderData('en', 'bare');
|
||||
|
||||
expect(data?.settings).toEqual(createEmptySiteSettings('en'));
|
||||
});
|
||||
|
||||
it('returns the resolved page unchanged', async () => {
|
||||
const resolved = page({ path: 'unchanged' });
|
||||
resolvePage.mockResolvedValue({
|
||||
page: resolved,
|
||||
settings: settings(),
|
||||
languageAlternatives: []
|
||||
});
|
||||
|
||||
const data = await resolvePageRenderData('en', 'unchanged');
|
||||
|
||||
expect(data?.page).toBe(resolved);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolvePreviewRenderData', () => {
|
||||
it('returns null for an empty token without calling the backend', async () => {
|
||||
expect(await resolvePreviewRenderData('')).toBeNull();
|
||||
expect(getPreview).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns null when the token resolves to nothing', async () => {
|
||||
getPreview.mockResolvedValue(null);
|
||||
|
||||
expect(await resolvePreviewRenderData('token-1')).toBeNull();
|
||||
expect(getPreview).toHaveBeenCalledWith('token-1');
|
||||
});
|
||||
|
||||
it('renders a draft: previews deliberately skip the published gate', async () => {
|
||||
getPreview.mockResolvedValue({
|
||||
page: page({ status: 'draft' }),
|
||||
settings: settings(),
|
||||
languageAlternatives: []
|
||||
});
|
||||
|
||||
const data = await resolvePreviewRenderData('token-2');
|
||||
|
||||
expect(data?.page.status).toBe('draft');
|
||||
});
|
||||
|
||||
it('falls back to empty settings in the locale of the previewed page', async () => {
|
||||
getPreview.mockResolvedValue({
|
||||
page: page({ locale: 'de', status: 'draft' }),
|
||||
languageAlternatives: []
|
||||
});
|
||||
|
||||
const data = await resolvePreviewRenderData('token-3');
|
||||
|
||||
expect(data?.settings).toEqual(createEmptySiteSettings('de'));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user