diff --git a/scripts/review/documents.js b/scripts/review/documents.js index 9216eac42..9c899284c 100644 --- a/scripts/review/documents.js +++ b/scripts/review/documents.js @@ -23,7 +23,11 @@ function readRegistry(source) { return entries(source).map((e) => e.values); } function validLocation(location) { - if (!/^[a-zA-Z0-9][a-zA-Z0-9_./-]*$/.test(location) || location.split('/').some((p) => !p || p === '.' || p === '..')) + if ( + typeof location !== 'string' || + !/^[a-zA-Z0-9][a-zA-Z0-9_./-]*$/.test(location) || + location.split('/').some((p) => !p || p === '.' || p === '..') + ) throw Error('Invalid document location'); return location; } @@ -40,6 +44,8 @@ const ASSET_ROOTS = ['figures', 'bibliographies', 'files', 'downloads']; function registryMap(source) { const result = new Map(); for (const entry of readRegistry(source)) { + // Download-only landing-page tiles have no MDX document identity. + if (!Object.hasOwn(entry, 'doc_location') && typeof entry.downloadUrl === 'string' && entry.downloadUrl.trim()) continue; const location = validLocation(entry.doc_location); if (result.has(location)) throw Error(`Duplicate document location: ${location}`); result.set(location, entry); diff --git a/tests/review-documents.test.js b/tests/review-documents.test.js index 5d39c40b5..2f4322ccc 100644 --- a/tests/review-documents.test.js +++ b/tests/review-documents.test.js @@ -1,7 +1,27 @@ const test = require('node:test'); const assert = require('node:assert/strict'); -const { readRegistry, flipDraft, documentScope, publicationChanges, registryChanges } = require('../scripts/review/documents'); +const { readRegistry, flipDraft, documentScope, publicationChanges, registryChanges, validLocation } = require('../scripts/review/documents'); const source = `const docs = [{doc_location:'a/manual', active:true, draft:true},{doc_location:'b/manual',active:true,draft:true}]; module.exports={docs};`; +test('actual registry supports PDF-only tiles while detecting document changes', () => { + const registry = require('node:fs').readFileSync(require('node:path').join(__dirname, '../src/docConfig.js'), 'utf8'); + assert.deepEqual(registryChanges(registry, registry), []); + const changed = registry.replace('const docs = [', "const docs = [{doc_location:'regression/manual',active:true,draft:true},"); + assert.deepEqual(registryChanges(registry, changed), ['regression/manual']); +}); + +test('PDF-only tiles need no document path but malformed document entries are rejected', () => { + const pdf = `const docs=[{downloadUrl:'/report.pdf',doc_name:'Report'}];`; + assert.deepEqual(registryChanges(pdf, pdf), []); + for (const entry of ['{}', "{downloadUrl:''}", "{doc_location:null,downloadUrl:'/report.pdf'}"]) { + assert.throws(() => registryChanges(`const docs=[${entry}];`, source), /Invalid document location/); + } +}); + +test('document locations reject non-string values with a validation error', () => { + for (const value of [undefined, null, 123, true, {}, '']) { + assert.throws(() => validLocation(value), /Invalid document location/); + } +}); test('only the explicitly selected registry flag changes', () => { const result = flipDraft(source, 'a/manual'); assert.equal(readRegistry(result)[0].draft, false);