Files
admin/apps/web/tests/structured-data.test.ts

74 lines
2.8 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { articleJsonLd, breadcrumbListJsonLd } from '#web-runtime/lib/structured-data';
import type { PageDocument } from '#web-runtime/types';
function page(overrides: Partial<PageDocument> = {}): PageDocument {
return {
id: '61',
status: 'published',
locale: 'en',
path: 'blog/api-webhooks',
groupKey: 'blog-api-webhooks',
layout: 'blog-item',
title: 'Working with webhooks',
seo: { title: 'Working with webhooks — Crux', description: 'How webhooks work.' },
breadcrumbs: [],
blocks: [],
publishedAt: '2026-06-23T13:28:30.697+00:00',
updatedAt: '2026-08-13T13:28:30.698+00:00',
...overrides
};
}
describe('articleJsonLd', () => {
it('marks up the dates and the hero image', () => {
const media = { url: 'https://cdn.example.com/hero.jpg', alt: 'Hero' };
const data = articleJsonLd(page({ hero: { type: 'low', ctaItems: [], media } }));
expect(data).toEqual({
'@context': 'https://schema.org',
'@type': 'Article',
'headline': 'Working with webhooks — Crux',
'description': 'How webhooks work.',
'datePublished': '2026-06-23T13:28:30.697+00:00',
'dateModified': '2026-08-13T13:28:30.698+00:00',
'image': ['https://cdn.example.com/hero.jpg'],
'inLanguage': 'en'
});
});
it('emits nothing without a publication date, rather than an Article a crawler will drop', () => {
expect(articleJsonLd(page({ publishedAt: null }))).toBeNull();
});
it('omits dateModified when the page has never been changed since publishing', () => {
const published = '2026-06-23T13:28:30.697+00:00';
const data = articleJsonLd(page({ publishedAt: published, updatedAt: published }));
expect(data).not.toHaveProperty('dateModified');
expect(data).toMatchObject({ datePublished: published });
});
it('falls back to the page title and omits fields the page does not have', () => {
const data = articleJsonLd(page({ seo: { title: '', description: '' } }));
expect(data).toMatchObject({ headline: 'Working with webhooks' });
expect(data).not.toHaveProperty('description');
expect(data).not.toHaveProperty('image');
});
});
describe('breadcrumbListJsonLd', () => {
it('numbers the trail from one and leaves the current page without a url', () => {
const data = breadcrumbListJsonLd([
{ label: 'Home', href: '/en' },
{ label: 'Blog', href: '/en/blog' },
{ label: 'Working with webhooks', href: '/en/blog/api-webhooks' }
]) as { itemListElement: Array<Record<string, unknown>> };
expect(data.itemListElement.map((item) => item['position'])).toEqual([1, 2, 3]);
expect(data.itemListElement[0]).toHaveProperty('item');
expect(data.itemListElement[2]).not.toHaveProperty('item');
});
});