diff --git a/content/index.md b/content/index.md index 483ef9a75604..07ba324c2ae4 100644 --- a/content/index.md +++ b/content/index.md @@ -75,6 +75,10 @@ childGroups: octicon: ShieldLockIcon children: - code-security + - code-security/supply-chain-security + - code-security/dependabot + - code-security/code-scanning + - code-security/secret-scanning - name: Client apps octicon: DeviceMobileIcon children: diff --git a/lib/all-products.js b/lib/all-products.js index 7364b34cba25..0d941c3afa21 100644 --- a/lib/all-products.js +++ b/lib/all-products.js @@ -9,7 +9,6 @@ const homepage = path.posix.join(process.cwd(), 'content/index.md') const { data } = frontmatter(await fs.readFile(homepage, 'utf8')) export const productIds = data.children -export const productGroups = [] const externalProducts = data.externalProducts const internalProducts = {} @@ -45,17 +44,37 @@ for (const productId of productIds) { export const productMap = Object.assign({}, internalProducts, externalProducts) -for (const group of data.childGroups) { - productGroups.push({ - name: group.name, - icon: group.icon || null, - octicon: group.octicon || null, - children: group.children.map((id) => productMap[id]), +function getPage(id, lang, pageMap) { + const productId = id.split('/')[0] + const product = productMap[productId] + const href = removeFPTFromPath(path.posix.join('/', lang, product.versions[0], id)) + const page = pageMap[href] + if (!page) { + throw new Error(`Unable to find a page by the href '${href}'. Review your 'childGroups' frontmatter maybe.`) + } + // Return only the props needed for the ProductSelectionCard, since + // that's the only place this is ever used. + return { + id, + name: page.shortTitle || page.title, + href: href.replace(`/${lang}/`, '/'), + versions: page.applicableVersions, + } +} + +export function getProductGroups(pageMap, lang) { + return data.childGroups.map((group) => { + return { + name: group.name, + icon: group.icon || null, + octicon: group.octicon || null, + // Typically the children are product IDs, but we support deeper page paths too + children: group.children.map((id) => productMap[id] || getPage(id, lang, pageMap)), + } }) } export default { productIds, productMap, - productGroups, } diff --git a/middleware/context.js b/middleware/context.js index c935660f2b1f..6cc53abb734f 100644 --- a/middleware/context.js +++ b/middleware/context.js @@ -1,7 +1,7 @@ import languages from '../lib/languages.js' import enterpriseServerReleases from '../lib/enterprise-server-releases.js' import { allVersions } from '../lib/all-versions.js' -import { productMap, productGroups } from '../lib/all-products.js' +import { productMap, getProductGroups } from '../lib/all-products.js' import pathUtils from '../lib/path-utils.js' import productNames from '../lib/product-names.js' import warmServer from '../lib/warm-server.js' @@ -39,7 +39,7 @@ export default async function contextualize(req, res, next) { req.context.currentProduct = getProductStringFromPath(req.pagePath) req.context.currentCategory = getCategoryStringFromPath(req.pagePath) req.context.productMap = productMap - req.context.productGroups = productGroups + req.context.productGroups = getProductGroups(pageMap, req.language) req.context.activeProducts = activeProducts req.context.allVersions = allVersions req.context.currentPathWithoutLanguage = getPathWithoutLanguage(req.pagePath) diff --git a/tests/rendering/server.js b/tests/rendering/server.js index 66f71f47d5fb..0f09e5683996 100644 --- a/tests/rendering/server.js +++ b/tests/rendering/server.js @@ -6,6 +6,7 @@ import { loadPages } from '../../lib/page-data.js' import CspParse from 'csp-parse' import { productMap } from '../../lib/all-products.js' import { SURROGATE_ENUMS } from '../../middleware/set-fastly-surrogate-key.js' +import { getPathWithoutVersion } from '../../lib/path-utils.js' import { describe, jest } from '@jest/globals' const AZURE_STORAGE_URL = 'githubdocs.azureedge.net' @@ -79,21 +80,24 @@ describe('server', () => { test('renders the Enterprise homepages with links to expected products in both the sidebar and page body', async () => { const enterpriseProducts = [ - `/en/enterprise-server@${enterpriseServerReleases.latest}`, - '/en/enterprise-cloud@latest', + `enterprise-server@${enterpriseServerReleases.latest}`, + 'enterprise-cloud@latest', ] - enterpriseProducts.forEach(async (ep) => { - const $ = await getDOM(ep) + for (const ep of enterpriseProducts) { + const $ = await getDOM(`/en/${ep}`) const sidebarItems = $('[data-testid=sidebar] li a').get() const sidebarTitles = sidebarItems.map((el) => $(el).text().trim()) const sidebarHrefs = sidebarItems.map((el) => $(el).attr('href')) - const productItems = $('[data-testid=product] div a').get() - const productTitles = productItems.map((el) => $(el).text().trim()) - const productHrefs = productItems.map((el) => $(el).attr('href')) + const productItems = activeProducts.filter( + (prod) => prod.external || prod.versions.includes(ep) + ) + const productTitles = productItems.map((prod) => prod.name) + const productHrefs = productItems.map((prod) => + prod.external ? prod.href : `/en/${ep}${getPathWithoutVersion(prod.href)}` + ) const titlesInProductsButNotSidebar = lodash.difference(productTitles, sidebarTitles) - const hrefsInProductsButNotSidebar = lodash.difference(productHrefs, sidebarHrefs) expect( @@ -104,7 +108,7 @@ describe('server', () => { hrefsInProductsButNotSidebar.length, `Found hrefs missing from sidebar: ${hrefsInProductsButNotSidebar.join(', ')}` ).toBe(0) - }) + } }) test('sets Content Security Policy (CSP) headers', async () => {