1
0
mirror of synced 2026-01-30 15:01:41 -05:00

Merge branch 'main' into repo-sync

This commit is contained in:
Octomerger Bot
2021-05-19 23:03:28 +10:00
committed by GitHub
19 changed files with 526 additions and 83 deletions

View File

@@ -4,7 +4,7 @@
{%- if breadcrumb.href == '' -%}
<span title="{{ breadcrumb.documentType }}: {{ breadcrumb.title }}">{{ breadcrumb.title }}</span>
{%- else -%}
<a title="{{ breadcrumb.documentType }}: {{ breadcrumb.title }}" href="{{ breadcrumb.href }}" class="d-inline-block {% if breadcrumb.href == currentPath %}text-gray-light{% endif %}">{{ breadcrumb.title }}</a>
<a title="{{ breadcrumb.documentType }}: {{ breadcrumb.title }}" href="{{ breadcrumb.href }}" class="d-inline-block {% if breadcrumb.href == currentPath %}color-text-tertiary{% endif %}">{{ breadcrumb.title }}</a>
{%- endif -%}
{%- endfor -%}
</nav>

View File

@@ -13,7 +13,7 @@
{% if currentProductTree.renderedShortTitle %}{% assign productTitle = currentProductTree.renderedShortTitle %}{% else %}{% assign productTitle = currentProductTree.renderedFullTitle %}{% endif %}
<li title="" class="sidebar-product mb-2">
<a href="{{currentProductTree.href}}" class="pl-4 pr-5 pb-1 f4">{{ productTitle }}</a>
<a href="{{currentProductTree.href}}" class="pl-4 pr-5 pb-1 f4 color-text-primary">{{ productTitle }}</a>
</li>
<li>
<ul class="sidebar-categories list-style-none">
@@ -22,12 +22,12 @@
<li class="sidebar-category py-1 {% if currentPath contains childPage.href %}active {% if currentPath == childPage.href %}is-current-page {% endif %}{% endif %}{% if standaloneCategory %}standalone-category{% endif %}">
{% if childPage.renderedShortTitle %}{% assign childTitle = childPage.renderedShortTitle %}{% else %}{% assign childTitle = childPage.renderedFullTitle %}{% endif %}
{% if standaloneCategory %}
<a href="{{childPage.href}}" class="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3">{{ childTitle }}</a>
<a href="{{childPage.href}}" class="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3 color-text-primary">{{ childTitle }}</a>
{% else %}
<details class="dropdown-withArrow details details-reset" {% if currentPath contains childPage.href or forloop.index < 4 %}open{% endif %}>
<summary>
<div class="d-flex flex-justify-between">
<a href="{{childPage.href}}" class="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3">{{ childTitle }}</a>
<a href="{{childPage.href}}" class="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3 color-text-primary">{{ childTitle }}</a>
{% if forloop.index < 4 %}
<svg xmlns="http://www.w3.org/2000/svg" class="octicon flex-shrink-0 arrow mr-3" style="margin-top:7px" viewBox="0 0 16 16" width="16" height="16"> <path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></svg>
{% endif %}
@@ -41,12 +41,12 @@
{% for grandchildPage in childPage.childPages %}
{% if grandchildPage.renderedShortTitle %}{% assign grandchildTitle = grandchildPage.renderedShortTitle %}{% else %}{% assign grandchildTitle = grandchildPage.renderedFullTitle %}{% endif %}
<li class="sidebar-maptopic {% if currentPath contains grandchildPage.href %}active {% if currentPath == grandchildPage.href %}is-current-page{% endif %}{% endif %}">
<a href="{{grandchildPage.href}}" class="pl-4 pr-5 py-2">{{ grandchildTitle }} </a>
<a href="{{grandchildPage.href}}" class="pl-4 pr-5 py-2 color-text-primary">{{ grandchildTitle }} </a>
<ul class="sidebar-articles my-2">
{% for greatgrandchildPage in grandchildPage.childPages %}
{% if greatgrandchildPage.renderedShortTitle %}{% assign greatgrandchildTitle = greatgrandchildPage.renderedShortTitle %}{% else %}{% assign greatgrandchildTitle = greatgrandchildPage.renderedFullTitle %}{% endif %}
<li class="sidebar-article {% if currentPath contains greatgrandchildPage.href %}active {% if currentPath == greatgrandchildPage.href %}is-current-page{% endif %}{% endif %}">
<a href="{{greatgrandchildPage.href}}" class="pl-6 pr-5 py-1{% if forloop.last %} pb-2{% endif %}">{{ greatgrandchildTitle }}</a>
<a href="{{greatgrandchildPage.href}}" class="pl-6 pl-4 pr-5 py-1{% if forloop.last %} pb-2{% endif %} color-text-primary">{{ greatgrandchildTitle }}</a>
</li>
{% endfor %}
</ul>
@@ -59,7 +59,7 @@
{% for grandchildPage in childPage.childPages %}
{% if grandchildPage.renderedShortTitle %}{% assign grandchildTitle = grandchildPage.renderedShortTitle %}{% else %}{% assign grandchildTitle = grandchildPage.renderedFullTitle %}{% endif %}
<li class="sidebar-article {% if currentPath contains grandchildPage.href %}active {% if currentPath == grandchildPage.href %}is-current-page{% endif %}{% endif %}">
<a href="{{grandchildPage.href}}" class="pl-4 pr-5 py-1{% if forloop.last %} pb-2{% endif %}">{{ grandchildTitle }}</a>
<a href="{{grandchildPage.href}}" class="pl-6 pl-4 pr-5 py-1{% if forloop.last %} pb-2{% endif %} color-text-primary">{{ grandchildTitle }}</a>
</li>
{% endfor %}
</ul>

View File

@@ -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,

View File

@@ -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`

View File

@@ -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')

View File

@@ -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)
})
}

View File

@@ -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"
}

View File

@@ -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",

View File

@@ -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 }

View File

@@ -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
}

View File

@@ -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([])
})
})
})

View File

@@ -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')

View File

@@ -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)

View File

@@ -1,3 +1,4 @@
require('../../lib/feature-flags')
const { getDOM } = require('../helpers/supertest')
describe('sidebar', () => {

View File

@@ -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}/`)

View File

@@ -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/<category>/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 () => {

View File

@@ -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 = `<a class="link-with-intro Bump-link--hover no-underline d-block offset-lg-2 col-lg-8 mb-5" href="/en/github/writing-on-github/basic-writing-and-formatting-syntax">
<h4 class="link-with-intro-title h4-mktg">${page.title}<span class="Bump-link-symbol">→</span></h4>
<p class="link-with-intro-intro f5">${page.intro}</p>

View File

@@ -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'
})

View File

@@ -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')