import { describe, expect, it } from 'vitest'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { assertManifest, assertIndexDiscoveryMeta, assertStaticIndexShell, assertWebmasterVerificationMeta, assertPluginsDiscoveryMeta, analyzeSitemap, assertPrerenderedPluginRoutes, assertPrerenderedRouteIdentities, assertPrerenderedSkillRoutes, assertPrerenderedTopicRoutes, assertPrerenderedWorkbenchRoutes, assertIndexSocialMeta, assertLlms, assertRobots, assertSitemap, assertSocialCard, extractSitemapLocations, } from './verify-seo-assets.js'; const FIXTURE_ROOT_URL = 'https://owner.github.io/repo/'; const FIXTURE_SOCIAL_IMAGE_URL = 'https://owner.github.io/repo/social-card.png'; const PACKAGE_URL = 'https://www.npmjs.com/package/agentic-awesome-skills'; describe('Bing Webmaster Tools verification metadata', () => { it('requires the current verification token', () => { const html = ''; expect(() => assertWebmasterVerificationMeta(html)).not.toThrow(); expect(() => assertWebmasterVerificationMeta('')) .toThrow('current Bing Webmaster Tools verification token'); }); }); function buildRouteIdentityHtml({ routeUrl, canonicalUrl = routeUrl, ogUrl = routeUrl, socialImageUrl = FIXTURE_SOCIAL_IMAGE_URL, jsonLd = [], } = {}) { return ` `; } function writeRouteIdentityFixture(distDir, routeUrl, html) { const routePath = new URL(routeUrl).pathname.replace(/^\/repo\/?/, ''); const filePath = path.join(distDir, routePath || '.', 'index.html'); fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, html); } function currentIdentityJsonLd(routeUrl) { const entries = [ { '@context': 'https://schema.org', '@type': 'WebPage', url: routeUrl, mainEntityOfPage: routeUrl, }, ]; const relativeRoute = new URL(routeUrl).pathname.replace(new URL(FIXTURE_ROOT_URL).pathname, ''); if (routeUrl === FIXTURE_ROOT_URL || relativeRoute.startsWith('topics/')) { const sourceCode = { '@context': 'https://schema.org', '@type': 'SoftwareSourceCode', url: 'https://github.com/sickn33/agentic-awesome-skills', codeRepository: 'https://github.com/sickn33/agentic-awesome-skills', mainEntityOfPage: routeUrl, sameAs: [...new Set([routeUrl, FIXTURE_ROOT_URL, 'https://www.npmjs.com/package/agentic-awesome-skills'])], }; entries.push({ '@context': 'https://schema.org', '@type': 'Organization', '@id': 'https://github.com/sickn33/agentic-awesome-skills#organization', name: 'Agentic Awesome Skills', url: 'https://github.com/sickn33/agentic-awesome-skills', sameAs: [ 'https://x.com/AASkills_', 'https://www.npmjs.com/package/agentic-awesome-skills', FIXTURE_ROOT_URL, ], }, { '@context': 'https://schema.org', '@type': 'WebSite', url: FIXTURE_ROOT_URL.replace(/\/$/, ''), sameAs: 'https://github.com/sickn33/agentic-awesome-skills', potentialAction: { '@type': 'SearchAction', target: `${FIXTURE_ROOT_URL.replace(/\/$/, '')}/?q={search_term_string}`, }, }, sourceCode); } else if (relativeRoute === 'plugins/') { entries.push({ '@context': 'https://schema.org', '@type': 'Organization', '@id': 'https://github.com/sickn33/agentic-awesome-skills#organization', name: 'Agentic Awesome Skills', url: 'https://github.com/sickn33/agentic-awesome-skills', }); } return entries; } describe('seo assets verification helpers', () => { it('extracts sitemap location values in declaration order', () => { const xml = ` https://example.com/ https://example.com/skill/agent-a `; const locs = extractSitemapLocations(xml); expect(locs).toEqual([ 'https://example.com/', 'https://example.com/skill/agent-a', ]); }); it('validates a canonical sitemap with base path and enough top skills', () => { const xml = ` https://owner.github.io/repo/ https://owner.github.io/repo/plugins/ https://owner.github.io/repo/topics/antigravity-cli-skills/ https://owner.github.io/repo/skill/agent-a/ https://owner.github.io/repo/skill/agent-b/ `; expect(() => assertSitemap(xml, { minSkillUrls: 2 })).not.toThrow(); }); it('rejects sitemap routes that switch away from the homepage origin', () => { const xml = ` ${FIXTURE_ROOT_URL} https://evil.example/repo/skill/agent-a/ `; expect(() => analyzeSitemap(xml)).toThrow('share the homepage origin'); }); it('uses the explicit sitemap root when the homepage is not the first route', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routes = [`${FIXTURE_ROOT_URL}skill/agent-a/`, FIXTURE_ROOT_URL]; const report = analyzeSitemap(`${routes.map((url) => `${url}`).join('')}`); for (const routeUrl of routes) { writeRouteIdentityFixture( distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, jsonLd: currentIdentityJsonLd(routeUrl) }), ); } expect(report.rootUrl).toBe(FIXTURE_ROOT_URL); expect(() => assertPrerenderedRouteIdentities(routes, distDir, '/repo', report.rootUrl)).not.toThrow(); }); it('throws when sitemap has duplicated URLs', () => { const xml = ` https://example.com/ https://example.com/ `; expect(() => assertSitemap(xml)).toThrow('duplicated'); }); it('throws when hosted sitemap verification sees localhost URLs', () => { const xml = ` http://localhost/repo/ http://localhost/repo/skill/agent-a `; expect(() => assertSitemap(xml, { requireHostedUrl: true })).toThrow('localhost'); }); it('rejects a self-consistent legacy hosted catalog root', () => { const legacyRoot = 'https://sickn33.github.io/legacy-catalog/'; const xml = ` ${legacyRoot} ${legacyRoot}skill/agent-a/ `; expect(() => assertSitemap(xml, { requireHostedUrl: true })).toThrow('Hosted sitemap root'); }); it('rejects slashless sitemap routes', () => { const xml = ` ${FIXTURE_ROOT_URL} ${FIXTURE_ROOT_URL}skill/agent-a `; expect(() => assertSitemap(xml)).toThrow('trailing slash'); }); it('requires robots directives', () => { const robots = ` User-agent: * Allow: / User-agent: GPTBot Allow: / User-agent: OAI-SearchBot Allow: / User-agent: ClaudeBot Allow: / User-agent: PerplexityBot Allow: / Sitemap: https://example.com/sitemap.xml `; expect(() => assertRobots(robots)).not.toThrow(); }); it('requires robots.txt to point exactly to the current sitemap', () => { const robots = ` User-agent: * Allow: / User-agent: GPTBot User-agent: OAI-SearchBot User-agent: ClaudeBot User-agent: PerplexityBot Sitemap: https://owner.github.io/legacy-repo/sitemap.xml `; expect(() => assertRobots(robots, { expectedSitemapUrl: `${FIXTURE_ROOT_URL}sitemap.xml` })).toThrow( 'current sitemap exactly', ); }); it('requires exact canonical and og:url values for every sitemap route', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeUrl = `${FIXTURE_ROOT_URL}skill/agent-a/`; writeRouteIdentityFixture( distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, canonicalUrl: `${FIXTURE_ROOT_URL}skill/legacy-agent-a/`, jsonLd: currentIdentityJsonLd(routeUrl), }), ); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow('rel="canonical"'); writeRouteIdentityFixture( distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, ogUrl: `${FIXTURE_ROOT_URL}skill/legacy-agent-a/`, jsonLd: currentIdentityJsonLd(routeUrl), }), ); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow('og:url'); }); it('requires exact current social-card URLs for og and Twitter images', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeUrl = `${FIXTURE_ROOT_URL}skill/agent-a/`; writeRouteIdentityFixture( distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, socialImageUrl: `${FIXTURE_ROOT_URL}social-card-legacy.png`, jsonLd: currentIdentityJsonLd(routeUrl), }), ); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow('og:image'); }); it('rejects legacy repository and catalog URLs only in JSON-LD identity fields', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeUrl = `${FIXTURE_ROOT_URL}skill/agent-a/`; writeRouteIdentityFixture( distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, jsonLd: [ ...currentIdentityJsonLd(routeUrl), { '@type': 'Thing', url: 'https://github.com/sickn33/legacy-awesome-skills' }, ], }), ); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow('legacy first-party GitHub'); writeRouteIdentityFixture( distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, jsonLd: [ ...currentIdentityJsonLd(routeUrl), { '@type': 'Thing', url: 'https://owner.github.io/legacy-repo/' }, ], }), ); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow('legacy first-party Pages'); }); it('requires the primary route JSON-LD identity to equal the sitemap route', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeUrl = `${FIXTURE_ROOT_URL}skill/agent-a/`; const wrongRoute = `${FIXTURE_ROOT_URL}skill/wrong/`; writeRouteIdentityFixture( distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, jsonLd: [{ '@type': 'SoftwareApplication', '@id': wrongRoute, url: wrongRoute, mainEntityOfPage: wrongRoute, }], }), ); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow( 'SoftwareApplication @id', ); }); it('rejects duplicate SoftwareApplication route identities', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeUrl = `${FIXTURE_ROOT_URL}skill/agent-a/`; const application = { '@type': 'SoftwareApplication', '@id': routeUrl, url: routeUrl, mainEntityOfPage: routeUrl, }; writeRouteIdentityFixture( distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, jsonLd: [application, { ...application }] }), ); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow( 'exactly one SoftwareApplication', ); }); it('rejects a wrong-owner repository and a missing npm identity', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeUrl = FIXTURE_ROOT_URL; const wrongRepository = currentIdentityJsonLd(routeUrl).map((entry) => entry['@type'] === 'SoftwareSourceCode' ? { ...entry, url: 'https://github.com/other-owner/agentic-awesome-skills', codeRepository: 'https://github.com/other-owner/agentic-awesome-skills' } : entry, ); writeRouteIdentityFixture(distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, jsonLd: wrongRepository })); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow( 'current repository URL', ); const missingPackage = currentIdentityJsonLd(routeUrl).map((entry) => { if (!['Organization', 'SoftwareSourceCode'].includes(entry['@type'])) return entry; return { ...entry, sameAs: (entry.sameAs || []).filter((value) => value !== PACKAGE_URL) }; }); writeRouteIdentityFixture(distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, jsonLd: missingPackage })); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow( 'exactly the current social, npm package, and catalog identities', ); }); it('rejects drifted WebSite and nested SoftwareSourceCode identities', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeUrl = `${FIXTURE_ROOT_URL}topics/antigravity-cli-skills/`; const wrongWebSite = currentIdentityJsonLd(routeUrl).map((entry) => entry['@type'] === 'WebSite' ? { ...entry, url: `${FIXTURE_ROOT_URL}topics/wrong/`, sameAs: 'https://github.com/other-owner/legacy-agentic-awesome-skills' } : entry, ); writeRouteIdentityFixture(distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, jsonLd: wrongWebSite })); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow( 'WebSite JSON-LD must use the exact current catalog base URL', ); const nestedWrongSource = { '@type': 'SoftwareSourceCode', url: 'https://github.com/other-owner/legacy-agentic-awesome-skills', codeRepository: 'https://github.com/other-owner/legacy-agentic-awesome-skills', mainEntityOfPage: `${FIXTURE_ROOT_URL}topics/wrong/`, sameAs: [FIXTURE_ROOT_URL, 'https://www.npmjs.com/package/agentic-awesome-skills'], }; const wrongNestedIdentity = currentIdentityJsonLd(routeUrl).map((entry) => entry['@type'] === 'WebPage' ? { ...entry, about: nestedWrongSource } : entry, ); writeRouteIdentityFixture(distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, jsonLd: wrongNestedIdentity })); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow( 'current repository URL', ); }); it('rejects extra or nested wrong-owner schema identity fields', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeUrl = `${FIXTURE_ROOT_URL}topics/antigravity-cli-skills/`; const otherRepository = 'https://github.com/other-owner/legacy-agentic-awesome-skills'; const cases = [ currentIdentityJsonLd(routeUrl).map((entry) => entry['@type'] === 'Organization' ? { ...entry, sameAs: [...entry.sameAs, otherRepository] } : entry), currentIdentityJsonLd(routeUrl).map((entry) => entry['@type'] === 'WebPage' ? { ...entry, author: { '@type': 'Organization', '@id': 'https://github.com/sickn33/agentic-awesome-skills#organization', url: 'https://github.com/sickn33/agentic-awesome-skills', sameAs: [otherRepository], }, } : entry), currentIdentityJsonLd(routeUrl).map((entry) => entry['@type'] === 'SoftwareSourceCode' ? { ...entry, '@id': `${otherRepository}#source` } : entry), currentIdentityJsonLd(routeUrl).map((entry) => entry['@type'] === 'WebSite' ? { ...entry, '@id': `${otherRepository}#website` } : entry), ]; const expectedMessages = [ 'sameAs may contain only exact current project identities', 'sameAs may contain only exact current project identities', 'SoftwareSourceCode JSON-LD @id', 'WebSite JSON-LD @id', ]; cases.forEach((jsonLd, index) => { writeRouteIdentityFixture(distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, jsonLd })); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow( expectedMessages[index], ); }); }); it('rejects wrong-owner project nodes declared with expanded Schema.org type IRIs', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeUrl = `${FIXTURE_ROOT_URL}topics/antigravity-cli-skills/`; const otherRepository = 'https://github.com/other-owner/legacy-agentic-awesome-skills'; const expandedNodes = [ { '@type': 'https://schema.org/Organization', '@id': `${otherRepository}#organization`, url: otherRepository, sameAs: [otherRepository], }, { '@type': 'http://schema.org/SoftwareSourceCode', '@id': `${otherRepository}#source`, url: otherRepository, codeRepository: otherRepository, mainEntityOfPage: `${FIXTURE_ROOT_URL}topics/wrong/`, sameAs: [otherRepository], }, { '@type': 'schema:WebSite', '@id': `${otherRepository}#website`, url: `${FIXTURE_ROOT_URL}topics/wrong/`, sameAs: otherRepository, }, ]; const expectedMessages = [ 'current repository URL', 'current repository URL', 'exact current catalog base URL', ]; expandedNodes.forEach((about, index) => { const jsonLd = currentIdentityJsonLd(routeUrl).map((entry) => entry['@type'] === 'WebPage' ? { ...entry, about } : entry, ); writeRouteIdentityFixture(distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, jsonLd })); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow( expectedMessages[index], ); }); }); it('rejects prerendered route files reached through a symlink', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const externalRouteDir = path.join(tmpDir, 'external-route'); const routeUrl = `${FIXTURE_ROOT_URL}skill/agent-a/`; fs.mkdirSync(path.join(distDir, 'skill'), { recursive: true }); fs.mkdirSync(externalRouteDir, { recursive: true }); fs.writeFileSync( path.join(externalRouteDir, 'index.html'), buildRouteIdentityHtml({ routeUrl, jsonLd: currentIdentityJsonLd(routeUrl) }), ); try { fs.symlinkSync(externalRouteDir, path.join(distDir, 'skill', 'agent-a'), 'dir'); } catch (error) { if (['EPERM', 'EACCES', 'ENOTSUP'].includes(error?.code)) return; throw error; } expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow('symlinks'); }); it('rejects a verification root beneath a symlinked ancestor', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const physicalParent = path.join(tmpDir, 'physical'); const logicalParent = path.join(tmpDir, 'logical'); const physicalDist = path.join(physicalParent, 'dist'); const routeUrl = `${FIXTURE_ROOT_URL}skill/agent-a/`; writeRouteIdentityFixture( physicalDist, routeUrl, buildRouteIdentityHtml({ routeUrl, jsonLd: currentIdentityJsonLd(routeUrl) }), ); try { fs.symlinkSync(physicalParent, logicalParent, 'dir'); } catch (error) { if (['EPERM', 'EACCES', 'ENOTSUP'].includes(error?.code)) return; throw error; } expect(() => assertPrerenderedRouteIdentities( [routeUrl], path.join(logicalParent, 'dist'), '/repo', FIXTURE_ROOT_URL, )).toThrow('symlinks'); }); it('rejects duplicate canonical and route identity meta tags', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeUrl = `${FIXTURE_ROOT_URL}skill/agent-a/`; const baseHtml = buildRouteIdentityHtml({ routeUrl, jsonLd: currentIdentityJsonLd(routeUrl) }); const cases = [ ['', 'exactly one rel="canonical"'], ['', 'exactly one property="og:url"'], ['', 'exactly one property="og:image"'], ['', 'exactly one name="twitter:image"'], ]; for (const [duplicateTag, expectedMessage] of cases) { writeRouteIdentityFixture(distDir, routeUrl, baseHtml.replace('', `${duplicateTag}`)); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow(expectedMessage); } }); it('rejects duplicate identity tags with browser-valid alternate attribute syntax', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeUrl = `${FIXTURE_ROOT_URL}skill/agent-a/`; const baseHtml = buildRouteIdentityHtml({ routeUrl, jsonLd: currentIdentityJsonLd(routeUrl) }); const cases = [ ['', 'exactly one rel="canonical"'], ['', 'exactly one rel="canonical"'], ['', 'exactly one property="og:url"'], ['', 'exactly one name="twitter:image"'], ]; for (const [duplicateTag, expectedMessage] of cases) { writeRouteIdentityFixture(distDir, routeUrl, baseHtml.replace('', `${duplicateTag}`)); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow(expectedMessage); } }); it('detects duplicate primary JSON-LD with unquoted or spaced type attributes', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeUrl = `${FIXTURE_ROOT_URL}skill/agent-a/`; const application = { '@type': 'SoftwareApplication', '@id': routeUrl, url: routeUrl, mainEntityOfPage: routeUrl, }; const alternateScripts = [ ``, ``, ]; for (const alternateScript of alternateScripts) { const baseHtml = buildRouteIdentityHtml({ routeUrl, jsonLd: [application] }); writeRouteIdentityFixture(distDir, routeUrl, baseHtml.replace('', `${alternateScript}`)); expect(() => assertPrerenderedRouteIdentities([routeUrl], distDir, '/repo', FIXTURE_ROOT_URL)).toThrow( 'exactly one SoftwareApplication', ); } }); it('accepts exact current identities for all generated sitemap routes without policing provenance text', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routes = [FIXTURE_ROOT_URL, `${FIXTURE_ROOT_URL}skill/agent-a/`]; for (const routeUrl of routes) { writeRouteIdentityFixture( distDir, routeUrl, buildRouteIdentityHtml({ routeUrl, jsonLd: [ ...currentIdentityJsonLd(routeUrl), { '@type': 'WebPage', description: 'Compatibility note: migrated from https://owner.github.io/legacy-repo/.', }, ], }), ); } expect(() => assertPrerenderedRouteIdentities(routes, distDir, '/repo', FIXTURE_ROOT_URL)).not.toThrow(); }); it('requires llms.txt discovery signals', () => { const llms = ` # Agentic Awesome Skills Current release: V1.2.3. 1,678+ agentic skills with specialized plugins for Claude Code and Codex CLI. https://github.com/sickn33/agentic-awesome-skills https://sickn33.github.io/agentic-awesome-skills/workbench Canonical source of truth: the GitHub repository is the primary project URL. `; expect(() => assertLlms(llms, { expectedReleaseLabel: 'V1.2.3' })).not.toThrow(); }); it('rejects stale llms.txt release labels', () => { const llms = ` # Agentic Awesome Skills Current release: V1.2.2. 1,678+ agentic skills with specialized plugins for Claude Code and Codex CLI. https://github.com/sickn33/agentic-awesome-skills https://sickn33.github.io/agentic-awesome-skills/workbench Canonical source of truth: the GitHub repository is the primary project URL. `; expect(() => assertLlms(llms, { expectedReleaseLabel: 'V1.2.3' })).toThrow('current release'); }); it('requires social image tags in rendered index html', () => { const html = ` `; expect(() => assertIndexSocialMeta(html)).not.toThrow(); }); it('requires current discovery copy in rendered index html', () => { const html = ` Agentic Awesome Skills GitHub | 1,678+ AI coding skills `; expect(() => assertIndexDiscoveryMeta(html)).not.toThrow(); }); it('rejects stale count labels in rendered index JSON-LD', () => { const html = ` Agentic Awesome Skills GitHub | 1,678+ AI coding skills `; expect(() => assertIndexDiscoveryMeta(html)).toThrow('stale skill count'); }); it('requires current discovery copy in the source index shell', () => { const html = ` Agentic Awesome Skills GitHub | 1,678+ AI coding skills `; expect(() => assertStaticIndexShell(html)).not.toThrow(); }); it('requires current discovery copy in the social card', () => { const svg = ` Agentic Awesome Skills social card Social preview with 1,678 plus agentic skills. 1,678+ Agentic Skills `; expect(() => assertSocialCard(svg)).not.toThrow(); }); it('accepts a 1200x630 PNG social card', () => { const png = Buffer.alloc(24); Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]).copy(png, 0); png.write('IHDR', 12, 'ascii'); png.writeUInt32BE(1200, 16); png.writeUInt32BE(630, 20); expect(() => assertSocialCard(png)).not.toThrow(); }); it('rejects stale social card count labels', () => { const svg = ` Agentic Awesome Skills social card 1,700+ Agentic Skills `; expect(() => assertSocialCard(svg)).toThrow('Social card'); }); it('requires plugin landing discovery copy in rendered plugin html', () => { const html = ` AAS Specialized Plugins | 15 AI coding workflow packs `; expect(() => assertPluginsDiscoveryMeta(html)).not.toThrow(); }); it('validates prerendered topic route files when present', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeFile = path.join(distDir, 'topics', 'antigravity-cli-skills', 'index.html'); fs.mkdirSync(path.dirname(routeFile), { recursive: true }); fs.writeFileSync( routeFile, 'Antigravity CLI Skills | Installable AI agent playbooks
A GitHub repository for installable AI agent skills
', ); const xml = ` https://owner.github.io/repo/ https://owner.github.io/repo/topics/antigravity-cli-skills/ `; const report = analyzeSitemap(xml, { minSkillUrls: 0 }); expect(() => assertPrerenderedTopicRoutes(report.topicUrls, distDir, report.normalizedRootPath)).not.toThrow(); }); it('validates prerendered skill route files when present', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeFile = path.join(distDir, 'skill', 'agent-a', 'index.html'); fs.mkdirSync(path.dirname(routeFile), { recursive: true }); fs.writeFileSync( routeFile, '
Antigravity CLI skills for agentic coding workflows
', ); const xml = ` https://owner.github.io/repo/ https://owner.github.io/repo/skill/agent-a/ `; const report = analyzeSitemap(xml); expect(() => assertPrerenderedSkillRoutes(report.skillUrls, distDir, report.normalizedRootPath)).not.toThrow(); }); it('validates prerendered plugin route files when present', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeFile = path.join(distDir, 'plugins', 'index.html'); fs.mkdirSync(path.dirname(routeFile), { recursive: true }); fs.writeFileSync( routeFile, 'AAS Specialized Plugins | 15 AI coding workflow packs', ); const xml = ` https://owner.github.io/repo/ https://owner.github.io/repo/plugins/ `; const report = analyzeSitemap(xml, { minSkillUrls: 0 }); expect(() => assertPrerenderedPluginRoutes(report.pluginUrls, distDir, report.normalizedRootPath)).not.toThrow(); }); it('validates the prerendered workbench route and its in-memory review promise', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const routeFile = path.join(distDir, 'workbench', 'index.html'); fs.mkdirSync(path.dirname(routeFile), { recursive: true }); fs.writeFileSync( routeFile, 'Stack Review Workbench | Agentic Awesome Skills', ); const xml = ` https://owner.github.io/repo/ https://owner.github.io/repo/workbench/ `; const report = analyzeSitemap(xml, { minSkillUrls: 0 }); expect(report.workbenchUrls).toEqual(['https://owner.github.io/repo/workbench/']); expect(() => assertPrerenderedWorkbenchRoutes( report.workbenchUrls, distDir, report.normalizedRootPath, )).not.toThrow(); }); it('throws when a prerendered skill file is missing', () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-')); const distDir = path.join(tmpDir, 'dist'); const xml = ` https://owner.github.io/repo/ https://owner.github.io/repo/skill/agent-a/ `; const report = analyzeSitemap(xml); expect(() => assertPrerenderedSkillRoutes(report.skillUrls, distDir, report.normalizedRootPath)).toThrow( 'Missing prerendered page for skill route', ); }); it('rejects missing social image tags', () => { const html = ` `; expect(() => assertIndexSocialMeta(html)).toThrow('twitter:image'); }); it('requires manifest identity and theme fields', () => { const manifest = JSON.stringify( { name: 'Antigravity', short_name: 'AG', theme_color: '#112233', description: 'desc', icons: [{ src: 'icon.svg' }], }, null, 2, ); expect(() => assertManifest(manifest)).not.toThrow(); }); });