import assert from 'node:assert/strict' import test from 'node:test' import { GUARD_MESSAGE, findOffenders, flattenPages, guardMessage, isNoindex, noindexRule, pagePattern, pagePatterns, patternCovers } from '../lib/guard.ts' const base = { ssr: true, indexable: true, routeRules: {}, pages: [], noindexRoutes: [], inlineRouteRules: false } test('isNoindex: Nitro route-rule patterns', () => { assert.equal(isNoindex('/x', ['/x/**']), true) assert.equal(isNoindex('/x/a/b', ['/x/**']), true) assert.equal(isNoindex('/xy', ['/x/**']), false) assert.equal(isNoindex('/x/a', ['/x/*']), true) assert.equal(isNoindex('/x/a/b', ['/x/*']), false) assert.equal(isNoindex('/x', ['/x/*']), false) assert.equal(isNoindex('/konto', ['/konto']), true) assert.equal(isNoindex('/konto/dane', ['/konto']), false) assert.equal(isNoindex('/anything', ['/**']), true) assert.equal(isNoindex('/x/a', []), false) assert.equal(isNoindex('/x/a', ['']), false) }) test('patternCovers: a pattern against another pattern', () => { assert.equal(patternCovers('/p/**', '/p/**'), true) assert.equal(patternCovers('/p/**', '/p/:id'), true) assert.equal(patternCovers('/p/*', '/p/:id'), true) assert.equal(patternCovers('/p/*', '/p/**'), false, 'one segment does not cover a catch-all') assert.equal(patternCovers('/p/:id', '/p/edit'), true) assert.equal(patternCovers('/p/edit', '/p/:id'), false, 'a static segment does not cover a parameter') assert.equal(patternCovers('/**', '/**'), true) }) test('pagePattern: Nuxt page paths become route-rule patterns', () => { assert.equal(pagePattern('/posts/:slug()'), '/posts/:slug') assert.equal(pagePattern('/:slug(.*)*'), '/**') assert.equal(pagePattern('/'), '/') assert.equal(pagePattern('/panel'), '/panel') assert.deepEqual(pagePatterns('/a/:id?'), ['/a/:id', '/a']) assert.deepEqual(pagePatterns('/a/:id()'), ['/a/:id']) }) test('flattenPages: children get their full path', () => { const flat = flattenPages([{ path: '/konto', children: [{ path: '', mode: 'client' }, { path: 'dane' }] }]) assert.deepEqual( flat.map((p) => p.fullPath), ['/konto', '/konto', '/konto/dane'], ) }) test('findOffenders: nothing to report on a server-rendered app', () => { assert.deepEqual(findOffenders({ ...base, routeRules: { '/**': { swr: 300 } }, pages: [{ path: '/' }, { path: '/blog/:slug()' }] }), []) }) test('findOffenders: a route rule with ssr false', () => { assert.deepEqual(findOffenders({ ...base, routeRules: { '/x/**': { ssr: false }, '/y': { ssr: true } } }), ['/x/**']) }) test('findOffenders: a declared noindex route passes', () => { assert.deepEqual(findOffenders({ ...base, routeRules: { '/x/**': { ssr: false } }, noindexRoutes: ['/x/**'] }), []) assert.deepEqual(findOffenders({ ...base, routeRules: { '/x/a': { ssr: false } }, noindexRoutes: ['/x/**'] }), []) assert.deepEqual(findOffenders({ ...base, routeRules: { '/x/**': { ssr: false } }, noindexRoutes: ['/x/*'] }), ['/x/**'], 'a one-segment declaration does not cover a catch-all rule') }) test('findOffenders: a .client.vue page, nested or not', () => { assert.deepEqual(findOffenders({ ...base, pages: [{ path: '/panel', mode: 'client' }] }), ['/panel (.client.vue page)']) assert.deepEqual(findOffenders({ ...base, pages: [{ path: '/konto', children: [{ path: 'dane', mode: 'client' }] }] }), ['/konto/dane (.client.vue page)']) assert.deepEqual(findOffenders({ ...base, pages: [{ path: '/panel', mode: 'client' }], noindexRoutes: ['/panel'] }), []) assert.deepEqual(findOffenders({ ...base, pages: [{ path: '/p/:id()', mode: 'client' }], noindexRoutes: ['/p/**'] }), []) }) test('findOffenders: global ssr false, reported once', () => { assert.deepEqual(findOffenders({ ...base, ssr: false, routeRules: { '/**': { ssr: false } } }), ['ssr: false (the whole app)']) assert.deepEqual(findOffenders({ ...base, ssr: false, noindexRoutes: ['/**'] }), []) }) test('findOffenders: inline route rules are refused', () => { const out = findOffenders({ ...base, inlineRouteRules: true }) assert.equal(out.length, 1) assert.match(out[0]!, /experimental\.inlineRouteRules/) }) test('findOffenders: seo.indexable false checks nothing', () => { assert.deepEqual(findOffenders({ ...base, indexable: false, ssr: false, routeRules: { '/x/**': { ssr: false } }, inlineRouteRules: true }), []) }) test('guardMessage: the D-70 message lists the offenders', () => { const msg = guardMessage(['/x/**', '/panel (.client.vue page)']) assert.ok(msg.startsWith(`${GUARD_MESSAGE}: /x/**, /panel (.client.vue page). `)) assert.ok(msg.includes('seo.noindexRoutes')) }) test('noindexRule: adds X-Robots-Tag and keeps the rest of the rule and an own header', () => { assert.deepEqual(noindexRule(undefined), { headers: { 'x-robots-tag': 'noindex, nofollow' } }) assert.deepEqual(noindexRule({ ssr: false, headers: { 'cache-control': 'no-store' } }), { ssr: false, headers: { 'cache-control': 'no-store', 'x-robots-tag': 'noindex, nofollow' }, }) assert.deepEqual(noindexRule({ headers: { 'X-Robots-Tag': 'none' } }), { headers: { 'X-Robots-Tag': 'none' } }) })