seo: align public identity and social metadata

This commit is contained in:
2026-09-05 00:41:08 +03:00
parent da1b5882ed
commit eae03b2cd2
6 changed files with 252 additions and 52 deletions

View File

@@ -101,8 +101,46 @@ const writeValidArtifacts = async (site?: SiteConfig) => {
};
describe('artifact verification', () => {
it('accepts the complete private preview artifact set', async () => {
const { directory, inputs, contract } = await writeValidArtifacts();
it('accepts one exact canonical, Open Graph, Twitter, and privacy-safe JSON-LD metadata set', async () => {
const { directory, html, inputs, contract } = await writeValidArtifacts();
const document = new DOMParser().parseFromString(html, 'text/html');
const previewAsset = inputs.manifest.assets.find(
({ id }) => id === inputs.content.metadata.socialPreviewAssetId,
)!;
const expectedImage = `https://portfolio.localhost/assets/${previewAsset.fileName}`;
expect(document.querySelectorAll('link[rel="canonical"]')).toHaveLength(1);
expect(document.querySelector('link[rel="canonical"]')?.getAttribute('href'))
.toBe('https://portfolio.localhost/');
for (const [attribute, value] of [
['meta[property="og:type"]', 'profile'],
['meta[property="og:title"]', 'Luca Pîslaru — System Administrator & Full-Stack Developer'],
['meta[property="og:description"]', inputs.content.metadata.description],
['meta[property="og:url"]', 'https://portfolio.localhost/'],
['meta[property="og:image"]', expectedImage],
['meta[name="twitter:card"]', 'summary_large_image'],
['meta[name="twitter:title"]', 'Luca Pîslaru — System Administrator & Full-Stack Developer'],
['meta[name="twitter:description"]', inputs.content.metadata.description],
['meta[name="twitter:image"]', expectedImage],
] as const) {
const nodes = document.querySelectorAll(attribute);
expect(nodes, attribute).toHaveLength(1);
expect(nodes[0]?.getAttribute('content'), attribute).toBe(value);
}
const jsonLdNodes = document.querySelectorAll('script[type="application/ld+json"]');
expect(jsonLdNodes).toHaveLength(1);
const jsonLd = JSON.parse(jsonLdNodes[0]!.textContent ?? '');
expect(jsonLd.mainEntity.homeLocation).toEqual({
'@type': 'Place',
address: {
'@type': 'PostalAddress',
addressLocality: 'Bucharest',
addressCountry: 'RO',
},
});
expect(JSON.stringify(jsonLd)).not.toMatch(
/streetAddress|postalCode|telephone|interactionStatistic|"image"/iu,
);
await expect(verifyArtifactDirectory(directory, inputs, contract)).resolves.toBeUndefined();
});
@@ -204,6 +242,46 @@ describe('artifact verification', () => {
mutate: (html: string) => html.replace('</head>', '<link rel="canonical" href="https://portfolio.localhost/" /></head>'),
error: /exactly one canonical/i,
},
{
name: 'duplicate Twitter card metadata',
mutate: (html: string) => html.replace(
'</head>',
'<meta name="twitter:card" content="summary_large_image" /></head>',
),
error: /exactly one twitter:card/i,
},
{
name: 'an Open Graph canonical drift',
mutate: (html: string) => html.replace(
'<meta property="og:url" content="https://portfolio.localhost/" />',
'<meta property="og:url" content="https://other.example.test/" />',
),
error: /og:url.*canonical|metadata.*match/i,
},
{
name: 'a mismatched Open Graph image',
mutate: (html: string) => html.replace(
/<meta property="og:image" content="[^"]+" \/>/u,
'<meta property="og:image" content="https://portfolio.localhost/assets/not-approved.webp" />',
),
error: /og:image.*manifest|metadata.*match/i,
},
{
name: 'a mismatched Twitter image',
mutate: (html: string) => html.replace(
/<meta name="twitter:image" content="[^"]+" \/>/u,
'<meta name="twitter:image" content="https://portfolio.localhost/assets/not-approved.webp" />',
),
error: /twitter:image.*manifest|metadata.*match/i,
},
{
name: 'private JSON-LD location detail',
mutate: (html: string) => html.replace(
'"addressCountry":"RO"',
'"addressCountry":"RO","streetAddress":"Private street"',
),
error: /json-ld.*(?:unsafe|location|public metadata|match)/i,
},
{
name: 'duplicate H1 identity',
mutate: (html: string) => html.replace('</main>', '<h1>Luca Pîslaru</h1></main>'),