diff --git a/.env.example b/.env.example
index 20c941977c..e8896feab8 100644
--- a/.env.example
+++ b/.env.example
@@ -1,6 +1,3 @@
ALGOLIA_API_KEY=
ALGOLIA_APPLICATION_ID=
ALLOW_TRANSLATION_COMMITS=
-EARLY_ACCESS_HOSTNAME=
-EARLY_ACCESS_SHARED_SECRET=
-GITHUB_TOKEN=
\ No newline at end of file
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 566734f9eb..a0597d5a0b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -127,10 +127,3 @@ jobs:
run: npx jest tests/${{ matrix.test-group }}/
env:
NODE_OPTIONS: "--max_old_space_size=4096"
-
- - name: Send Slack notification if workflow fails
- uses: rtCamp/action-slack-notify@e17352feaf9aee300bf0ebc1dfbf467d80438815
- if: failure() && github.ref == 'early-access'
- env:
- SLACK_WEBHOOK: ${{ secrets.DOCS_ALERTS_SLACK_WEBHOOK }}
- SLACK_MESSAGE: "Tests are failing on the `early-access` branch. https://github.com/github/docs-internal/tree/early-access"
diff --git a/.gitignore b/.gitignore
index 4c960428f8..9c06d3b323 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,13 +1,14 @@
.algolia-cache
.DS_Store
.env
-node_modules
+/node_modules/
npm-debug.log
-coverage
-content/early-access
-content/early-access-test
+coverage/
+/assets/early-access/
+/content/early-access/
+/data/early-access/
# blc: broken link checker
blc_output.log
blc_output_internal.log
-dist
+/dist/
diff --git a/crowdin.yml b/crowdin.yml
index 5f0cf63ee4..726820c67c 100644
--- a/crowdin.yml
+++ b/crowdin.yml
@@ -14,9 +14,10 @@ files:
"data/reusables/README.md",
"data/variables/product.yml",
"data/variables/README.md",
+ "data/early-access",
"data/graphql",
"data/products.yml"
- ]
+ ]
# These end up as env vars used by the GitHub Actions workflow
project_id_env: CROWDIN_PROJECT_ID
diff --git a/lib/fetch-early-access-paths.js b/lib/fetch-early-access-paths.js
deleted file mode 100644
index 6cc6b86e02..0000000000
--- a/lib/fetch-early-access-paths.js
+++ /dev/null
@@ -1,33 +0,0 @@
-// This module loads an array of Early Access page paths from EARLY_ACCESS_HOSTNAME
-//
-// See also middleware/early-acces-proxy.js which fetches Early Access docs from the obscured remote host
-
-require('dotenv').config()
-
-const got = require('got')
-const isURL = require('is-url')
-
-module.exports = async function fetchEarlyAccessPaths () {
- let url
- if (process.env.NODE_ENV === 'test') return []
-
- if (!isURL(process.env.EARLY_ACCESS_HOSTNAME)) {
- console.log('EARLY_ACCESS_HOSTNAME is not defined; skipping fetching early access paths')
- return []
- }
-
- try {
- url = `${process.env.EARLY_ACCESS_HOSTNAME}/early-access-paths.json`
- const { body } = await got(url, {
- json: true,
- timeout: 3000,
- headers: {
- 'early-access-shared-secret': process.env.EARLY_ACCESS_SHARED_SECRET
- }
- })
- return body
- } catch (err) {
- console.error('Unable to fetch early-access-paths.json from', url, err)
- return []
- }
-}
diff --git a/lib/warm-server.js b/lib/warm-server.js
index 355fd7531c..5abfef326e 100644
--- a/lib/warm-server.js
+++ b/lib/warm-server.js
@@ -1,5 +1,4 @@
-const fetchEarlyAccessPaths = require('./fetch-early-access-paths')
-let pages, site, redirects, siteTree, earlyAccessPaths
+let pages, site, redirects, siteTree
module.exports = async function warmServer () {
if (!pages) {
@@ -8,10 +7,9 @@ module.exports = async function warmServer () {
}
// Promise.all is used to load multiple things in parallel
- ;[pages, site, earlyAccessPaths] = await Promise.all([
+ ;[pages, site] = await Promise.all([
require('./pages')(),
- require('./site-data')(),
- fetchEarlyAccessPaths()
+ require('./site-data')()
])
redirects = await require('./redirects/precompile')(pages)
@@ -19,6 +17,6 @@ module.exports = async function warmServer () {
}
return {
- pages, site, redirects, siteTree, earlyAccessPaths
+ pages, site, redirects, siteTree
}
}
diff --git a/middleware/context.js b/middleware/context.js
index 55f0f055e5..54fbbdcbe7 100644
--- a/middleware/context.js
+++ b/middleware/context.js
@@ -12,7 +12,7 @@ const featureFlags = Object.keys(require('../feature-flags'))
// Note that additional middleware in middleware/index.js adds to this context object
module.exports = async function contextualize (req, res, next) {
// Ensure that we load some data only once on first request
- const { site, redirects, pages, siteTree, earlyAccessPaths } = await warmServer()
+ const { site, redirects, pages, siteTree } = await warmServer()
req.context = {}
// make feature flag environment variables accessible in layouts
@@ -33,7 +33,6 @@ module.exports = async function contextualize (req, res, next) {
req.context.currentPath = req.path
req.context.query = req.query
req.context.languages = languages
- req.context.earlyAccessPaths = earlyAccessPaths
req.context.productNames = productNames
req.context.enterpriseServerReleases = enterpriseServerReleases
req.context.enterpriseServerVersions = Object.keys(allVersions).filter(version => version.startsWith('enterprise-server@'))
diff --git a/middleware/early-access-paths.js b/middleware/early-access-paths.js
deleted file mode 100644
index 164780f1ac..0000000000
--- a/middleware/early-access-paths.js
+++ /dev/null
@@ -1,33 +0,0 @@
-const { chain } = require('lodash')
-let paths
-
-// This middleware finds all pages with `hidden: true` frontmatter
-// and responds with a JSON array of all requests paths (and redirects) that lead to those pages.
-
-// Requesting this path from EARLY_ACCESS_HOSTNAME will respond with an array of Early Access paths.
-// Requesting this path from docs.github.com (production) will respond with an empty array (no Early Access paths).
-
-module.exports = async (req, res, next) => {
- if (req.path !== '/early-access-paths.json') return next()
-
- if (
- !req.headers ||
- !req.headers['early-access-shared-secret'] ||
- req.headers['early-access-shared-secret'] !== process.env.EARLY_ACCESS_SHARED_SECRET
- ) {
- return res.status(401).send({ error: '401 Unauthorized' })
- }
-
- paths = paths || chain(req.context.pages)
- .filter(page => page.hidden && page.languageCode === 'en')
- .map(page => {
- const permalinks = page.permalinks.map(permalink => permalink.href)
- const redirects = Object.keys(page.redirects)
- return permalinks.concat(redirects)
- })
- .flatten()
- .uniq()
- .value()
-
- return res.json(paths)
-}
diff --git a/middleware/early-access-proxy.js b/middleware/early-access-proxy.js
deleted file mode 100644
index 5df20e2228..0000000000
--- a/middleware/early-access-proxy.js
+++ /dev/null
@@ -1,25 +0,0 @@
-// This module serves requests to Early Access content from a hidden proxy host (EARLY_ACCESS_HOSTNAME).
-// Paths to this content are fetched in the warmServer module at startup.
-
-const got = require('got')
-const isURL = require('is-url')
-
-module.exports = async (req, res, next) => {
- if (
- isURL(process.env.EARLY_ACCESS_HOSTNAME) &&
- req.context &&
- req.context.earlyAccessPaths &&
- req.context.earlyAccessPaths.includes(req.path)
- ) {
- try {
- const proxyURL = `${process.env.EARLY_ACCESS_HOSTNAME}${req.path}`
- const proxiedRes = await got(proxyURL)
- res.set('content-type', proxiedRes.headers['content-type'])
- res.send(proxiedRes.body)
- } catch (err) {
- next()
- }
- } else {
- next()
- }
-}
diff --git a/middleware/index.js b/middleware/index.js
index 98106e419c..9a7ea154be 100644
--- a/middleware/index.js
+++ b/middleware/index.js
@@ -40,8 +40,6 @@ module.exports = function (app) {
app.use(require('./detect-language'))
app.use(asyncMiddleware(require('./context')))
app.use('/csrf', require('./csrf-route'))
- app.use(require('./early-access-paths'))
- app.use(require('./early-access-proxy'))
app.use(require('./find-page'))
app.use(require('./notices'))
app.use(require('./archived-enterprise-versions'))
@@ -56,6 +54,7 @@ module.exports = function (app) {
app.use(require('./contextualizers/webhooks'))
app.use(require('./disable-caching-on-safari'))
app.get('/_500', asyncMiddleware(require('./trigger-error')))
+ app.get('/hidden', require('./list-hidden-pages'))
app.use(require('./breadcrumbs'))
app.use(require('./featured-links'))
app.get('/*', asyncMiddleware(require('./render-page')))
diff --git a/middleware/list-hidden-pages.js b/middleware/list-hidden-pages.js
new file mode 100644
index 0000000000..d82a3d7b71
--- /dev/null
+++ b/middleware/list-hidden-pages.js
@@ -0,0 +1,21 @@
+module.exports = async function listHidden (req, res, next) {
+ if (process.env.NODE_ENV === 'production') {
+ return res.status(403).end()
+ }
+
+ const hiddenPages = req.context.pages.filter(page => page.hidden)
+ let urls = []
+
+ hiddenPages.forEach(page => {
+ const pageUrls = page.permalinks.map(permalink => permalink.href)
+ urls = urls.concat(pageUrls)
+ })
+
+ const output = `
+
+ ${urls.map(url => `- ${url}
`).join('\n')}
+
+ `
+
+ return res.send(output)
+}
diff --git a/middleware/render-page.js b/middleware/render-page.js
index c24b00935d..e7921135db 100644
--- a/middleware/render-page.js
+++ b/middleware/render-page.js
@@ -1,5 +1,4 @@
const { get } = require('lodash')
-const env = require('lil-env-thing')
const { liquid } = require('../lib/render-content')
const patterns = require('../lib/patterns')
const layouts = require('../lib/layouts')
@@ -64,7 +63,7 @@ module.exports = async function renderPage (req, res, next) {
}
// `?json` query param for debugging request context
- if ('json' in req.query && !env.production) {
+ if ('json' in req.query && process.env.NODE_ENV !== 'production') {
if (req.query.json.length > 1) {
// deep reference: ?json=page.permalinks
return res.json(get(context, req.query.json))
diff --git a/package-lock.json b/package-lock.json
index 948120da18..18ed555816 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3169,7 +3169,7 @@
},
"agentkeepalive": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-2.2.0.tgz",
+ "resolved": "http://registry.npmjs.org/agentkeepalive/-/agentkeepalive-2.2.0.tgz",
"integrity": "sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8="
},
"aggregate-error": {
@@ -3329,7 +3329,7 @@
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=",
"requires": {
"sprintf-js": "~1.0.2"
}
@@ -4114,7 +4114,7 @@
},
"brfs": {
"version": "1.6.1",
- "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz",
+ "resolved": "http://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz",
"integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==",
"requires": {
"quote-stream": "^1.0.1",
@@ -4193,7 +4193,7 @@
},
"browserify-aes": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
"requires": {
"buffer-xor": "^1.0.3",
@@ -4227,7 +4227,7 @@
},
"browserify-rsa": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+ "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
"requires": {
"bn.js": "^4.1.0",
@@ -5645,7 +5645,7 @@
},
"create-hash": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
"requires": {
"cipher-base": "^1.0.1",
@@ -5657,7 +5657,7 @@
},
"create-hmac": {
"version": "1.1.7",
- "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
"requires": {
"cipher-base": "^1.0.3",
@@ -6384,7 +6384,7 @@
},
"diffie-hellman": {
"version": "5.0.3",
- "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
"integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
"requires": {
"bn.js": "^4.1.0",
@@ -6824,7 +6824,7 @@
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=",
"dev": true,
"requires": {
"is-arrayish": "^0.2.1"
@@ -9962,7 +9962,7 @@
"dependencies": {
"mkdirp": {
"version": "0.3.0",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
+ "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
"integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4="
},
"nopt": {
@@ -14899,11 +14899,6 @@
"type-check": "~0.3.2"
}
},
- "lil-env-thing": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/lil-env-thing/-/lil-env-thing-1.0.0.tgz",
- "integrity": "sha1-etQmBiG/M1rR6HE1d5s15vFmxns="
- },
"limited-request-queue": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/limited-request-queue/-/limited-request-queue-2.0.0.tgz",
@@ -15143,7 +15138,7 @@
},
"magic-string": {
"version": "0.22.5",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
+ "resolved": "http://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
"integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==",
"requires": {
"vlq": "^0.2.2"
@@ -18704,7 +18699,7 @@
},
"sha.js": {
"version": "2.4.11",
- "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"requires": {
"inherits": "^2.0.1",
diff --git a/package.json b/package.json
index f45ba73e65..672b7aaa44 100644
--- a/package.json
+++ b/package.json
@@ -49,7 +49,6 @@
"is-url": "^1.2.4",
"js-cookie": "^2.2.1",
"js-yaml": "^3.14.0",
- "lil-env-thing": "^1.0.0",
"liquid": "^5.1.0",
"lodash": "^4.17.19",
"mini-css-extract-plugin": "^0.9.0",
diff --git a/tests/content/crowdin-config.js b/tests/content/crowdin-config.js
index 2bbb509482..b54168c969 100644
--- a/tests/content/crowdin-config.js
+++ b/tests/content/crowdin-config.js
@@ -1,9 +1,46 @@
const config = require('../../lib/crowdin-config').read()
+const loadPages = require('../../lib/pages')
+const ignoredPagePaths = config.files[0].ignore
+const ignoredDataPaths = config.files[2].ignore
describe('crowdin.yml config file', () => {
+ let pages
+ beforeAll(async (done) => {
+ pages = await loadPages()
+ done()
+ })
+
test('has expected file stucture', async () => {
expect(config.files.length).toBe(3)
expect(config.files[0].source).toBe('/content/**/*.md')
expect(config.files[0].ignore).toContain('/content/README.md')
})
+
+ test('ignores all Early Access paths', async () => {
+ expect(ignoredPagePaths).toContain('content/early-access')
+ expect(ignoredDataPaths).toContain('data/early-access')
+ })
+
+ test('ignores all hidden pages', async () => {
+ const hiddenPages = pages
+ .filter(page => page.hidden && page.languageCode === 'en')
+ .map(page => `/content/${page.relativePath}`)
+ const overlooked = hiddenPages.filter(page => !isIgnored(page, ignoredPagePaths))
+ const message = `Found some hidden pages that are not yet excluded from localization.
+ Please copy and paste the lines below into the \`ignore\` section of /crowdin.yml: \n\n"${overlooked.join('",\n"')}"`
+
+ // This may not be true anymore given the separation of Early Access docs
+ // expect(hiddenPages.length).toBeGreaterThan(0)
+ expect(ignoredPagePaths.length).toBeGreaterThan(0)
+ expect(overlooked, message).toHaveLength(0)
+ })
})
+
+// file is ignored if its exact filename in the list,
+// or if it's within an ignored directory
+function isIgnored (filename, ignoredPagePaths) {
+ return ignoredPagePaths.some(ignoredPath => {
+ const isDirectory = !ignoredPath.endsWith('.md')
+ return ignoredPath === filename || (isDirectory && filename.startsWith(ignoredPath))
+ })
+}
diff --git a/tests/rendering/early-access-paths.js b/tests/rendering/early-access-paths.js
deleted file mode 100644
index d597b54b9d..0000000000
--- a/tests/rendering/early-access-paths.js
+++ /dev/null
@@ -1,64 +0,0 @@
-const MockExpressResponse = require('mock-express-response')
-const middleware = require('../../middleware/early-access-paths')
-
-describe('GET /early-access-paths.json', () => {
- beforeEach(() => {
- delete process.env['early-access-shared-secret']
- })
-
- test('responds with 401 if shared secret is missing', async () => {
- const req = {
- path: '/early-access-paths.json',
- headers: {}
- }
- const res = new MockExpressResponse()
- const next = jest.fn()
- await middleware(req, res, next)
-
- expect(res._getJSON()).toEqual({ error: '401 Unauthorized' })
- })
-
- test('responds with an array of hidden paths', async () => {
- process.env.EARLY_ACCESS_SHARED_SECRET = 'bananas'
-
- const req = {
- path: '/early-access-paths.json',
- headers: {
- 'early-access-shared-secret': 'bananas'
- },
- context: {
- pages: [
- {
- hidden: true,
- languageCode: 'en',
- permalinks: [
- { href: '/some-hidden-page' }
- ],
- redirects: {
- '/old-hidden-page': '/new-hidden-page'
- }
- },
- {
- hidden: false,
- languageCode: 'en'
- }
- ]
- }
- }
- const res = new MockExpressResponse()
- const next = jest.fn()
- await middleware(req, res, next)
-
- expect(res._getJSON()).toEqual(['/some-hidden-page', '/old-hidden-page'])
- })
-
- test('ignores requests to other paths', async () => {
- const req = {
- path: '/not-early-access'
- }
- const res = new MockExpressResponse()
- const next = jest.fn()
- await middleware(req, res, next)
- expect(next).toHaveBeenCalled()
- })
-})
diff --git a/tests/rendering/early-access-proxy.js b/tests/rendering/early-access-proxy.js
deleted file mode 100644
index e0e60ee287..0000000000
--- a/tests/rendering/early-access-proxy.js
+++ /dev/null
@@ -1,80 +0,0 @@
-
-const middleware = require('../../middleware/early-access-proxy')
-const nock = require('nock')
-const MockExpressResponse = require('mock-express-response')
-
-describe('Early Access middleware', () => {
- const OLD_EARLY_ACCESS_HOSTNAME = process.env.EARLY_ACCESS_HOSTNAME
-
- beforeAll(() => {
- process.env.EARLY_ACCESS_HOSTNAME = 'https://secret-website.com'
- })
-
- afterAll(() => {
- process.env.EARLY_ACCESS_HOSTNAME = OLD_EARLY_ACCESS_HOSTNAME
- })
-
- const baseReq = {
- context: {
- earlyAccessPaths: ['/alpha-product/foo', '/beta-product/bar', '/baz']
- }
- }
-
- test('are proxied from an obscured host', async () => {
- const mock = nock('https://secret-website.com')
- .get('/alpha-product/foo')
- .reply(200, 'yay here is your proxied content', { 'content-type': 'text/html' })
- const req = { ...baseReq, path: '/alpha-product/foo' }
- const res = new MockExpressResponse()
- const next = jest.fn()
- await middleware(req, res, next)
- expect(mock.isDone()).toBe(true)
- expect(res._getString()).toBe('yay here is your proxied content')
- })
-
- test('follows redirects', async () => {
- const mock = nock('https://secret-website.com')
- .get('/alpha-product/foo')
- .reply(301, undefined, { Location: 'https://secret-website.com/alpha-product/foo2' })
- .get('/alpha-product/foo2')
- .reply(200, 'yay you survived the redirect', { 'content-type': 'text/html' })
- const req = { ...baseReq, path: '/alpha-product/foo' }
- const res = new MockExpressResponse()
- const next = jest.fn()
- await middleware(req, res, next)
- expect(mock.isDone()).toBe(true)
- expect(res._getString()).toBe('yay you survived the redirect')
- })
-
- test('calls next() if no redirect is found', async () => {
- const req = { ...baseReq, path: '/en' }
- const res = new MockExpressResponse()
- const next = jest.fn()
- await middleware(req, res, next)
- expect(next).toHaveBeenCalled()
- })
-
- test('calls next() if proxy request respond with 404', async () => {
- const mock = nock('https://secret-website.com')
- .get('/beta-product/bar')
- .reply(404, 'no dice', { 'content-type': 'text/html' })
- const req = { ...baseReq, path: '/beta-product/bar' }
- const res = new MockExpressResponse()
- const next = jest.fn()
- await middleware(req, res, next)
- expect(mock.isDone()).toBe(true)
- expect(next).toHaveBeenCalled()
- })
-
- test('calls next() if proxy request responds with 500', async () => {
- const mock = nock('https://secret-website.com')
- .get('/beta-product/bar')
- .reply(500, 'no dice', { 'content-type': 'text/html' })
- const req = { ...baseReq, path: '/beta-product/bar' }
- const res = new MockExpressResponse()
- const next = jest.fn()
- await middleware(req, res, next)
- expect(mock.isDone()).toBe(true)
- expect(next).toHaveBeenCalled()
- })
-})
diff --git a/tests/rendering/server.js b/tests/rendering/server.js
index 2b588df6b5..27c5b1bc51 100644
--- a/tests/rendering/server.js
+++ b/tests/rendering/server.js
@@ -3,6 +3,7 @@ const enterpriseServerReleases = require('../../lib/enterprise-server-releases')
const { get, getDOM, head } = require('../helpers')
const path = require('path')
const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version')
+const loadPages = require('../../lib/pages')
describe('server', () => {
jest.setTimeout(60 * 1000)
@@ -375,6 +376,44 @@ describe('server', () => {
})
})
+ describe('hidden articles', () => {
+ let hiddenPageHrefs, hiddenPages
+
+ beforeAll(async (done) => {
+ const $ = await getDOM('/hidden')
+ hiddenPageHrefs = $('a').map((i, el) => $(el).attr('href')).get()
+
+ const allPages = await loadPages()
+ hiddenPages = allPages.filter(page => page.languageCode === 'en' && page.hidden)
+
+ done()
+ })
+
+ test('are listed at /hidden', async () => {
+ expect(hiddenPageHrefs.length).toBe(hiddenPages.length)
+ })
+
+ test('are not listed at /hidden in production', async () => {
+ const oldNodeEnv = process.env.NODE_ENV
+ process.env.NODE_ENV = 'production'
+ const res = await get('/hidden')
+ process.env.NODE_ENV = oldNodeEnv
+ expect(res.statusCode).toBe(403)
+ })
+
+ test('have noindex meta tags', async () => {
+ if (hiddenPageHrefs.length > 0) {
+ const $ = await getDOM(hiddenPageHrefs[0])
+ expect($('meta[content="noindex"]').length).toBe(1)
+ }
+ })
+
+ test('non-hidden articles do not have noindex meta tags', async () => {
+ const $ = await getDOM('/en/articles/set-up-git')
+ expect($('meta[content="noindex"]').length).toBe(0)
+ })
+ })
+
describe('redirects', () => {
test('redirects old articles to their English URL', async () => {
const res = await get('/articles/deleting-a-team')
diff --git a/tests/unit/early-access.js b/tests/unit/early-access.js
index 84732a31b8..f347e018bb 100644
--- a/tests/unit/early-access.js
+++ b/tests/unit/early-access.js
@@ -6,9 +6,19 @@ const testViaActionsOnly = process.env.GITHUB_ACTIONS ? test : test.skip
// TODO test ea-config
// TODO this should only run locally
-describe('cloning content/early-access', () => {
+describe('cloning early-access', () => {
+ testViaActionsOnly('the assets directory exists', async () => {
+ const eaContentDir = path.join(process.cwd(), 'assets/early-access')
+ expect(fs.existsSync(eaContentDir)).toBe(true)
+ })
+
testViaActionsOnly('the content directory exists', async () => {
- const eaContentDir = path.join(process.cwd(), 'content/early-access-test')
+ const eaContentDir = path.join(process.cwd(), 'content/early-access')
+ expect(fs.existsSync(eaContentDir)).toBe(true)
+ })
+
+ testViaActionsOnly('the data directory exists', async () => {
+ const eaContentDir = path.join(process.cwd(), 'data/early-access')
expect(fs.existsSync(eaContentDir)).toBe(true)
})
})