From a71c6493d2798332d21a1243f7c61e31f6e433a3 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Mon, 23 Oct 2023 14:17:12 -0400 Subject: [PATCH] Validate that all category slugs match their title or shortTitle (#44749) --- script/reconcile-category-dirs-with-ids.js | 26 +++++++++----- src/content-linter/tests/category-pages.js | 41 +++++++++++++++------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/script/reconcile-category-dirs-with-ids.js b/script/reconcile-category-dirs-with-ids.js index 67d24ce783..c8b1fd140b 100755 --- a/script/reconcile-category-dirs-with-ids.js +++ b/script/reconcile-category-dirs-with-ids.js @@ -25,10 +25,14 @@ const slugger = new GithubSlugger() const contentDir = path.join(ROOT, 'content') +const INCLUDE_MAP_TOPICS = Boolean(JSON.parse(process.env.INCLUDE_MAP_TOPICS || 'false')) + main() async function main() { - const englishCategoryIndices = getEnglishCategoryIndices() + const englishCategoryIndices = getEnglishCategoryIndices().filter((name) => { + return INCLUDE_MAP_TOPICS || name.split(path.sep).length < 5 + }) const shouldRename = [] @@ -36,6 +40,10 @@ async function main() { const contents = fs.readFileSync(categoryIndex, 'utf8') const { data } = frontmatter(contents) + if (data.allowTitleToDifferFromFilename) { + continue + } + const categoryDirPath = path.dirname(categoryIndex) const categoryDirName = path.basename(categoryDirPath) @@ -47,18 +55,20 @@ async function main() { } const title = await renderContent(data.title, context, { textOnly: true }) slugger.reset() - const expectedSlug = slugger.slug(decode(title)) + const expectedSlugs = [slugger.slug(decode(title))] + const shortTitle = data.shortTitle + ? await renderContent(data.shortTitle, context, { textOnly: true }) + : '' + if (shortTitle && shortTitle !== title) { + expectedSlugs.push(slugger.slug(decode(shortTitle))) + } // If the directory name already matches the expected slug, bail out now - if (categoryDirName === expectedSlug) continue - - if (data.allowTitleToDifferFromFilename) { - continue - } + if (expectedSlugs.includes(categoryDirName)) continue // Figure out the new path for the category const categoryDirParentDir = path.dirname(categoryDirPath) - const newPath = path.join(categoryDirParentDir, expectedSlug) + const newPath = path.join(categoryDirParentDir, expectedSlugs.at(-1)) const oldRelativePath = path.relative(ROOT, categoryDirPath) const newRelativePath = path.relative(ROOT, newPath) diff --git a/src/content-linter/tests/category-pages.js b/src/content-linter/tests/category-pages.js index e15574c974..ebc7e23c1b 100644 --- a/src/content-linter/tests/category-pages.js +++ b/src/content-linter/tests/category-pages.js @@ -1,4 +1,3 @@ -import { fileURLToPath } from 'url' import path from 'path' import fs from 'fs' import walk from 'walk-sync' @@ -11,12 +10,11 @@ import { renderContent } from '#src/content-render/index.js' import getApplicableVersions from '#src/versions/lib/get-applicable-versions.js' import contextualize from '../../../middleware/context.js' import shortVersions from '#src/versions/middleware/short-versions.js' - -const __dirname = path.dirname(fileURLToPath(import.meta.url)) +import { ROOT } from '../../../lib/constants.js' const slugger = new GithubSlugger() -const contentDir = path.join(__dirname, '../../../content') +const contentDir = path.join(ROOT, 'content') describe('category pages', () => { const walkOptions = { @@ -69,8 +67,10 @@ describe('category pages', () => { let publishedArticlePaths, availableArticlePaths, indexTitle, + indexShortTitle, categoryVersions, - categoryChildTypes + categoryChildTypes, + allowTitleToDifferFromFilename const articleVersions = {} beforeAll(async () => { @@ -80,6 +80,7 @@ describe('category pages', () => { const indexContents = await fs.promises.readFile(indexAbsPath, 'utf8') const { data } = matter(indexContents) categoryVersions = getApplicableVersions(data.versions, indexAbsPath) + allowTitleToDifferFromFilename = data.allowTitleToDifferFromFilename categoryChildTypes = [] const articleLinks = data.children.filter((child) => { const mdPath = getPath(productDir, indexLink, child) @@ -114,6 +115,14 @@ describe('category pages', () => { ? await renderContent(data.title, req.context, { textOnly: true }) : data.title + if (data.shortTitle) { + indexShortTitle = data.shortTitle.includes('{') + ? await renderContent(data.shortTitle, req.context, { textOnly: true }) + : data.shortTitle + } else { + indexShortTitle = '' + } + publishedArticlePaths = ( await Promise.all( articleLinks.map(async (articleLink) => { @@ -195,20 +204,28 @@ describe('category pages', () => { ).toBe(true) }) - // TODO: Unskip this test once the related script has been executed - // Docs Engineering issue: 963 - test.skip('slugified title matches parent directory name', () => { + test('slugified title matches parent directory name', () => { + if (allowTitleToDifferFromFilename) return + // Get the parent directory name const categoryDirPath = path.dirname(indexAbsPath) const categoryDirName = path.basename(categoryDirPath) slugger.reset() - const expectedSlug = slugger.slug(decode(indexTitle)) + const expectedSlugs = [slugger.slug(decode(indexTitle))] + if (indexShortTitle && indexShortTitle !== indexTitle) { + expectedSlugs.push(slugger.slug(decode(indexShortTitle))) + } + let customMessage = `Directory name "${categoryDirName}" is not one of ${expectedSlugs + .map((x) => `"${x}"`) + .join(', ')} which comes from the title "${indexTitle}"${ + indexShortTitle ? ` or shortTitle "${indexShortTitle}"` : ' (no shortTitle)' + }` + const newCategoryDirPath = path.join(path.dirname(categoryDirPath), expectedSlugs.at(-1)) + customMessage += `\nTo resolve this consider running:\n ./script/move-content.js ${categoryDirPath} ${newCategoryDirPath}\n` // Check if the directory name matches the expected slug - expect(categoryDirName).toBe(expectedSlug) - - // If this fails, execute "script/reconcile-category-dirs-with-ids.js" + expect(expectedSlugs.includes(categoryDirName), customMessage).toBeTruthy() }) }, )