Files
smileBeda 48f05bcf8a feat: release meme protocol v1.1.0
Add one-click meme copying, complete viewer actions, fixed-footer pagination, protocol lore, and the Homo Reseticus download chooser.

Install dedicated social and favicon assets, expand local demo data, and synchronize the changelog, documentation, package, and OpenAPI release metadata.
2026-08-14 18:54:37 -03:00

94 lines
3.5 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import { feedJson, manifest, renderIndex, sitemapXml } from '../src/seo.js';
const id = 'a'.repeat(64);
const meme = {
id,
createdAt: '2026-07-24T12:00:00.000Z',
width: 1200,
height: 900,
mime: 'image/webp',
moderationScore: 92
};
const template = [
'<title>__SEO_TITLE__</title>',
'<meta name="description" content="__SEO_DESCRIPTION__">',
'<link rel="canonical" href="__SEO_CANONICAL__">',
'<meta property="og:image" content="__SEO_IMAGE__">',
'<script nonce="__CSP_NONCE__">__SEO_JSON_LD__</script>'
].join('');
test('renders a meme permalink with meme-specific share metadata', () => {
const html = renderIndex({
template,
baseUrl: 'https://memes.example',
nonce: 'test-nonce',
approvedCount: 1,
meme
});
assert.match(html, /<title>Meme 0xAAAA\.\.\.AAAA \| The Meme Protocol<\/title>/);
assert.match(html, new RegExp(`canonical" href="https://memes\\.example/meme/${id}"`));
assert.match(html, new RegExp(`og:image" content="https://memes\\.example/media/${id}"`));
assert.match(html, /"@type":"ImageObject"/);
assert.match(html, /nonce="test-nonce"/);
});
test('renders the homepage with the dedicated social preview image', () => {
const html = renderIndex({
template,
baseUrl: 'https://memes.example',
nonce: 'home-nonce',
approvedCount: 12
});
assert.match(html, /og:image" content="https:\/\/memes\.example\/assets\/social-preview-v1\.png"/);
assert.doesNotMatch(html, /yoonect-logo\.png/);
});
test('uses the supplied favicon suite in the page and web app manifest', () => {
const index = fs.readFileSync(new URL('../public/index.html', import.meta.url), 'utf8');
const appManifest = manifest('https://memes.example');
assert.match(index, /\/assets\/favicon\/favicon\.svg\?v=20260814/);
assert.match(index, /\/assets\/favicon\/favicon-96x96\.png\?v=20260814/);
assert.match(index, /\/favicon\.ico\?v=20260814/);
assert.match(index, /\/assets\/favicon\/apple-touch-icon\.png\?v=20260814/);
assert.doesNotMatch(index, /rel="icon"[^>]+yoonect-logo\.png/);
assert.deepEqual(appManifest.icons.map(({ sizes, purpose }) => ({ sizes, purpose })), [
{ sizes: '192x192', purpose: 'maskable' },
{ sizes: '512x512', purpose: 'maskable' }
]);
assert.match(appManifest.icons[0].src, /web-app-manifest-192x192\.png\?v=20260814$/);
assert.match(appManifest.icons[1].src, /web-app-manifest-512x512\.png\?v=20260814$/);
});
test('renders a shareable lore page with text-file metadata', () => {
const html = renderIndex({
template,
baseUrl: 'https://memes.example',
nonce: 'lore-nonce',
approvedCount: 1,
lore: true
});
assert.match(html, /<title>Lore \| The Meme Protocol<\/title>/);
assert.match(html, /canonical" href="https:\/\/memes\.example\/lore"/);
assert.match(html, /og:image" content="https:\/\/memes\.example\/assets\/social-preview-v1\.png"/);
assert.match(html, /"@type":"CreativeWork"/);
assert.match(html, /https:\/\/memes\.example\/assets\/meme-protocol-lore\.txt/);
});
test('discovery feeds point readers to viewer permalinks', () => {
const feed = feedJson('https://memes.example', [meme]);
const sitemap = sitemapXml('https://memes.example', [meme]);
assert.match(sitemap, /https:\/\/memes\.example\/lore/);
assert.equal(feed.items[0].url, `https://memes.example/meme/${id}`);
assert.equal(feed.items[0].image, `https://memes.example/media/${id}`);
assert.match(sitemap, new RegExp(`https://memes\\.example/meme/${id}`));
});