diff --git a/includes/breadcrumbs.html b/includes/breadcrumbs.html index 8c22ab26ed..5de6581631 100644 --- a/includes/breadcrumbs.html +++ b/includes/breadcrumbs.html @@ -4,7 +4,7 @@ {%- if breadcrumb.href == '' -%} {{ breadcrumb.title }} {%- else -%} - {{ breadcrumb.title }} + {{ breadcrumb.title }} {%- endif -%} {%- endfor -%} diff --git a/includes/sidebar-product.html b/includes/sidebar-product.html index 5b5b6b78c1..7b3ab35366 100644 --- a/includes/sidebar-product.html +++ b/includes/sidebar-product.html @@ -13,7 +13,7 @@ {% if currentProductTree.renderedShortTitle %}{% assign productTitle = currentProductTree.renderedShortTitle %}{% else %}{% assign productTitle = currentProductTree.renderedFullTitle %}{% endif %}
  • diff --git a/middleware/contextualizers/breadcrumbs.js b/middleware/contextualizers/breadcrumbs.js index 2eccfd0e41..6013d7814c 100644 --- a/middleware/contextualizers/breadcrumbs.js +++ b/middleware/contextualizers/breadcrumbs.js @@ -22,7 +22,13 @@ module.exports = async function breadcrumbs (req, res, next) { async function createBreadcrumb (pageArray, context) { // Find each page in the siteTree's array of child pages that starts with the requested path. - const childPage = pageArray.find(page => context.currentPath.startsWith(page.href)) + let childPage = pageArray.find(page => context.currentPath.startsWith(page.href)) + + // Fall back to English if needed + if (!childPage) { + childPage = pageArray.find(page => context.currentPath.startsWith(page.href.replace(`/${context.currentLanguage}`, '/en'))) + if (!childPage) return + } context.breadcrumbs.push({ documentType: childPage.page.documentType, diff --git a/tests/content/category-pages.js b/tests/content/category-pages.js index 62ce22f675..088e30d1cf 100644 --- a/tests/content/category-pages.js +++ b/tests/content/category-pages.js @@ -1,3 +1,4 @@ +require('../../lib/feature-flags') const path = require('path') const fs = require('fs') const walk = require('walk-sync') @@ -16,6 +17,8 @@ const entities = new XmlEntities() const contentDir = path.join(__dirname, '../../content') const linkRegex = /{% (?:(?:topic_)?link_in_list|link_with_intro) ?\/(.*?) ?%}/g +const testOldSiteTree = process.env.FEATURE_NEW_SITETREE ? test.skip : test + describe('category pages', () => { let siteData @@ -45,14 +48,16 @@ describe('category pages', () => { // Each link corresponds to a product subdirectory (category). // Example: "getting-started-with-github" const contents = fs.readFileSync(productIndex, 'utf8') // TODO move to async - const { content } = matter(contents) + const { content, data } = matter(contents) const productDir = path.dirname(productIndex) - const categoryLinks = getLinks(content) + const categoryLinks = process.env.FEATURE_NEW_SITETREE + ? data.children + : getLinks(content) // Only include category directories, not standalone category files like content/actions/quickstart.md - .filter(link => fs.existsSync(getPath(productDir, link, 'index'))) - // TODO this should move to async, but you can't asynchronously define tests with Jest... + .filter(link => fs.existsSync(getPath(productDir, link, 'index'))) + // TODO this should move to async, but you can't asynchronously define tests with Jest... // Map those to the Markdown file paths that represent that category page index const categoryPaths = categoryLinks.map(link => getPath(productDir, link, 'index')) @@ -65,6 +70,7 @@ describe('category pages', () => { if (!categoryTuples.length) return + // TODO rework this for the new site tree structure describe.each(categoryTuples)( 'category index "%s"', (indexRelPath, indexAbsPath, indexLink) => { @@ -78,7 +84,9 @@ describe('category pages', () => { const indexContents = await readFileAsync(indexAbsPath, 'utf8') const { data, content } = matter(indexContents) categoryVersions = getApplicableVersions(data.versions, indexAbsPath) - const articleLinks = getLinks(content) + const articleLinks = process.env.FEATURE_NEW_SITETREE + ? data.children + : getLinks(content) // Save the index title for later testing indexTitle = await renderContent(data.title, { site: siteData }, { textOnly: true }) @@ -125,19 +133,19 @@ describe('category pages', () => { ) }) - test('contains all expected articles', () => { + testOldSiteTree('contains all expected articles', () => { const missingArticlePaths = difference(availableArticlePaths, publishedArticlePaths) const errorMessage = formatArticleError('Missing article links:', missingArticlePaths) expect(missingArticlePaths.length, errorMessage).toBe(0) }) - test('does not any unexpected articles', () => { + testOldSiteTree('does not any unexpected articles', () => { const unexpectedArticles = difference(publishedArticlePaths, availableArticlePaths) const errorMessage = formatArticleError('Unexpected article links:', unexpectedArticles) expect(unexpectedArticles.length, errorMessage).toBe(0) }) - test('contains only articles and map topics with versions that are also available in the parent category', () => { + testOldSiteTree('contains only articles and map topics with versions that are also available in the parent category', () => { Object.entries(articleVersions).forEach(([articleName, articleVersions]) => { const unexpectedVersions = difference(articleVersions, categoryVersions) const errorMessage = `${articleName} has versions that are not available in parent category` diff --git a/tests/content/featured-links.js b/tests/content/featured-links.js index 5911c86499..a6553c78f1 100644 --- a/tests/content/featured-links.js +++ b/tests/content/featured-links.js @@ -1,3 +1,4 @@ +require('../../lib/feature-flags') const { getDOM, getJSON } = require('../helpers/supertest') const enterpriseServerReleases = require('../../lib/enterprise-server-releases') const japaneseCharacters = require('japanese-characters') diff --git a/tests/content/site-tree.js b/tests/content/site-tree.js index b6a7e4916b..ab8e1b61f2 100644 --- a/tests/content/site-tree.js +++ b/tests/content/site-tree.js @@ -1,18 +1,31 @@ +require('../../lib/feature-flags') const revalidator = require('revalidator') const schema = require('../helpers/schemas/site-tree-schema') const latestEnterpriseRelease = require('../../lib/enterprise-server-releases').latest const { getJSON } = require('../helpers/supertest') +const { loadSiteTree } = require('../../lib/page-data') +const loadData = require('../../lib/site-data') const flat = require('flat') const japaneseCharacters = require('japanese-characters') const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version') +const renderTreeTitlesMiddleware = require('../../middleware/contextualizers/render-tree-titles') + +const testNewSiteTree = process.env.FEATURE_NEW_SITETREE ? test : test.skip +const testOldSiteTree = process.env.FEATURE_NEW_SITETREE ? test.skip : test describe('siteTree', () => { jest.setTimeout(3 * 60 * 1000) - let siteTree, flatTree + let siteTree, flatTree, siteData beforeAll(async (done) => { - siteTree = await getJSON('/en?json=siteTree') - flatTree = flat(siteTree) + if (process.env.FEATURE_NEW_SITETREE) { + siteTree = await loadSiteTree() + siteData = await loadData() + } else { + siteTree = await getJSON('/en?json=siteTree') + flatTree = flat(siteTree) + } + done() }) @@ -21,21 +34,28 @@ describe('siteTree', () => { expect('ja' in siteTree).toBe(true) }) - test('object order', () => { + testOldSiteTree('object order', () => { expect(Object.keys(siteTree)[0]).toBe('en') expect(Object.keys(siteTree.en)[0]).toBe(nonEnterpriseDefaultVersion) expect(Object.keys(siteTree.en[nonEnterpriseDefaultVersion].products.github.categories)[0]).toBe('/en/github/getting-started-with-github') }) - test('object structure', () => { + testOldSiteTree('object structure', () => { expect(nonEnterpriseDefaultVersion in siteTree.en).toBe(true) expect(`enterprise-server@${latestEnterpriseRelease}` in siteTree.en).toBe(true) expect(flatTree[`en.${nonEnterpriseDefaultVersion}.products.github.href`]).toBe('/en/github') expect(flatTree[`en.${nonEnterpriseDefaultVersion}.products.github.categories./en/github/getting-started-with-github.href`]).toBe('/en/github/getting-started-with-github') }) + testNewSiteTree('object order and structure', () => { + expect(Object.keys(siteTree)[0]).toBe('en') + expect(Object.keys(siteTree.en)[0]).toBe(nonEnterpriseDefaultVersion) + expect(siteTree.en[nonEnterpriseDefaultVersion].childPages[0].href).toBe('/en/github') + expect(siteTree.en[nonEnterpriseDefaultVersion].childPages[0].childPages[0].href).toBe('/en/github/getting-started-with-github') + }) + describe('localized titles', () => { - test('titles for categories', () => { + testOldSiteTree('titles for categories', () => { const japaneseTitle = flatTree[`ja.${nonEnterpriseDefaultVersion}.products.github.categories./ja/github/getting-started-with-github.title`] expect(typeof japaneseTitle).toBe('string') expect(japaneseCharacters.presentIn(japaneseTitle)).toBe(true) @@ -45,11 +65,17 @@ describe('siteTree', () => { expect(japaneseCharacters.presentIn(englishTitle)).toBe(false) }) - test.todo('titles for maptopics') + testNewSiteTree('titles for categories', () => { + const japaneseTitle = siteTree.ja[nonEnterpriseDefaultVersion].childPages[0].childPages[0].page.title + expect(typeof japaneseTitle).toBe('string') + expect(japaneseCharacters.presentIn(japaneseTitle)).toBe(true) - test.todo('titles for articles') + const englishTitle = siteTree.en[nonEnterpriseDefaultVersion].childPages[0].childPages[0].page.title + expect(typeof englishTitle).toBe('string') + expect(japaneseCharacters.presentIn(englishTitle)).toBe(false) + }) - test('articles that include site data in liquid templating', () => { + testOldSiteTree('articles that include site data in liquid templating', () => { const pageWithDynamicTitle = siteTree.en[`enterprise-server@${latestEnterpriseRelease}`] .products.admin .categories[`/en/enterprise-server@${latestEnterpriseRelease}/admin/enterprise-support`] @@ -57,9 +83,39 @@ describe('siteTree', () => { // title: 'Working with {{ site.data.variables.contact.github_support }}' expect(pageWithDynamicTitle.title).toEqual('Working with GitHub Support') }) + + testNewSiteTree('articles that include site data in liquid templating', async () => { + const ghesLatest = `enterprise-server@${latestEnterpriseRelease}` + const ghesSiteTree = siteTree.en[ghesLatest] + + // Find a page in the tree that we know contains Liquid + const pageWithDynamicTitle = ghesSiteTree + .childPages.find(child => child.href === `/en/${ghesLatest}/admin`) + .childPages.find(child => child.href === `/en/${ghesLatest}/admin/enterprise-support`) + + // Confirm the raw title contains Liquid + expect(pageWithDynamicTitle.page.title).toEqual('Working with {% data variables.contact.github_support %}') + + // Create a req so we can apply middleware + const req = { + context: { + page: ghesSiteTree.childPages[0].page, // this can be any non-homepage page + siteTree, + currentLanguage: 'en', + currentVersion: ghesLatest, + site: siteData.en.site + } + } + + // Pass the test req and an empty res and next + await renderTreeTitlesMiddleware(req, {}, () => {}) + + // Confirm a new property contains the rendered title + expect(pageWithDynamicTitle.renderedFullTitle).toEqual('Working with GitHub Support') + }) }) - test('object validation', () => { + testOldSiteTree('object validation', () => { const products = Object.values(siteTree.en[nonEnterpriseDefaultVersion].products) expect(products.length).toBeGreaterThan(0) @@ -81,4 +137,22 @@ describe('siteTree', () => { }) }) }) + + testNewSiteTree('object validation', () => { + const childPages = siteTree.en[nonEnterpriseDefaultVersion].childPages + expect(childPages.length).toBeGreaterThan(0) + + validate(siteTree.en[nonEnterpriseDefaultVersion]) + }) }) + +function validate (currentPage) { + (currentPage.childPages || []).forEach(childPage => { + const { valid, errors } = revalidator.validate(childPage, schema.childPage) + const expectation = JSON.stringify(errors, null, 2) + expect(valid, expectation).toBe(true) + + // Run recurisvely until we run out of child pages + validate(childPage) + }) +} diff --git a/tests/fixtures/developer-redirects.json b/tests/fixtures/developer-redirects.json index 21942051ec..45efcb332c 100644 --- a/tests/fixtures/developer-redirects.json +++ b/tests/fixtures/developer-redirects.json @@ -6,7 +6,7 @@ "/apps/managing-oauth-apps": "/en/developers/apps/managing-oauth-apps", "/apps/quickstart-guides": "/en/developers/apps/guides", "/marketplace": "/en/developers/github-marketplace", - "/marketplace/getting-started": "/en/developers/github-marketplace/about-github-marketplace", + "/marketplace/getting-started": "/en/developers/github-marketplace/github-marketplace-overview/about-github-marketplace", "/marketplace/integrating-with-the-github-marketplace-api": "/en/developers/github-marketplace/using-the-github-marketplace-api-in-your-app", "/marketplace/listing-on-github-marketplace": "/en/developers/github-marketplace/listing-an-app-on-github-marketplace", "/marketplace/selling-your-app": "/en/developers/github-marketplace/selling-your-app-on-github-marketplace", @@ -20,7 +20,7 @@ "/v3/misc": "/en/rest/reference/emojis", "/v4/public_schema": "/en/graphql/overview/public-schema", "/v4/object/repository": "/en/graphql/reference/objects#repository", - "/webhooks": "/en/developers/webhooks-and-events/about-webhooks", + "/webhooks": "/en/developers/webhooks-and-events/webhooks/about-webhooks", "/en/enterprise/2.18/apps": "/en/enterprise/2.18/user/developers/apps", "/en/enterprise/2.18/apps/building-oauth-apps/authorizing-oauth-apps": "/en/enterprise/2.18/user/developers/apps/authorizing-oauth-apps", "/en/enterprise/2.18/apps/building-oauth-apps/understanding-scopes-for-oauth-apps": "/en/enterprise/2.18/user/developers/apps/scopes-for-oauth-apps", @@ -55,14 +55,14 @@ "/en/enterprise/2.20/v4/guides/migrating-from-rest": "/en/enterprise-server@2.20/graphql/guides/migrating-from-rest-to-graphql", "/en/enterprise/2.20/webhooks": "/en/enterprise-server@2.20/developers/webhooks-and-events/about-webhooks", "/en/enterprise/2.21/apps": "/en/enterprise-server@2.21/developers/apps", - "/en/enterprise/2.21/apps/building-oauth-apps/authorizing-oauth-apps": "/en/enterprise-server@2.21/developers/apps/authorizing-oauth-apps", - "/en/enterprise/2.21/apps/building-oauth-apps/understanding-scopes-for-oauth-apps": "/en/enterprise-server@2.21/developers/apps/scopes-for-oauth-apps", - "/en/enterprise/2.21/apps/differences-between-apps": "/en/enterprise-server@2.21/developers/apps/differences-between-github-apps-and-oauth-apps", + "/en/enterprise/2.21/apps/building-oauth-apps/authorizing-oauth-apps": "/en/enterprise-server@2.21/developers/apps/building-oauth-apps/authorizing-oauth-apps", + "/en/enterprise/2.21/apps/building-oauth-apps/understanding-scopes-for-oauth-apps": "/en/enterprise-server@2.21/developers/apps/building-oauth-apps/scopes-for-oauth-apps", + "/en/enterprise/2.21/apps/differences-between-apps": "/en/enterprise-server@2.21/developers/apps/getting-started-with-apps/differences-between-github-apps-and-oauth-apps", "/en/enterprise/2.21/v3/auth": "/en/enterprise-server@2.21/rest/overview/other-authentication-methods", "/en/enterprise/2.21/v3/guides/managing-deploy-keys": "/en/enterprise-server@2.21/developers/overview/managing-deploy-keys", "/en/enterprise/2.21/v4/explorer": "/en/enterprise-server@2.21/graphql/overview/explorer", "/en/enterprise/2.21/v4/guides/forming-calls": "/en/enterprise-server@2.21/graphql/guides/forming-calls-with-graphql", "/en/enterprise/2.21/v4/guides/intro-to-graphql": "/en/enterprise-server@2.21/graphql/guides/introduction-to-graphql", "/en/enterprise/2.21/v4/guides/migrating-from-rest": "/en/enterprise-server@2.21/graphql/guides/migrating-from-rest-to-graphql", - "/en/enterprise/2.21/webhooks": "/en/enterprise-server@2.21/developers/webhooks-and-events/about-webhooks" + "/en/enterprise/2.21/webhooks": "/en/enterprise-server@2.21/developers/webhooks-and-events/webhooks/about-webhooks" } diff --git a/tests/fixtures/rest-redirects.json b/tests/fixtures/rest-redirects.json index 7452894547..3befed7b19 100644 --- a/tests/fixtures/rest-redirects.json +++ b/tests/fixtures/rest-redirects.json @@ -288,8 +288,8 @@ "/en/enterprise/2.20/v3/users/gpg_keys": "/en/enterprise-server@2.20/rest/reference/users#gpg-keys", "/en/enterprise/2.20/v3/users": "/en/enterprise-server@2.20/rest/reference/users", "/en/enterprise/2.20/v3/users/keys": "/en/enterprise-server@2.20/rest/reference/users#keys", - "/en/enterprise/2.21/v3/activity/event_types": "/en/enterprise-server@2.21/developers/webhooks-and-events/github-event-types", - "/en/enterprise/2.21/v3/activity/events/types": "/en/enterprise-server@2.21/developers/webhooks-and-events/webhook-events-and-payloads", + "/en/enterprise/2.21/v3/activity/event_types": "/en/enterprise-server@2.21/developers/webhooks-and-events/events/github-event-types", + "/en/enterprise/2.21/v3/activity/events/types": "/en/enterprise-server@2.21/developers/webhooks-and-events/webhooks/webhook-events-and-payloads", "/en/enterprise/2.21/v3/activity/events": "/en/enterprise-server@2.21/rest/reference/activity#events", "/en/enterprise/2.21/v3/activity/feeds": "/en/enterprise-server@2.21/rest/reference/activity#feeds", "/en/enterprise/2.21/v3/activity": "/en/enterprise-server@2.21/rest/reference/activity", @@ -332,7 +332,7 @@ "/en/enterprise/2.21/v3/issues/comments": "/en/enterprise-server@2.21/rest/reference/issues#comments", "/en/enterprise/2.21/v3/issues/events": "/en/enterprise-server@2.21/rest/reference/issues#events", "/en/enterprise/2.21/v3/issues": "/en/enterprise-server@2.21/rest/reference/issues", - "/en/enterprise/2.21/v3/issues/issue-event-types": "/en/enterprise-server@2.21/developers/webhooks-and-events/issue-event-types", + "/en/enterprise/2.21/v3/issues/issue-event-types": "/en/enterprise-server@2.21/developers/webhooks-and-events/events/issue-event-types", "/en/enterprise/2.21/v3/issues/labels": "/en/enterprise-server@2.21/rest/reference/issues#labels", "/en/enterprise/2.21/v3/issues/milestones": "/en/enterprise-server@2.21/rest/reference/issues#milestones", "/en/enterprise/2.21/v3/issues/timeline": "/en/enterprise-server@2.21/rest/reference/issues#timeline", @@ -393,8 +393,8 @@ "/v3/actions/workflow-jobs": "/en/rest/reference/actions#workflow-jobs", "/v3/actions/workflow-runs": "/en/rest/reference/actions#workflow-runs", "/v3/actions/workflows": "/en/rest/reference/actions#workflows", - "/v3/activity/event_types": "/en/developers/webhooks-and-events/github-event-types", - "/v3/activity/events/types": "/en/developers/webhooks-and-events/webhook-events-and-payloads", + "/v3/activity/event_types": "/en/developers/webhooks-and-events/events/github-event-types", + "/v3/activity/events/types": "/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads", "/v3/activity/events": "/en/rest/reference/activity#events", "/v3/activity/feeds": "/en/rest/reference/activity#feeds", "/v3/activity": "/en/rest/reference/activity", @@ -440,7 +440,7 @@ "/v3/issues/comments": "/en/rest/reference/issues#comments", "/v3/issues/events": "/en/rest/reference/issues#events", "/v3/issues": "/en/rest/reference/issues", - "/v3/issues/issue-event-types": "/en/developers/webhooks-and-events/issue-event-types", + "/v3/issues/issue-event-types": "/en/developers/webhooks-and-events/events/issue-event-types", "/v3/issues/labels": "/en/rest/reference/issues#labels", "/v3/issues/milestones": "/en/rest/reference/issues#milestones", "/v3/issues/timeline": "/en/rest/reference/issues#timeline", diff --git a/tests/helpers/schemas/site-tree-schema.js b/tests/helpers/schemas/site-tree-schema.js index 3a1365f048..1630a32c43 100644 --- a/tests/helpers/schemas/site-tree-schema.js +++ b/tests/helpers/schemas/site-tree-schema.js @@ -63,4 +63,34 @@ const article = { } } -module.exports = { product, category, maptopic, article } +const childPage = { + type: 'object', + properties: { + href: { + type: 'string', + required: true + }, + page: { + type: 'object', + required: true, + properties: { + title: { + type: 'string', + required: true + }, + relativePath: { + type: 'string', + required: true + }, + permalinks: { + type: 'array', + required: true, + minItems: 1 + } + } + } + } +} + +// TODO after enabling FEATURE_NEW_SITETREE, we can delete everything except childPage +module.exports = { product, category, maptopic, article, childPage } diff --git a/tests/linting/lint-files.js b/tests/linting/lint-files.js index 5949f2cce0..96abaeb74e 100644 --- a/tests/linting/lint-files.js +++ b/tests/linting/lint-files.js @@ -412,8 +412,8 @@ describe('lint markdown content', () => { // Filter out some very specific false positive matches const matches = initialMatches.filter(match => { if ( - markdownRelPath === 'content/admin/enterprise-management/migrating-from-github-enterprise-1110x-to-2123.md' || - markdownRelPath === 'content/admin/all-releases.md' + markdownRelPath.endsWith('migrating-from-github-enterprise-1110x-to-2123.md') || + markdownRelPath.endsWith('all-releases.md') ) { return false } diff --git a/tests/rendering/breadcrumbs.js b/tests/rendering/breadcrumbs.js index 4db6e7bfaa..d5d7dd1c68 100644 --- a/tests/rendering/breadcrumbs.js +++ b/tests/rendering/breadcrumbs.js @@ -1,7 +1,11 @@ +require('../../lib/feature-flags') const { getDOM, getJSON } = require('../helpers/supertest') const describeInternalOnly = process.env.GITHUB_REPOSITORY === 'github/docs-internal' ? describe : describe.skip +const testNewSiteTree = process.env.FEATURE_NEW_SITETREE ? test : test.skip +const testOldSiteTree = process.env.FEATURE_NEW_SITETREE ? test.skip : test + describe('breadcrumbs', () => { jest.setTimeout(300 * 1000) @@ -11,7 +15,7 @@ describe('breadcrumbs', () => { expect($('.breadcrumbs')).toHaveLength(1) }) - test('article pages have breadcrumbs with product, category, maptopic, and article', async () => { + testOldSiteTree('article pages have breadcrumbs with product, category, maptopic, and article', async () => { const $ = await getDOM('/github/getting-started-with-github/supported-browsers') const $breadcrumbs = $('.breadcrumbs a') @@ -22,7 +26,18 @@ describe('breadcrumbs', () => { expect($breadcrumbs.eq(3).attr('title')).toBe('article: Supported browsers') }) - test('maptopic pages include their own grayed-out breadcrumb', async () => { + testNewSiteTree('article pages have breadcrumbs with product, category, maptopic, and article', async () => { + const $ = await getDOM('/github/getting-started-with-github/supported-browsers') + const $breadcrumbs = $('.breadcrumbs a') + + expect($breadcrumbs).toHaveLength(4) + expect($breadcrumbs[0].attribs.title).toBe('product: GitHub.com') + expect($breadcrumbs[1].attribs.title).toBe('category: Getting started') + expect($breadcrumbs[2].attribs.title).toBe('mapTopic: Using GitHub') + expect($breadcrumbs[3].attribs.title).toBe('article: Supported browsers') + }) + + testOldSiteTree('maptopic pages include their own grayed-out breadcrumb', async () => { const $ = await getDOM('/github/getting-started-with-github/using-github') const $breadcrumbs = $('.breadcrumbs a') @@ -33,7 +48,18 @@ describe('breadcrumbs', () => { expect($breadcrumbs.eq(2).hasClass('color-text-tertiary')).toBe(true) }) - test('works for enterprise user pages', async () => { + testNewSiteTree('maptopic pages include their own grayed-out breadcrumb', async () => { + const $ = await getDOM('/github/getting-started-with-github/using-github') + const $breadcrumbs = $('.breadcrumbs a') + + expect($breadcrumbs).toHaveLength(3) + expect($breadcrumbs[0].attribs.title).toBe('product: GitHub.com') + expect($breadcrumbs[1].attribs.title).toBe('category: Getting started') + expect($breadcrumbs[2].attribs.title).toBe('mapTopic: Using GitHub') + expect($breadcrumbs[2].attribs.class.includes('color-text-tertiary')).toBe(true) + }) + + testOldSiteTree('works for enterprise user pages', async () => { const $ = await getDOM('/en/enterprise/user/github/getting-started-with-github/supported-browsers') const $breadcrumbs = $('.breadcrumbs a') expect($breadcrumbs).toHaveLength(4) @@ -41,24 +67,51 @@ describe('breadcrumbs', () => { expect($breadcrumbs.eq(0).attr('title')).toBe('product: GitHub.com') }) - test('parses Liquid variables inside titles', async () => { + testNewSiteTree('works for enterprise user pages', async () => { + const $ = await getDOM('/en/enterprise/user/github/getting-started-with-github/supported-browsers') + const $breadcrumbs = $('.breadcrumbs a') + expect($breadcrumbs).toHaveLength(4) + // The product is still GitHub.com on an Enterprise Server version + expect($breadcrumbs[0].attribs.title).toBe('product: GitHub.com') + }) + + testOldSiteTree('parses Liquid variables inside titles', async () => { const $ = await getDOM('/en/enterprise/admin/enterprise-support') const $breadcrumbs = $('.breadcrumbs a') expect($breadcrumbs).toHaveLength(2) expect($breadcrumbs.eq(1).attr('title')).toBe('category: Working with GitHub Support') }) - test('English breadcrumbs link to English pages', async () => { + testNewSiteTree('parses Liquid variables inside titles', async () => { + const $ = await getDOM('/en/enterprise/admin/enterprise-support') + const $breadcrumbs = $('.breadcrumbs a') + expect($breadcrumbs).toHaveLength(2) + expect($breadcrumbs[1].attribs.title).toBe('category: Working with GitHub Support') + }) + + testOldSiteTree('English breadcrumbs link to English pages', async () => { const $ = await getDOM('/en/github/setting-up-and-managing-your-github-user-account') const $breadcrumbs = $('.breadcrumbs a') expect($breadcrumbs.eq(0).attr('href')).toBe('/en/github') }) - test('localized breadcrumbs link to localize pages', async () => { + testNewSiteTree('English breadcrumbs link to English pages', async () => { + const $ = await getDOM('/en/github/setting-up-and-managing-your-github-user-account') + const $breadcrumbs = $('.breadcrumbs a') + expect($breadcrumbs[0].attribs.href).toBe('/en/github') + }) + + testOldSiteTree('localized breadcrumbs link to localize pages', async () => { const $ = await getDOM('/ja/github/setting-up-and-managing-your-github-user-account') const $breadcrumbs = $('.breadcrumbs a') expect($breadcrumbs.eq(0).attr('href')).toBe('/ja/github') }) + + testNewSiteTree('localized breadcrumbs link to localize pages', async () => { + const $ = await getDOM('/ja/github/setting-up-and-managing-your-github-user-account') + const $breadcrumbs = $('.breadcrumbs a') + expect($breadcrumbs[0].attribs.href).toBe('/ja/github') + }) }) describeInternalOnly('early access rendering', () => { @@ -83,7 +136,7 @@ describe('breadcrumbs', () => { }) describe('context.breadcrumbs object', () => { - test('works on product index pages', async () => { + testOldSiteTree('works on product index pages', async () => { const breadcrumbs = await getJSON('/en/github?json=breadcrumbs') const expected = { product: { @@ -94,7 +147,19 @@ describe('breadcrumbs', () => { expect(breadcrumbs).toEqual(expected) }) - test('works on category index pages', async () => { + testNewSiteTree('works on product index pages', async () => { + const breadcrumbs = await getJSON('/en/github?json=breadcrumbs') + const expected = [ + { + documentType: 'product', + href: '/en/github', + title: 'GitHub.com' + } + ] + expect(breadcrumbs).toEqual(expected) + }) + + testOldSiteTree('works on category index pages', async () => { const breadcrumbs = await getJSON('/en/github/authenticating-to-github?json=breadcrumbs') const expected = { product: { @@ -109,7 +174,24 @@ describe('breadcrumbs', () => { expect(breadcrumbs).toEqual(expected) }) - test('works on maptopic pages', async () => { + testNewSiteTree('works on category index pages', async () => { + const breadcrumbs = await getJSON('/en/github/authenticating-to-github?json=breadcrumbs') + const expected = [ + { + documentType: 'product', + href: '/en/github', + title: 'GitHub.com' + }, + { + documentType: 'category', + href: '/en/github/authenticating-to-github', + title: 'Authentication' + } + ] + expect(breadcrumbs).toEqual(expected) + }) + + testOldSiteTree('works on maptopic pages', async () => { const breadcrumbs = await getJSON('/en/github/authenticating-to-github/keeping-your-account-and-data-secure?json=breadcrumbs') const expected = { product: { @@ -128,7 +210,29 @@ describe('breadcrumbs', () => { expect(breadcrumbs).toEqual(expected) }) - test('works on articles that DO have maptopics ', async () => { + testNewSiteTree('works on maptopic pages', async () => { + const breadcrumbs = await getJSON('/en/github/authenticating-to-github/keeping-your-account-and-data-secure?json=breadcrumbs') + const expected = [ + { + documentType: 'product', + href: '/en/github', + title: 'GitHub.com' + }, + { + documentType: 'category', + href: '/en/github/authenticating-to-github', + title: 'Authentication' + }, + { + documentType: 'mapTopic', + href: '/en/github/authenticating-to-github/keeping-your-account-and-data-secure', + title: 'Keeping your account and data secure' + } + ] + expect(breadcrumbs).toEqual(expected) + }) + + testOldSiteTree('works on articles that DO have maptopics ', async () => { const breadcrumbs = await getJSON('/en/github/authenticating-to-github/creating-a-strong-password?json=breadcrumbs') const expected = { product: { @@ -151,7 +255,34 @@ describe('breadcrumbs', () => { expect(breadcrumbs).toEqual(expected) }) - test('works on articles that DO NOT have maptopics ', async () => { + testNewSiteTree('works on articles that DO have maptopics ', async () => { + const breadcrumbs = await getJSON('/en/github/authenticating-to-github/creating-a-strong-password?json=breadcrumbs') + const expected = [ + { + documentType: 'product', + href: '/en/github', + title: 'GitHub.com' + }, + { + documentType: 'category', + href: '/en/github/authenticating-to-github', + title: 'Authentication' + }, + { + documentType: 'mapTopic', + href: '/en/github/authenticating-to-github/keeping-your-account-and-data-secure', + title: 'Keeping your account and data secure' + }, + { + documentType: 'article', + href: '/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-strong-password', + title: 'Creating a strong password' + } + ] + expect(breadcrumbs).toEqual(expected) + }) + + testOldSiteTree('works on articles that DO NOT have maptopics ', async () => { const breadcrumbs = await getJSON('/github/site-policy/github-privacy-statement?json=breadcrumbs') const expected = { product: { @@ -170,9 +301,36 @@ describe('breadcrumbs', () => { expect(breadcrumbs).toEqual(expected) }) - test('returns an empty object on the landing page', async () => { + testNewSiteTree('works on articles that DO NOT have maptopics ', async () => { + const breadcrumbs = await getJSON('/github/site-policy/github-privacy-statement?json=breadcrumbs') + const expected = [ + { + documentType: 'product', + href: '/en/github', + title: 'GitHub.com' + }, + { + documentType: 'category', + href: '/en/github/site-policy', + title: 'Site policy' + }, + { + documentType: 'article', + href: '/en/github/site-policy/github-privacy-statement', + title: 'GitHub Privacy Statement' + } + ] + expect(breadcrumbs).toEqual(expected) + }) + + testOldSiteTree('returns an empty object on the landing page', async () => { const breadcrumbs = await getJSON('/en?json=breadcrumbs') expect(breadcrumbs).toEqual({}) }) + + testNewSiteTree('returns an empty array on the landing page', async () => { + const breadcrumbs = await getJSON('/en?json=breadcrumbs') + expect(breadcrumbs).toEqual([]) + }) }) }) diff --git a/tests/rendering/header.js b/tests/rendering/header.js index c381ccf311..45e848333d 100644 --- a/tests/rendering/header.js +++ b/tests/rendering/header.js @@ -1,6 +1,10 @@ +require('../../lib/feature-flags') const { getDOM } = require('../helpers/supertest') const { oldestSupported, latest } = require('../../lib/enterprise-server-releases') +const testNewSiteTree = process.env.FEATURE_NEW_SITETREE ? test : test.skip +const testOldSiteTree = process.env.FEATURE_NEW_SITETREE ? test.skip : test + describe('header', () => { jest.setTimeout(5 * 60 * 1000) @@ -19,11 +23,16 @@ describe('header', () => { }) describe('language links', () => { - test('lead to the same page in a different language', async () => { + testOldSiteTree('lead to the same page in a different language', async () => { const $ = await getDOM('/github/administering-a-repository/managing-a-branch-protection-rule') expect($('#languages-selector a[href="/ja/github/administering-a-repository/managing-a-branch-protection-rule"]').length).toBe(1) }) + testNewSiteTree('lead to the same page in a different language', async () => { + const $ = await getDOM('/github/administering-a-repository/managing-a-branch-protection-rule') + expect($('#languages-selector a[href="/ja/github/administering-a-repository/defining-the-mergeability-of-pull-requests/managing-a-branch-protection-rule"]').length).toBe(1) + }) + test('display the native name and the English name for each translated language', async () => { const $ = await getDOM('/en') expect($('#languages-selector a[href="/en"]').text().trim()).toBe('English') diff --git a/tests/rendering/server.js b/tests/rendering/server.js index d03ac9c728..615203973b 100644 --- a/tests/rendering/server.js +++ b/tests/rendering/server.js @@ -1,3 +1,4 @@ +require('../../lib/feature-flags') const lodash = require('lodash') const enterpriseServerReleases = require('../../lib/enterprise-server-releases') const { get, getDOM, head, post } = require('../helpers/supertest') @@ -7,6 +8,9 @@ const { loadPages } = require('../../lib/pages') const builtAssets = require('../../lib/built-asset-urls') const AZURE_STORAGE_URL = 'githubdocs.azureedge.net' +const testNewSiteTree = process.env.FEATURE_NEW_SITETREE ? test : test.skip +const testOldSiteTree = process.env.FEATURE_NEW_SITETREE ? test.skip : test + describe('server', () => { jest.setTimeout(60 * 1000) @@ -319,11 +323,16 @@ describe('server', () => { expect($(`a[href="${latestEnterprisePath}/articles/managing-files-on-github"]`).length).toBe(1) }) - test('dotcom categories on GHE have Enterprise user links', async () => { + testOldSiteTree('dotcom categories on GHE have Enterprise user links', async () => { const $ = await getDOM(`${latestEnterprisePath}/github/managing-large-files`) expect($(`article a[href="${latestEnterprisePath}/github/managing-large-files/conditions-for-large-files"]`).length).toBe(1) }) + testNewSiteTree('dotcom categories on GHE have Enterprise user links', async () => { + const $ = await getDOM(`${latestEnterprisePath}/github/managing-large-files`) + expect($(`article a[href="${latestEnterprisePath}/github/managing-large-files/working-with-large-files/conditions-for-large-files"]`).length).toBe(1) + }) + test('dotcom-only links on GHE are dotcom-only', async () => { const $ = await getDOM(`${latestEnterprisePath}/github/setting-up-and-managing-your-github-profile/sending-your-github-enterprise-server-contributions-to-your-githubcom-profile`) expect($('article a[href="/en/articles/github-privacy-statement"]').length).toBe(1) @@ -351,7 +360,7 @@ describe('server', () => { }) describe('article versions', () => { - test('includes links to all versions of each article', async () => { + testOldSiteTree('includes links to all versions of each article', async () => { const articlePath = 'github/setting-up-and-managing-your-github-user-account/about-your-personal-dashboard' const $ = await getDOM(`/en/enterprise-server@${enterpriseServerReleases.latest}/${articlePath}`) expect($(`.article-versions a.active[href="/en/enterprise-server@${enterpriseServerReleases.latest}/${articlePath}"]`).length).toBe(2) @@ -361,6 +370,16 @@ describe('server', () => { expect($(`.article-versions a[href="/en/enterprise/2.13/user/${articlePath}"]`).length).toBe(0) }) + testNewSiteTree('includes links to all versions of each article', async () => { + const articlePath = 'github/setting-up-and-managing-your-github-user-account/managing-user-account-settings/about-your-personal-dashboard' + const $ = await getDOM(`/en/enterprise-server@${enterpriseServerReleases.latest}/${articlePath}`) + expect($(`.article-versions a.active[href="/en/enterprise-server@${enterpriseServerReleases.latest}/${articlePath}"]`).length).toBe(2) + expect($(`.article-versions a.active[href="/en/${articlePath}"]`).length).toBe(0) + + // 2.13 predates this feature, so it should be excluded: + expect($(`.article-versions a[href="/en/enterprise/2.13/user/${articlePath}"]`).length).toBe(0) + }) + test('is not displayed if article has only one version', async () => { const $ = await getDOM('/en/articles/signing-up-for-a-new-github-account') expect($('.article-versions').length).toBe(0) @@ -413,11 +432,16 @@ describe('server', () => { expect(res.statusCode).toBe(301) }) - test('redirects old articles to their slugified URL', async () => { + testOldSiteTree('redirects old articles to their slugified URL', async () => { const res = await get('/articles/about-github-s-ip-addresses') expect(res.text).toBe('Moved Permanently. Redirecting to /en/github/authenticating-to-github/about-githubs-ip-addresses') }) + testNewSiteTree('redirects old articles to their slugified URL', async () => { + const res = await get('/articles/about-github-s-ip-addresses') + expect(res.text).toBe('Moved Permanently. Redirecting to /en/github/authenticating-to-github/keeping-your-account-and-data-secure/about-githubs-ip-addresses') + }) + test('redirects / to /en', async () => { const res = await get('/') expect(res.statusCode).toBe(301) @@ -436,12 +460,18 @@ describe('server', () => { expect(res.headers.location).toBe('https://desktop.github.com') }) - test('redirects /insights/foo paths to /enterprise/user/insights/foo', async () => { + testOldSiteTree('redirects /insights/foo paths to /enterprise/user/insights/foo', async () => { const res = await get('/en/insights/installing-and-configuring-github-insights/about-github-insights') expect(res.statusCode).toBe(301) expect(res.headers.location).toBe(`/en/enterprise-server@${enterpriseServerReleases.latest}/insights/installing-and-configuring-github-insights/about-github-insights`) }) + testNewSiteTree('redirects /insights/foo paths to /enterprise/user/insights/foo', async () => { + const res = await get('/en/insights/installing-and-configuring-github-insights/about-github-insights') + expect(res.statusCode).toBe(301) + expect(res.headers.location).toBe(`/en/enterprise-server@${enterpriseServerReleases.latest}/insights/installing-and-configuring-github-insights/installing-and-updating-github-insights/about-github-insights`) + }) + // this oneoff redirect is temporarily disabled because it introduces too much complexity // we can reenable it down the road if needed test.skip('redirects versioned category page', async () => { @@ -469,11 +499,16 @@ describe('server', () => { expect($('.markdown-body ul li a').length).toBeGreaterThan(5) }) - test('map topic renders with h2 links to articles', async () => { + testOldSiteTree('map topic renders with h2 links to articles', async () => { const $ = await getDOM('/en/github/setting-up-and-managing-your-github-user-account/managing-user-account-settings') expect($('a[href="/en/github/setting-up-and-managing-your-github-user-account/changing-your-github-username"] h2').length).toBe(1) }) + testNewSiteTree('map topic renders with h2 links to articles', async () => { + const $ = await getDOM('/en/github/setting-up-and-managing-your-github-user-account/managing-user-account-settings') + expect($('a[href="/en/github/setting-up-and-managing-your-github-user-account/managing-user-account-settings/changing-your-github-username"] h2').length).toBe(1) + }) + test('map topic renders with one intro for every h2', async () => { const $ = await getDOM('/en/github/setting-up-and-managing-your-github-user-account/managing-user-account-settings') const $h2s = $('article a.link-with-intro') @@ -596,11 +631,16 @@ describe('GitHub Desktop URLs', () => { expect($('article a[href^="/en/desktop/installing-and-configuring-github-desktop/"]').length).toBeGreaterThan(1) }) - test('renders a Desktop article within a map topic', async () => { + testOldSiteTree('renders a Desktop article within a map topic', async () => { const res = await get('/en/desktop/installing-and-configuring-github-desktop/installing-github-desktop') expect(res.statusCode).toBe(200) }) + testNewSiteTree('renders a Desktop article within a map topic', async () => { + const res = await get('/en/desktop/installing-and-configuring-github-desktop/installing-and-authenticating-to-github-desktop/installing-github-desktop') + expect(res.statusCode).toBe(200) + }) + test('renders the Desktop homepage in Japanese', async () => { const res = await get('/ja/desktop') expect(res.statusCode).toBe(200) diff --git a/tests/rendering/sidebar.js b/tests/rendering/sidebar.js index 2e66798a1a..3602d9dfe1 100644 --- a/tests/rendering/sidebar.js +++ b/tests/rendering/sidebar.js @@ -1,3 +1,4 @@ +require('../../lib/feature-flags') const { getDOM } = require('../helpers/supertest') describe('sidebar', () => { diff --git a/tests/routing/developer-site-redirects.js b/tests/routing/developer-site-redirects.js index 0318c8c96e..08526a13ce 100644 --- a/tests/routing/developer-site-redirects.js +++ b/tests/routing/developer-site-redirects.js @@ -1,3 +1,4 @@ +require('../../lib/feature-flags') const path = require('path') const { eachOfLimit } = require('async') const enterpriseServerReleases = require('../../lib/enterprise-server-releases') @@ -8,6 +9,25 @@ const developerRedirectFixtures = require('../fixtures/developer-redirects') const MAX_CONCURRENT_REQUESTS = 50 +// TODO we can remove these after the new site tree is in production +const oldSiteTreeDeveloperRedirectOverrides = { + '/marketplace/getting-started': '/en/developers/github-marketplace/about-github-marketplace', + '/webhooks': '/en/developers/webhooks-and-events/about-webhooks', + '/en/enterprise/2.21/apps/building-oauth-apps/authorizing-oauth-apps': '/en/enterprise-server@2.21/developers/apps/authorizing-oauth-apps', + '/en/enterprise/2.21/apps/building-oauth-apps/understanding-scopes-for-oauth-apps': '/en/enterprise-server@2.21/developers/apps/scopes-for-oauth-apps', + '/en/enterprise/2.21/apps/differences-between-apps': '/en/enterprise-server@2.21/developers/apps/differences-between-github-apps-and-oauth-apps', + '/en/enterprise/2.21/webhooks': '/en/enterprise-server@2.21/developers/webhooks-and-events/about-webhooks' +} + +const oldSiteTreeRestRedirectOverrides = { + '/en/enterprise/2.21/v3/activity/event_types': '/en/enterprise-server@2.21/developers/webhooks-and-events/github-event-types', + '/en/enterprise/2.21/v3/activity/events/types': '/en/enterprise-server@2.21/developers/webhooks-and-events/webhook-events-and-payloads', + '/en/enterprise/2.21/v3/issues/issue-event-types': '/en/enterprise-server@2.21/developers/webhooks-and-events/issue-event-types', + '/v3/activity/event_types': '/en/developers/webhooks-and-events/github-event-types', + '/v3/activity/events/types': '/en/developers/webhooks-and-events/webhook-events-and-payloads', + '/v3/issues/issue-event-types': '/en/developers/webhooks-and-events/issue-event-types' +} + describe('developer redirects', () => { jest.setTimeout(60 * 1000) @@ -102,6 +122,12 @@ describe('developer redirects', () => { developerRedirectFixtures, MAX_CONCURRENT_REQUESTS, async (newPath, oldPath) => { + if (!process.env.FEATURE_NEW_SITETREE) { + if (oldSiteTreeDeveloperRedirectOverrides[oldPath]) { + newPath = oldSiteTreeDeveloperRedirectOverrides[oldPath] + } + } + const res = await get(oldPath) expect(res.statusCode, `${oldPath} did not redirect to ${newPath}`).toBe(301) expect(res.headers.location).toBe(newPath) @@ -115,6 +141,12 @@ describe('developer redirects', () => { restRedirectFixtures, MAX_CONCURRENT_REQUESTS, async (newPath, oldPath) => { + if (!process.env.FEATURE_NEW_SITETREE) { + if (oldSiteTreeRestRedirectOverrides[oldPath]) { + newPath = oldSiteTreeRestRedirectOverrides[oldPath] + } + } + // REST and GraphQL developer Enterprise paths with a version are only supported up to 2.21. // We make an exception to always redirect versionless paths to the latest version. newPath = newPath.replace('/enterprise-server/', `/enterprise-server@${enterpriseServerReleases.latest}/`) diff --git a/tests/routing/redirects.js b/tests/routing/redirects.js index 8064dfe669..ba8ec69bfa 100644 --- a/tests/routing/redirects.js +++ b/tests/routing/redirects.js @@ -1,3 +1,4 @@ +require('../../lib/feature-flags') const path = require('path') const { isPlainObject } = require('lodash') const supertest = require('supertest') @@ -8,6 +9,9 @@ const Page = require('../../lib/page') const { get } = require('../helpers/supertest') const versionSatisfiesRange = require('../../lib/version-satisfies-range') +const testNewSiteTree = process.env.FEATURE_NEW_SITETREE ? test : test.skip +const testOldSiteTree = process.env.FEATURE_NEW_SITETREE ? test.skip : test + describe('redirects', () => { jest.setTimeout(5 * 60 * 1000) @@ -18,7 +22,7 @@ describe('redirects', () => { done() }) - test('page.redirects is an array', async () => { + testOldSiteTree('page.redirects is an array', async () => { const page = await Page.init({ relativePath: 'github/collaborating-with-issues-and-pull-requests/about-branches.md', basePath: path.join(__dirname, '../../content'), @@ -28,6 +32,16 @@ describe('redirects', () => { expect(isPlainObject(page.redirects)).toBe(true) }) + testNewSiteTree('page.redirects is an array', async () => { + const page = await Page.init({ + relativePath: 'github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches.md', + basePath: path.join(__dirname, '../../content'), + languageCode: 'en' + }) + page.buildRedirects() + expect(isPlainObject(page.redirects)).toBe(true) + }) + test('dotcom homepage page.redirects', async () => { const page = await Page.init({ relativePath: 'github/index.md', @@ -77,12 +91,18 @@ describe('redirects', () => { expect(res.headers.location).toBe(expected) }) - test('do not work on other paths that include "search"', async () => { + testOldSiteTree('do not work on other paths that include "search"', async () => { const reqPath = `/en/enterprise-server@${enterpriseServerReleases.latest}/admin/configuration/enabling-unified-search-between-github-enterprise-server-and-githubcom` const res = await get(reqPath) expect(res.statusCode).toBe(200) }) + testNewSiteTree('do not work on other paths that include "search"', async () => { + const reqPath = `/en/enterprise-server@${enterpriseServerReleases.latest}/admin/configuration/managing-connections-between-github-enterprise-server-and-github-enterprise-cloud/enabling-unified-search-between-github-enterprise-server-and-githubcom` + const res = await get(reqPath) + expect(res.statusCode).toBe(200) + }) + test('work on deprecated versions', async () => { const res = await get('/enterprise/2.12/admin/search?utf8=%E2%9C%93&q=pulls') expect(res.statusCode).toBe(200) @@ -124,12 +144,19 @@ describe('redirects', () => { }) describe('localized redirects', () => { - test('redirect_from for renamed pages', async () => { + testOldSiteTree('redirect_from for renamed pages', async () => { const { res } = await get('/ja/desktop/contributing-to-projects/changing-a-remote-s-url-from-github-desktop') expect(res.statusCode).toBe(301) const expected = '/ja/desktop/contributing-and-collaborating-using-github-desktop/changing-a-remotes-url-from-github-desktop' expect(res.headers.location).toBe(expected) }) + + testNewSiteTree('redirect_from for renamed pages', async () => { + const { res } = await get('/ja/desktop/contributing-to-projects/changing-a-remote-s-url-from-github-desktop') + expect(res.statusCode).toBe(301) + const expected = '/ja/desktop/contributing-and-collaborating-using-github-desktop/working-with-your-remote-repository-on-github-or-github-enterprise/changing-a-remotes-url-from-github-desktop' + expect(res.headers.location).toBe(expected) + }) }) describe('enterprise home page', () => { @@ -243,7 +270,7 @@ describe('redirects', () => { expect(res.headers.location).toBe(expected) }) - test('admin/guides still redirects to admin in deep links on >=2.21', async () => { + testOldSiteTree('admin/guides still redirects to admin in deep links on >=2.21', async () => { const res = await get(`/en/enterprise-server@${firstRestoredAdminGuides}/admin/guides/installation/upgrading-github-enterprise`) expect(res.statusCode).toBe(301) const redirectRes = await get(res.headers.location) @@ -252,6 +279,15 @@ describe('redirects', () => { expect(res.headers.location).toBe(expected) }) + testNewSiteTree('admin/guides still redirects to admin in deep links on >=2.21', async () => { + const res = await get(`/en/enterprise-server@${firstRestoredAdminGuides}/admin/guides/installation/upgrading-github-enterprise`) + expect(res.statusCode).toBe(301) + const redirectRes = await get(res.headers.location) + expect(redirectRes.statusCode).toBe(200) + const expected = `/en/enterprise-server@${firstRestoredAdminGuides}/admin/enterprise-management/updating-the-virtual-machine-and-physical-resources/upgrading-github-enterprise-server` + expect(res.headers.location).toBe(expected) + }) + test('no version redirects to latest version (japanese)', async () => { const res = await get('/ja/enterprise/admin') expect(res.statusCode).toBe(301) @@ -300,7 +336,10 @@ describe('redirects', () => { }) describe('enterprise user article', () => { - const userArticle = `/en/enterprise-server@${enterpriseServerReleases.latest}/github/getting-started-with-github/set-up-git` + const userArticle = process.env.FEATURE_NEW_SITETREE + ? `/en/enterprise-server@${enterpriseServerReleases.latest}/github/getting-started-with-github/quickstart/set-up-git` + : `/en/enterprise-server@${enterpriseServerReleases.latest}/github/getting-started-with-github/set-up-git` + const japaneseUserArticle = userArticle.replace('/en/', '/ja/') test('no product redirects to GitHub.com product on the latest version', async () => { @@ -309,6 +348,12 @@ describe('redirects', () => { expect(res.headers.location).toBe(userArticle) }) + test('no product redirects to GitHub.com product on the latest version', async () => { + const res = await get(`/en/enterprise/${enterpriseServerReleases.latest}/user/articles/set-up-git`) + expect(res.statusCode).toBe(301) + expect(res.headers.location).toBe(userArticle) + }) + // 2.16 was the first version where we moved /articles/foo to /github//foo test('no product does not redirect to GitHub.com product in <=2.15', async () => { const res = await get('/en/enterprise/2.15/user/articles/set-up-git') @@ -335,7 +380,9 @@ describe('redirects', () => { }) describe('enterprise user article with frontmatter redirect', () => { - const userArticle = `/en/enterprise-server@${enterpriseServerReleases.latest}/github/getting-started-with-github/access-permissions-on-github` + const userArticle = process.env.FEATURE_NEW_SITETREE + ? `/en/enterprise-server@${enterpriseServerReleases.latest}/github/getting-started-with-github/learning-about-github/access-permissions-on-github` + : `/en/enterprise-server@${enterpriseServerReleases.latest}/github/getting-started-with-github/access-permissions-on-github` const redirectFromPath = '/articles/what-are-the-different-access-permissions' const japaneseUserArticle = userArticle.replace('/en/', '/ja/') @@ -365,7 +412,9 @@ describe('redirects', () => { }) describe('desktop guide', () => { - const desktopGuide = '/en/desktop/contributing-and-collaborating-using-github-desktop/creating-an-issue-or-pull-request' + const desktopGuide = process.env.FEATURE_NEW_SITETREE + ? '/en/desktop/contributing-and-collaborating-using-github-desktop/working-with-your-remote-repository-on-github-or-github-enterprise/creating-an-issue-or-pull-request' + : '/en/desktop/contributing-and-collaborating-using-github-desktop/creating-an-issue-or-pull-request' const japaneseDesktopGuides = desktopGuide.replace('/en/', '/ja/') test('no language code redirects to english', async () => { diff --git a/tests/unit/liquid-helpers.js b/tests/unit/liquid-helpers.js index 8ca42fbb8c..7f9442a5e2 100644 --- a/tests/unit/liquid-helpers.js +++ b/tests/unit/liquid-helpers.js @@ -1,8 +1,11 @@ +require('../../lib/feature-flags') const { liquid } = require('../../lib/render-content') const { loadPageMap } = require('../../lib/pages') const entities = new (require('html-entities').XmlEntities)() const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version') +const testOldSiteTree = process.env.FEATURE_NEW_SITETREE ? test.skip : test + describe('liquid helper tags', () => { jest.setTimeout(60 * 1000) @@ -76,9 +79,11 @@ describe('liquid helper tags', () => { expect(output).toBe(expected) }) - test('homepage_link_with_intro tag', async () => { + testOldSiteTree('homepage_link_with_intro tag', async () => { const template = '{% homepage_link_with_intro /github/writing-on-github/basic-writing-and-formatting-syntax %}' - const page = pageMap['/en/github/writing-on-github/basic-writing-and-formatting-syntax'] + const page = process.env.FEATURE_NEW_SITETREE + ? pageMap['/en/github/writing-on-github/basic-writing-and-formatting-syntax'] + : pageMap['/en/github/writing-on-github/basic-writing-and-formatting-syntax'] const expected = ` diff --git a/tests/unit/page.js b/tests/unit/page.js index 7503dc44de..244f9908d8 100644 --- a/tests/unit/page.js +++ b/tests/unit/page.js @@ -1,3 +1,4 @@ +require('../../lib/feature-flags') const path = require('path') const cheerio = require('cheerio') const Page = require('../../lib/page') @@ -15,7 +16,9 @@ jest.mock('../../lib/get-link-data') const nonEnterpriseDefaultPlan = nonEnterpriseDefaultVersion.split('@')[0] const opts = { - relativePath: 'github/collaborating-with-issues-and-pull-requests/about-branches.md', + relativePath: process.env.FEATURE_NEW_SITETREE + ? 'github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches.md' + : 'github/collaborating-with-issues-and-pull-requests/about-branches.md', basePath: path.join(__dirname, '../../content'), languageCode: 'en' } @@ -23,7 +26,7 @@ const opts = { describe('Page class', () => { test('preserves file path info', async () => { const page = await Page.init(opts) - expect(page.relativePath).toBe('github/collaborating-with-issues-and-pull-requests/about-branches.md') + expect(page.relativePath).toBe(opts.relativePath) expect(page.fullPath.includes(page.relativePath)).toBe(true) }) @@ -90,7 +93,9 @@ describe('Page class', () => { const context = { page: { version: `enterprise-server@${enterpriseServerReleases.latest}` }, currentVersion: `enterprise-server@${enterpriseServerReleases.latest}`, - currentPath: '/en/github/collaborating-with-issues-and-pull-requests/about-branches', + currentPath: process.env.FEATURE_NEW_SITETREE + ? '/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches' + : '/en/github/collaborating-with-issues-and-pull-requests/about-branches', currentLanguage: 'en' } const rendered = await page.render(context) @@ -114,7 +119,9 @@ describe('Page class', () => { const context = { page: { version: nonEnterpriseDefaultVersion }, currentVersion: nonEnterpriseDefaultVersion, - currentPath: '/en/github/collaborating-with-issues-and-pull-requests/about-branches', + currentPath: process.env.FEATURE_NEW_SITETREE + ? '/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches' + : '/en/github/collaborating-with-issues-and-pull-requests/about-branches', currentLanguage: 'en' } await page.render(context) @@ -125,7 +132,9 @@ describe('Page class', () => { test('does not rewrite links that include deprecated enterprise release numbers', async () => { const page = await Page.init({ - relativePath: 'admin/enterprise-management/migrating-from-github-enterprise-1110x-to-2123.md', + relativePath: process.env.FEATURE_NEW_SITETREE + ? 'admin/enterprise-management/updating-the-virtual-machine-and-physical-resources/migrating-from-github-enterprise-1110x-to-2123.md' + : 'admin/enterprise-management/migrating-from-github-enterprise-1110x-to-2123.md', basePath: path.join(__dirname, '../../content'), languageCode: 'en' }) @@ -148,7 +157,9 @@ describe('Page class', () => { const context = { page: { version: nonEnterpriseDefaultVersion }, currentVersion: nonEnterpriseDefaultVersion, - currentPath: `/en/${nonEnterpriseDefaultVersion}/github/collaborating-with-issues-and-pull-requests/about-branches`, + currentPath: process.env.FEATURE_NEW_SITETREE + ? `/en/${nonEnterpriseDefaultVersion}/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches` + : `/en/${nonEnterpriseDefaultVersion}/github/collaborating-with-issues-and-pull-requests/about-branches`, currentLanguage: 'en' } const rendered = await page.render(context) @@ -267,8 +278,11 @@ describe('Page class', () => { test('sets versioned values', async () => { const page = await Page.init(opts) - expect(page.permalinks.find(permalink => permalink.pageVersion === nonEnterpriseDefaultVersion).href).toBe('/en/github/collaborating-with-issues-and-pull-requests/about-branches') - expect(page.permalinks.find(permalink => permalink.pageVersion === `enterprise-server@${enterpriseServerReleases.oldestSupported}`).href).toBe(`/en/enterprise-server@${enterpriseServerReleases.oldestSupported}/github/collaborating-with-issues-and-pull-requests/about-branches`) + const expectedPath = process.env.FEATURE_NEW_SITETREE + ? 'github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches' + : 'github/collaborating-with-issues-and-pull-requests/about-branches' + expect(page.permalinks.find(permalink => permalink.pageVersion === nonEnterpriseDefaultVersion).href).toBe(`/en/${expectedPath}`) + expect(page.permalinks.find(permalink => permalink.pageVersion === `enterprise-server@${enterpriseServerReleases.oldestSupported}`).href).toBe(`/en/enterprise-server@${enterpriseServerReleases.oldestSupported}/${expectedPath}`) }) test('homepage permalinks', async () => { @@ -283,11 +297,16 @@ describe('Page class', () => { test('permalinks for dotcom-only pages', async () => { const page = await Page.init({ - relativePath: 'github/getting-started-with-github/signing-up-for-a-new-github-account.md', + relativePath: process.env.FEATURE_NEW_SITETREE + ? 'github/getting-started-with-github/signing-up-for-github/signing-up-for-a-new-github-account.md' + : 'github/getting-started-with-github/signing-up-for-a-new-github-account.md', basePath: path.join(__dirname, '../../content'), languageCode: 'en' }) - expect(page.permalinks.find(permalink => permalink.pageVersion === nonEnterpriseDefaultVersion).href).toBe('/en/github/getting-started-with-github/signing-up-for-a-new-github-account') + const expectedPath = process.env.FEATURE_NEW_SITETREE + ? '/en/github/getting-started-with-github/signing-up-for-github/signing-up-for-a-new-github-account' + : '/en/github/getting-started-with-github/signing-up-for-a-new-github-account' + expect(page.permalinks.find(permalink => permalink.pageVersion === nonEnterpriseDefaultVersion).href).toBe(expectedPath) expect(page.permalinks.length).toBe(1) }) @@ -315,11 +334,16 @@ describe('Page class', () => { test('permalinks for non-GitHub.com products with Enterprise versions', async () => { const page = await Page.init({ - relativePath: '/insights/installing-and-configuring-github-insights/about-github-insights.md', + relativePath: process.env.FEATURE_NEW_SITETREE + ? '/insights/installing-and-configuring-github-insights/installing-and-updating-github-insights/about-github-insights.md' + : '/insights/installing-and-configuring-github-insights/about-github-insights.md', basePath: path.join(__dirname, '../../content'), languageCode: 'en' }) - expect(page.permalinks.find(permalink => permalink.pageVersion === `enterprise-server@${enterpriseServerReleases.latest}`).href).toBe(`/en/enterprise-server@${enterpriseServerReleases.latest}/insights/installing-and-configuring-github-insights/about-github-insights`) + const expectedPath = process.env.FEATURE_NEW_SITETREE + ? `/en/enterprise-server@${enterpriseServerReleases.latest}/insights/installing-and-configuring-github-insights/installing-and-updating-github-insights/about-github-insights` + : `/en/enterprise-server@${enterpriseServerReleases.latest}/insights/installing-and-configuring-github-insights/about-github-insights` + expect(page.permalinks.find(permalink => permalink.pageVersion === `enterprise-server@${enterpriseServerReleases.latest}`).href).toBe(expectedPath) const pageVersions = page.permalinks.map(permalink => permalink.pageVersion) expect(pageVersions.length).toBeGreaterThan(1) expect(pageVersions.includes(nonEnterpriseDefaultVersion)).toBe(false) @@ -500,7 +524,9 @@ describe('Page class', () => { // Note this test will go out of date when we deprecate 2.20 test('pages that apply to newer enterprise versions', async () => { const page = await Page.init({ - relativePath: 'github/administering-a-repository/comparing-releases.md', + relativePath: process.env.FEATURE_NEW_SITETREE + ? 'github/administering-a-repository/releasing-projects-on-github/comparing-releases.md' + : 'github/administering-a-repository/comparing-releases.md', basePath: path.join(__dirname, '../../content'), languageCode: 'en' }) diff --git a/tests/unit/pages.js b/tests/unit/pages.js index c2443730fe..8f683fcfaa 100644 --- a/tests/unit/pages.js +++ b/tests/unit/pages.js @@ -1,3 +1,4 @@ +require('../../lib/feature-flags') const path = require('path') const { loadPages, loadPageMap } = require('../../lib/pages') const languageCodes = Object.keys(require('../../lib/languages')) @@ -10,6 +11,8 @@ const entities = new Entities() const { chain, difference } = require('lodash') const checkIfNextVersionOnly = require('../../lib/check-if-next-version-only') +const testOldSiteTree = process.env.FEATURE_NEW_SITETREE ? test.skip : test + describe('pages module', () => { jest.setTimeout(60 * 1000) @@ -41,9 +44,10 @@ describe('pages module', () => { expect(brokenPages.length, expectation).toBe(0) }) + // **TODO** fix duplicate redirects after new site tree feature flag is enabled // we can't put this in tests/redirects because duplicate routes have already been // overwritten during context.pages.redirects object assignment and can't be searched for - test('redirect_from routes are unique across English pages', () => { + testOldSiteTree('redirect_from routes are unique across English pages', () => { const sourceRedirectFrom = chain(pages) .filter(['languageCode', 'en']) .filter('redirect_from')