Forces the removal of a self-hosted runner from an enterprise. You can use this endpoint to completely remove the runner when the machine you were using no longer exists.
\nYou must authenticate using an access token with the admin:enterprise scope to use this endpoint.
The slug version of the enterprise name. You can also substitute this value with the enterprise id.
" + }, + { + "name": "phrase", + "description": "A search phrase. For more information, see [Searching the audit log](https://docs.github.com/github/setting-up-and-managing-organizations-and-teams/reviewing-the-audit-log-for-your-organization#searching-the-audit-log).", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "descriptionHTML": "A search phrase. For more information, see Searching the audit log.
" + }, + { + "name": "include", + "description": "The event types to include:\n\n- `web` - returns web (non-Git) events\n- `git` - returns Git events\n- `all` - returns both web and Git events\n\nThe default is `web`.", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "web", + "git", + "all" + ] + }, + "descriptionHTML": "The event types to include:
\nweb - returns web (non-Git) eventsgit - returns Git eventsall - returns both web and Git eventsThe default is web.
A cursor, as given in the Link header. If specified, the query only searches for events after this cursor.
" + }, + { + "name": "before", + "description": "A cursor, as given in the [Link header](https://docs.github.com/rest/overview/resources-in-the-rest-api#link-header). If specified, the query only searches for events before this cursor.", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "descriptionHTML": "A cursor, as given in the Link header. If specified, the query only searches for events before this cursor.
" + }, + { + "name": "per_page", + "description": "Results per page (max 100)", + "in": "query", + "schema": { + "type": "integer", + "default": 30 + }, + "descriptionHTML": "Results per page (max 100)
" + } + ], + "x-codeSamples": [ + { + "lang": "Shell", + "source": "curl \\\n -H \"Accept: application/vnd.github.v3+json\" \\\n https://api.github.com/enterprises/ENTERPRISE/audit-log", + "html": "curl \\\n -H \"Accept: application/vnd.github.v3+json\" \\\n https://api.github.com/enterprises/ENTERPRISE/audit-log"
+ },
+ {
+ "lang": "JavaScript",
+ "source": "await octokit.request('GET /enterprises/{enterprise}/audit-log', {\n enterprise: 'enterprise'\n})",
+ "html": "await octokit.request('GET /enterprises/{enterprise}/audit-log', {\n enterprise: 'enterprise'\n})\n"
+ }
+ ],
+ "summary": "Get the audit log for an enterprise",
+ "description": "**Note:** The audit log REST API is currently in beta and is subject to change. To join the beta, talk to your services or sales contact at GitHub.\n\nGets the audit log for an enterprise. To use this endpoint, you must be an enterprise admin, and you must use an access token with the `admin:enterprise` scope.",
+ "operationId": "audit-log/get-audit-log",
+ "tags": [
+ "audit-log"
+ ],
+ "externalDocs": {
+ "description": "API method documentation",
+ "url": "https://docs.github.com/rest/reference/enterprise-admin#get-the-audit-log-for-an-enterprise"
+ },
+ "x-github": {
+ "githubCloudOnly": true,
+ "enabledForGitHubApps": false,
+ "previews": [],
+ "category": "enterprise-admin",
+ "subcategory": "audit-log"
+ },
+ "slug": "get-the-audit-log-for-an-enterprise",
+ "category": "enterprise-admin",
+ "categoryLabel": "Enterprise admin",
+ "subcategory": "audit-log",
+ "subcategoryLabel": "Audit log",
+ "notes": [],
+ "bodyParameters": [],
+ "descriptionHTML": "Note: The audit log REST API is currently in beta and is subject to change. To join the beta, talk to your services or sales contact at GitHub.
\nGets the audit log for an enterprise. To use this endpoint, you must be an enterprise admin, and you must use an access token with the admin:enterprise scope.
[\n {\n \"@timestamp\": 1606929874512,\n \"action\": \"team.add_member\",\n \"actor\": \"octocat\",\n \"created_at\": 1606929874512,\n \"org\": \"octo-corp\",\n \"team\": \"octo-corp/example-team\",\n \"user\": \"monalisa\"\n },\n {\n \"@timestamp\": 1606507117008,\n \"action\": \"org.create\",\n \"actor\": \"octocat\",\n \"created_at\": 1606507117008,\n \"org\": \"octocat-test-org\"\n },\n {\n \"@timestamp\": 1605719148837,\n \"action\": \"repo.destroy\",\n \"actor\": \"monalisa\",\n \"created_at\": 1605719148837,\n \"org\": \"mona-org\",\n \"repo\": \"mona-org/mona-test-repo\",\n \"visibility\": \"private\"\n }\n]\n"
+ }
+ ]
+ },
{
"verb": "get",
"requestPath": "/enterprises/{enterprise}/settings/billing/actions",
@@ -14225,6 +14342,120 @@
"bodyParameters": [],
"descriptionHTML": "Removes a repository from an organization secret when the visibility for repository access is set to selected. The visibility is set when you Create or update an organization secret. You must authenticate using an access token with the admin:org scope to use this endpoint. GitHub Apps must have the secrets organization permission to use this endpoint.
A search phrase. For more information, see Searching the audit log.
" + }, + { + "name": "include", + "description": "The event types to include:\n\n- `web` - returns web (non-Git) events\n- `git` - returns Git events\n- `all` - returns both web and Git events\n\nThe default is `web`.", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "web", + "git", + "all" + ] + }, + "descriptionHTML": "The event types to include:
\nweb - returns web (non-Git) eventsgit - returns Git eventsall - returns both web and Git eventsThe default is web.
A cursor, as given in the Link header. If specified, the query only searches for events after this cursor.
" + }, + { + "name": "before", + "description": "A cursor, as given in the [Link header](https://docs.github.com/rest/overview/resources-in-the-rest-api#link-header). If specified, the query only searches for events before this cursor.", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "descriptionHTML": "A cursor, as given in the Link header. If specified, the query only searches for events before this cursor.
" + }, + { + "name": "per_page", + "description": "Results per page (max 100)", + "in": "query", + "schema": { + "type": "integer", + "default": 30 + }, + "descriptionHTML": "Results per page (max 100)
" + } + ], + "x-codeSamples": [ + { + "lang": "Shell", + "source": "curl \\\n -H \"Accept: application/vnd.github.v3+json\" \\\n https://api.github.com/orgs/ORG/audit-log", + "html": "curl \\\n -H \"Accept: application/vnd.github.v3+json\" \\\n https://api.github.com/orgs/ORG/audit-log"
+ },
+ {
+ "lang": "JavaScript",
+ "source": "await octokit.request('GET /orgs/{org}/audit-log', {\n org: 'org'\n})",
+ "html": "await octokit.request('GET /orgs/{org}/audit-log', {\n org: 'org'\n})\n"
+ }
+ ],
+ "summary": "Get the audit log for an organization",
+ "description": "**Note:** The audit log REST API is currently in beta and is subject to change. To join the beta, talk to your services or sales contact at GitHub.\n\nGets the audit log for an organization. For more information, see \"[Reviewing the audit log for your organization](https://docs.github.com/github/setting-up-and-managing-organizations-and-teams/reviewing-the-audit-log-for-your-organization).\"\n\nTo use this endpoint, you must be an organization owner, and you must use an access token with the `admin:org` scope. GitHub Apps must have the `organization_administration` read permission to use this endpoint.",
+ "operationId": "orgs/get-audit-log",
+ "tags": [
+ "orgs"
+ ],
+ "externalDocs": {
+ "description": "API method documentation",
+ "url": "https://docs.github.com/rest/reference/orgs#get-the-audit-log-for-an-organization"
+ },
+ "x-github": {
+ "githubCloudOnly": true,
+ "enabledForGitHubApps": true,
+ "previews": [],
+ "category": "orgs",
+ "subcategory": null
+ },
+ "slug": "get-the-audit-log-for-an-organization",
+ "category": "orgs",
+ "categoryLabel": "Orgs",
+ "notes": [],
+ "bodyParameters": [],
+ "descriptionHTML": "Note: The audit log REST API is currently in beta and is subject to change. To join the beta, talk to your services or sales contact at GitHub.
\nGets the audit log for an organization. For more information, see \"Reviewing the audit log for your organization.\"
\nTo use this endpoint, you must be an organization owner, and you must use an access token with the admin:org scope. GitHub Apps must have the organization_administration read permission to use this endpoint.
[\n {\n \"@timestamp\": 1606929874512,\n \"action\": \"team.add_member\",\n \"actor\": \"octocat\",\n \"created_at\": 1606929874512,\n \"org\": \"octo-corp\",\n \"team\": \"octo-corp/example-team\",\n \"user\": \"monalisa\"\n },\n {\n \"@timestamp\": 1606507117008,\n \"action\": \"org.create\",\n \"actor\": \"octocat\",\n \"created_at\": 1606507117008,\n \"org\": \"octocat-test-org\"\n },\n {\n \"@timestamp\": 1605719148837,\n \"action\": \"repo.destroy\",\n \"actor\": \"monalisa\",\n \"created_at\": 1605719148837,\n \"org\": \"mona-org\",\n \"repo\": \"mona-org/mona-test-repo\",\n \"visibility\": \"private\"\n }\n]\n"
+ }
+ ]
+ },
{
"verb": "get",
"requestPath": "/orgs/{org}/blocks",
diff --git a/lib/rest/static/dereferenced/api.github.com.deref.json b/lib/rest/static/dereferenced/api.github.com.deref.json
index 873643c5bc..7dbc79d269 100644
--- a/lib/rest/static/dereferenced/api.github.com.deref.json
+++ b/lib/rest/static/dereferenced/api.github.com.deref.json
@@ -17051,6 +17051,254 @@
}
}
},
+ "/enterprises/{enterprise}/audit-log": {
+ "get": {
+ "summary": "Get the audit log for an enterprise",
+ "description": "**Note:** The audit log REST API is currently in beta and is subject to change. To join the beta, talk to your services or sales contact at GitHub.\n\nGets the audit log for an enterprise. To use this endpoint, you must be an enterprise admin, and you must use an access token with the `admin:enterprise` scope.",
+ "operationId": "audit-log/get-audit-log",
+ "tags": [
+ "audit-log"
+ ],
+ "externalDocs": {
+ "description": "API method documentation",
+ "url": "https://docs.github.com/rest/reference/enterprise-admin#get-the-audit-log-for-an-enterprise"
+ },
+ "parameters": [
+ {
+ "name": "enterprise",
+ "description": "The slug version of the enterprise name. You can also substitute this value with the enterprise id.",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "phrase",
+ "description": "A search phrase. For more information, see [Searching the audit log](https://docs.github.com/github/setting-up-and-managing-organizations-and-teams/reviewing-the-audit-log-for-your-organization#searching-the-audit-log).",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "include",
+ "description": "The event types to include:\n\n- `web` - returns web (non-Git) events\n- `git` - returns Git events\n- `all` - returns both web and Git events\n\nThe default is `web`.",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "enum": [
+ "web",
+ "git",
+ "all"
+ ]
+ }
+ },
+ {
+ "name": "after",
+ "description": "A cursor, as given in the [Link header](https://docs.github.com/rest/overview/resources-in-the-rest-api#link-header). If specified, the query only searches for events after this cursor.",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "before",
+ "description": "A cursor, as given in the [Link header](https://docs.github.com/rest/overview/resources-in-the-rest-api#link-header). If specified, the query only searches for events before this cursor.",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "per_page",
+ "description": "Results per page (max 100)",
+ "in": "query",
+ "schema": {
+ "type": "integer",
+ "default": 30
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "@timestamp": {
+ "type": "integer",
+ "description": "The time the audit log event occurred, given as a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time)."
+ },
+ "action": {
+ "type": "string",
+ "description": "The name of the action that was performed, for example `user.login` or `repo.create`."
+ },
+ "active": {
+ "type": "boolean"
+ },
+ "active_was": {
+ "type": "boolean"
+ },
+ "actor": {
+ "type": "string",
+ "description": "The actor who performed the action."
+ },
+ "blocked_user": {
+ "type": "string",
+ "description": "The username of the account being blocked."
+ },
+ "business": {
+ "type": "string"
+ },
+ "config": {
+ "type": "array"
+ },
+ "config_was": {
+ "type": "array"
+ },
+ "content_type": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "integer",
+ "description": "The time the audit log event was recorded, given as a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time)."
+ },
+ "deploy_key_fingerprint": {
+ "type": "string"
+ },
+ "emoji": {
+ "type": "string"
+ },
+ "events": {
+ "type": "array"
+ },
+ "events_were": {
+ "type": "array"
+ },
+ "explanation": {
+ "type": "string"
+ },
+ "fingerprint": {
+ "type": "string"
+ },
+ "hook_id": {
+ "type": "integer"
+ },
+ "limited_availability": {
+ "type": "boolean"
+ },
+ "message": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "old_user": {
+ "type": "string"
+ },
+ "openssh_public_key": {
+ "type": "string"
+ },
+ "org": {
+ "type": "string"
+ },
+ "previous_visibility": {
+ "type": "string"
+ },
+ "read_only": {
+ "type": "boolean"
+ },
+ "repo": {
+ "type": "string",
+ "description": "The name of the repository."
+ },
+ "repository": {
+ "type": "string",
+ "description": "The name of the repository."
+ },
+ "repository_public": {
+ "type": "boolean"
+ },
+ "target_login": {
+ "type": "string"
+ },
+ "team": {
+ "type": "string"
+ },
+ "transport_protocol": {
+ "type": "integer",
+ "description": "The type of protocol (for example, HTTP or SSH) used to transfer Git data."
+ },
+ "transport_protocol_name": {
+ "type": "string",
+ "description": "A human readable name for the protocol (for example, HTTP or SSH) used to transfer Git data."
+ },
+ "user": {
+ "type": "string",
+ "description": "The user that was affected by the action performed (if available)."
+ },
+ "visibility": {
+ "type": "string",
+ "description": "The repository visibility, for example `public` or `private`."
+ }
+ }
+ }
+ },
+ "examples": {
+ "default": {
+ "value": [
+ {
+ "@timestamp": 1606929874512,
+ "action": "team.add_member",
+ "actor": "octocat",
+ "created_at": 1606929874512,
+ "org": "octo-corp",
+ "team": "octo-corp/example-team",
+ "user": "monalisa"
+ },
+ {
+ "@timestamp": 1606507117008,
+ "action": "org.create",
+ "actor": "octocat",
+ "created_at": 1606507117008,
+ "org": "octocat-test-org"
+ },
+ {
+ "@timestamp": 1605719148837,
+ "action": "repo.destroy",
+ "actor": "monalisa",
+ "created_at": 1605719148837,
+ "org": "mona-org",
+ "repo": "mona-org/mona-test-repo",
+ "visibility": "private"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "x-github": {
+ "githubCloudOnly": true,
+ "enabledForGitHubApps": false,
+ "previews": [
+
+ ],
+ "category": "enterprise-admin",
+ "subcategory": "audit-log"
+ }
+ }
+ },
"/enterprises/{enterprise}/settings/billing/actions": {
"get": {
"summary": "Get GitHub Actions billing for an enterprise",
@@ -49105,6 +49353,253 @@
}
}
},
+ "/orgs/{org}/audit-log": {
+ "get": {
+ "summary": "Get the audit log for an organization",
+ "description": "**Note:** The audit log REST API is currently in beta and is subject to change. To join the beta, talk to your services or sales contact at GitHub.\n\nGets the audit log for an organization. For more information, see \"[Reviewing the audit log for your organization](https://docs.github.com/github/setting-up-and-managing-organizations-and-teams/reviewing-the-audit-log-for-your-organization).\"\n\nTo use this endpoint, you must be an organization owner, and you must use an access token with the `admin:org` scope. GitHub Apps must have the `organization_administration` read permission to use this endpoint.",
+ "operationId": "orgs/get-audit-log",
+ "tags": [
+ "orgs"
+ ],
+ "externalDocs": {
+ "description": "API method documentation",
+ "url": "https://docs.github.com/rest/reference/orgs#get-the-audit-log-for-an-organization"
+ },
+ "parameters": [
+ {
+ "name": "org",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "phrase",
+ "description": "A search phrase. For more information, see [Searching the audit log](https://docs.github.com/github/setting-up-and-managing-organizations-and-teams/reviewing-the-audit-log-for-your-organization#searching-the-audit-log).",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "include",
+ "description": "The event types to include:\n\n- `web` - returns web (non-Git) events\n- `git` - returns Git events\n- `all` - returns both web and Git events\n\nThe default is `web`.",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "enum": [
+ "web",
+ "git",
+ "all"
+ ]
+ }
+ },
+ {
+ "name": "after",
+ "description": "A cursor, as given in the [Link header](https://docs.github.com/rest/overview/resources-in-the-rest-api#link-header). If specified, the query only searches for events after this cursor.",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "before",
+ "description": "A cursor, as given in the [Link header](https://docs.github.com/rest/overview/resources-in-the-rest-api#link-header). If specified, the query only searches for events before this cursor.",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "per_page",
+ "description": "Results per page (max 100)",
+ "in": "query",
+ "schema": {
+ "type": "integer",
+ "default": 30
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "@timestamp": {
+ "type": "integer",
+ "description": "The time the audit log event occurred, given as a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time)."
+ },
+ "action": {
+ "type": "string",
+ "description": "The name of the action that was performed, for example `user.login` or `repo.create`."
+ },
+ "active": {
+ "type": "boolean"
+ },
+ "active_was": {
+ "type": "boolean"
+ },
+ "actor": {
+ "type": "string",
+ "description": "The actor who performed the action."
+ },
+ "blocked_user": {
+ "type": "string",
+ "description": "The username of the account being blocked."
+ },
+ "business": {
+ "type": "string"
+ },
+ "config": {
+ "type": "array"
+ },
+ "config_was": {
+ "type": "array"
+ },
+ "content_type": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "integer",
+ "description": "The time the audit log event was recorded, given as a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time)."
+ },
+ "deploy_key_fingerprint": {
+ "type": "string"
+ },
+ "emoji": {
+ "type": "string"
+ },
+ "events": {
+ "type": "array"
+ },
+ "events_were": {
+ "type": "array"
+ },
+ "explanation": {
+ "type": "string"
+ },
+ "fingerprint": {
+ "type": "string"
+ },
+ "hook_id": {
+ "type": "integer"
+ },
+ "limited_availability": {
+ "type": "boolean"
+ },
+ "message": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "old_user": {
+ "type": "string"
+ },
+ "openssh_public_key": {
+ "type": "string"
+ },
+ "org": {
+ "type": "string"
+ },
+ "previous_visibility": {
+ "type": "string"
+ },
+ "read_only": {
+ "type": "boolean"
+ },
+ "repo": {
+ "type": "string",
+ "description": "The name of the repository."
+ },
+ "repository": {
+ "type": "string",
+ "description": "The name of the repository."
+ },
+ "repository_public": {
+ "type": "boolean"
+ },
+ "target_login": {
+ "type": "string"
+ },
+ "team": {
+ "type": "string"
+ },
+ "transport_protocol": {
+ "type": "integer",
+ "description": "The type of protocol (for example, HTTP or SSH) used to transfer Git data."
+ },
+ "transport_protocol_name": {
+ "type": "string",
+ "description": "A human readable name for the protocol (for example, HTTP or SSH) used to transfer Git data."
+ },
+ "user": {
+ "type": "string",
+ "description": "The user that was affected by the action performed (if available)."
+ },
+ "visibility": {
+ "type": "string",
+ "description": "The repository visibility, for example `public` or `private`."
+ }
+ }
+ }
+ },
+ "examples": {
+ "default": {
+ "value": [
+ {
+ "@timestamp": 1606929874512,
+ "action": "team.add_member",
+ "actor": "octocat",
+ "created_at": 1606929874512,
+ "org": "octo-corp",
+ "team": "octo-corp/example-team",
+ "user": "monalisa"
+ },
+ {
+ "@timestamp": 1606507117008,
+ "action": "org.create",
+ "actor": "octocat",
+ "created_at": 1606507117008,
+ "org": "octocat-test-org"
+ },
+ {
+ "@timestamp": 1605719148837,
+ "action": "repo.destroy",
+ "actor": "monalisa",
+ "created_at": 1605719148837,
+ "org": "mona-org",
+ "repo": "mona-org/mona-test-repo",
+ "visibility": "private"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "x-github": {
+ "githubCloudOnly": true,
+ "enabledForGitHubApps": true,
+ "previews": [
+
+ ],
+ "category": "orgs",
+ "subcategory": null
+ }
+ }
+ },
"/orgs/{org}/blocks": {
"get": {
"summary": "List users blocked by an organization",
diff --git a/lib/rewrite-local-links.js b/lib/rewrite-local-links.js
index 4854a739cd..2cd18221c0 100644
--- a/lib/rewrite-local-links.js
+++ b/lib/rewrite-local-links.js
@@ -2,6 +2,7 @@ const externalRedirects = Object.keys(require('./redirects/external-sites'))
const pathUtils = require('./path-utils')
const assert = require('assert')
const nonEnterpriseDefaultVersion = require('./non-enterprise-default-version')
+const supportedPlans = Object.values(require('./all-versions')).map(v => v.plan)
// Content authors write links like `/some/article/path`, but they need to be
// rewritten on the fly to match the current language and page version
@@ -24,11 +25,21 @@ function getNewHref (link, languageCode, version) {
// e.g. `/contact` should not be replaced with `/en/contact`
if (externalRedirects.includes(href)) return
+ let newHref
+
+ // If the link has a hardcoded plan name in it (e.g., /enterprise-server/rest/reference/oauth-authorizations),
+ // only rewrite it with a language code
+ if (supportedPlans.includes(href.split('/')[1])) {
+ newHref = pathUtils.getPathWithLanguage(href, languageCode)
+ }
+
// If link is dotcom-only, just get the language code
// Otherwise, get the versioned path with language code
- const newHref = link.hasClass('dotcom-only')
- ? pathUtils.getVersionedPathWithLanguage(href, nonEnterpriseDefaultVersion, languageCode)
- : pathUtils.getVersionedPathWithLanguage(href, version, languageCode)
+ if (!newHref) {
+ newHref = link.hasClass('dotcom-only')
+ ? pathUtils.getVersionedPathWithLanguage(href, nonEnterpriseDefaultVersion, languageCode)
+ : pathUtils.getVersionedPathWithLanguage(href, version, languageCode)
+ }
if (href !== newHref) link.attr('href', newHref)
}
diff --git a/lib/site-data.js b/lib/site-data.js
index 7c4ba0c988..bdd47f1ce7 100755
--- a/lib/site-data.js
+++ b/lib/site-data.js
@@ -2,12 +2,12 @@ const path = require('path')
const flat = require('flat')
const { get, set } = require('lodash')
const languages = require('./languages')
-const dataDirectory = require('@github-docs/data-directory')
+const dataDirectory = require('./data-directory')
const encodeBracketedParentheticals = require('./encode-bracketed-parentheticals')
-const loadSiteDataFromDir = dir => ({
+const loadSiteDataFromDir = async dir => ({
site: {
- data: dataDirectory(path.join(dir, 'data'), {
+ data: await dataDirectory(path.join(dir, 'data'), {
preprocess: dataString =>
encodeBracketedParentheticals(dataString.trimEnd()),
ignorePatterns: [/README\.md$/]
@@ -18,7 +18,7 @@ const loadSiteDataFromDir = dir => ({
module.exports = async function loadSiteData () {
// load english site data
const siteData = {
- en: loadSiteDataFromDir(languages.en.dir)
+ en: await loadSiteDataFromDir(languages.en.dir)
}
// load and add other language data to siteData where keys match english keys,
@@ -26,7 +26,7 @@ module.exports = async function loadSiteData () {
const englishKeys = Object.keys(flat(siteData.en))
for (const language of Object.values(languages)) {
if (language.code === 'en') continue
- const data = loadSiteDataFromDir(language.dir)
+ const data = await loadSiteDataFromDir(language.dir)
for (const key of englishKeys) {
set(
siteData,
diff --git a/lib/warm-server.js b/lib/warm-server.js
index b123113051..36cf5e7416 100644
--- a/lib/warm-server.js
+++ b/lib/warm-server.js
@@ -4,6 +4,16 @@ const loadRedirects = require('./redirects/precompile')
const loadSiteData = require('./site-data')
const loadSiteTree = require('./site-tree')
+// Instrument these functions so that
+// it's wrapped in a timer that reports to Datadog
+const dog = {
+ loadPages: statsd.asyncTimer(loadPages, 'load_pages'),
+ loadPageMap: statsd.asyncTimer(loadPageMap, 'load_page_map'),
+ loadRedirects: statsd.asyncTimer(loadRedirects, 'load_redirects'),
+ loadSiteData: statsd.asyncTimer(loadSiteData, 'load_site_data'),
+ loadSiteTree: statsd.asyncTimer(loadSiteTree, 'load_site_tree')
+}
+
// For local caching
let pageList, pageMap, site, redirects, siteTree
@@ -32,21 +42,21 @@ async function warmServer () {
if (!pageList || !site) {
// Promise.all is used to load multiple things in parallel
[pageList, site] = await Promise.all([
- pageList || loadPages(),
- site || loadSiteData()
+ pageList || dog.loadPages(),
+ site || dog.loadSiteData()
])
}
if (!pageMap) {
- pageMap = await loadPageMap(pageList)
+ pageMap = await dog.loadPageMap(pageList)
}
if (!redirects) {
- redirects = await loadRedirects(pageList, pageMap)
+ redirects = await dog.loadRedirects(pageList, pageMap)
}
if (!siteTree) {
- siteTree = await loadSiteTree(pageMap, site, redirects)
+ siteTree = await dog.loadSiteTree(pageMap, site, redirects)
}
if (process.env.NODE_ENV !== 'test') {
@@ -58,7 +68,7 @@ async function warmServer () {
// Instrument the `warmServer` function so that
// it's wrapped in a timer that reports to Datadog
-const instrumentedWarmServer = statsd.asyncTimer(warmServer, 'warm_server')
+dog.warmServer = statsd.asyncTimer(warmServer, 'warm_server')
// We only want statistics if the priming needs to occur, so let's wrap the
// real method and return early [without statistics] whenever possible
@@ -68,5 +78,5 @@ module.exports = async function warmServerWrapper () {
return getWarmedCache()
}
- return instrumentedWarmServer()
+ return dog.warmServer()
}
diff --git a/middleware/categories-for-support-team.js b/middleware/categories-for-support-team.js
index 7a38fb0cde..4b550a566e 100644
--- a/middleware/categories-for-support-team.js
+++ b/middleware/categories-for-support-team.js
@@ -2,7 +2,7 @@
// to quickly search for Help articles by title and insert the link to
// the article into a reply to a customer.
const path = require('path')
-const fs = require('fs')
+const fs = require('fs').promises
const matter = require('gray-matter')
const dotcomDir = path.join(__dirname, '../content/github')
const dotcomIndex = path.join(dotcomDir, 'index.md')
@@ -10,24 +10,22 @@ const linkRegex = /{% (?:topic_)?link_in_list ?\/(.*?) ?%}/g
module.exports = async (req, res, next) => {
if (req.path !== '/categories.json') return next()
- const categories = generateCategories()
+ const categories = await generateCategories()
return res.json(categories)
}
-function generateCategories () {
+async function generateCategories () {
// get links included in dotcom index page.
// each link corresponds to a dotcom subdirectory
// example: getting-started-with-github
- const links = getLinks(fs.readFileSync(dotcomIndex, 'utf8'))
-
- const categories = []
+ const links = getLinks(await fs.readFile(dotcomIndex, 'utf8'))
// get links included in each subdir's index page
// these are links to articles
- links.forEach(link => {
+ const categories = await Promise.all(links.map(async link => {
const category = {}
const indexPath = getPath(link, 'index')
- const indexContents = fs.readFileSync(indexPath, 'utf8')
+ const indexContents = await fs.readFile(indexPath, 'utf8')
const { data, content } = matter(indexContents)
// get name from title frontmatter
@@ -36,29 +34,23 @@ function generateCategories () {
// get child article links
const articleLinks = getLinks(content)
- const publishedArticles = []
-
- articleLinks.forEach(articleLink => {
- const publishedArticle = {}
-
+ category.published_articles = (await Promise.all(articleLinks.map(async articleLink => {
// get title from frontmatter
const articlePath = getPath(link, articleLink)
- const articleContents = fs.readFileSync(articlePath, 'utf8')
+ const articleContents = await fs.readFile(articlePath, 'utf8')
const { data } = matter(articleContents)
// do not include map topics in list of published articles
if (data.mapTopic) return
- publishedArticle.title = data.title
- publishedArticle.slug = articleLink
+ return {
+ title: data.title,
+ slug: articleLink
+ }
+ }))).filter(Boolean)
- publishedArticles.push(publishedArticle)
- })
-
- category.published_articles = publishedArticles
-
- categories.push(category)
- })
+ return category
+ }))
return categories
}
diff --git a/middleware/csp.js b/middleware/csp.js
index a081f0490c..647448e928 100644
--- a/middleware/csp.js
+++ b/middleware/csp.js
@@ -2,47 +2,66 @@
// inline scripts and content from untrusted sources.
const { contentSecurityPolicy } = require('helmet')
+const isArchivedVersion = require('../lib/is-archived-version')
+const versionSatisfiesRange = require('../lib/version-satisfies-range')
-module.exports = contentSecurityPolicy({
- directives: {
- defaultSrc: ["'none'"],
- connectSrc: [
- "'self'",
- '*.algolia.net',
- '*.algolianet.com'
- ],
- fontSrc: [
- "'self'",
- 'data:',
- 'github-images.s3.amazonaws.com'
- ],
- imgSrc: [
- "'self'",
- 'github.githubassets.com',
- 'github-images.s3.amazonaws.com',
- 'octodex.github.com',
- 'placehold.it',
- '*.githubusercontent.com',
- 'github.com'
- ],
- objectSrc: [
- "'self'"
- ],
- scriptSrc: [
- "'self'",
- 'data:'
- ],
- frameSrc: [ // exceptions for GraphQL Explorer
- 'https://graphql-explorer.githubapp.com', // production env
- 'http://localhost:3000', // development env
- 'https://www.youtube-nocookie.com'
- ],
- styleSrc: [
- "'self'",
- "'unsafe-inline'"
- ],
- childSrc: [
- "'self'" // exception for search in deprecated GHE versions
- ]
+// module.exports = contentSecurityPolicy({
+module.exports = async (req, res, next) => {
+ const csp = {
+ directives: {
+ defaultSrc: ["'none'"],
+ connectSrc: [
+ "'self'",
+ '*.algolia.net',
+ '*.algolianet.com'
+ ],
+ fontSrc: [
+ "'self'",
+ 'data:',
+ 'github-images.s3.amazonaws.com'
+ ],
+ imgSrc: [
+ "'self'",
+ 'github.githubassets.com',
+ 'github-images.s3.amazonaws.com',
+ 'octodex.github.com',
+ 'placehold.it',
+ '*.githubusercontent.com',
+ 'github.com'
+ ],
+ objectSrc: [
+ "'self'"
+ ],
+ scriptSrc: [
+ "'self'",
+ 'data:'
+ ],
+ frameSrc: [ // exceptions for GraphQL Explorer
+ 'https://graphql-explorer.githubapp.com', // production env
+ 'http://localhost:3000', // development env
+ 'https://www.youtube-nocookie.com'
+ ],
+ styleSrc: [
+ "'self'",
+ "'unsafe-inline'"
+ ],
+ childSrc: [
+ "'self'" // exception for search in deprecated GHE versions
+ ]
+ }
}
-})
+
+ const { requestedVersion } = isArchivedVersion(req)
+
+ // Exception for Algolia instantsearch in deprecated Enterprise docs (Node.js era)
+ if (versionSatisfiesRange(requestedVersion, '<=2.19') && versionSatisfiesRange(requestedVersion, '>2.12')) {
+ csp.directives.scriptSrc.push("'unsafe-eval'")
+ }
+
+ // Exception for search in deprecated Enterprise docs <=2.12 (static site era)
+ if (versionSatisfiesRange(requestedVersion, '<=2.12')) {
+ csp.directives.scriptSrc.push("'unsafe-inline'")
+ }
+
+ return contentSecurityPolicy(csp)(req, res, next)
+}
diff --git a/package-lock.json b/package-lock.json
index b33963cf8b..e4cee8054d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2969,36 +2969,6 @@
}
}
},
- "@github-docs/data-directory": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@github-docs/data-directory/-/data-directory-1.2.0.tgz",
- "integrity": "sha512-hp+Ubwl8e77EdnR4OncSUIE7G/cMn9ENOo6ABy8FjqdYCbAWgb/79w7yXVebIV5P3q5r6KAAqPzHj1N5SSrBgw==",
- "requires": {
- "lodash": "^4.17.15",
- "walk-sync": "^2.0.2"
- },
- "dependencies": {
- "matcher-collection": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-2.0.1.tgz",
- "integrity": "sha512-daE62nS2ZQsDg9raM0IlZzLmI2u+7ZapXBwdoeBUKAYERPDDIc0qNqA8E0Rp2D+gspKR7BgIFP52GeujaGXWeQ==",
- "requires": {
- "@types/minimatch": "^3.0.3",
- "minimatch": "^3.0.2"
- }
- },
- "walk-sync": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-2.1.0.tgz",
- "integrity": "sha512-KpH9Xw64LNSx7/UI+3guRZvJWlDxVA4+KKb/4puRoVrG8GkvZRxnF3vhxdjgpoKJGL2TVg1OrtkXIE/VuGPLHQ==",
- "requires": {
- "@types/minimatch": "^3.0.3",
- "ensure-posix-path": "^1.1.0",
- "matcher-collection": "^2.0.0"
- }
- }
- }
- },
"@github-docs/frontmatter": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@github-docs/frontmatter/-/frontmatter-1.3.1.tgz",
@@ -5301,7 +5271,7 @@
},
"agentkeepalive": {
"version": "2.2.0",
- "resolved": "http://registry.npmjs.org/agentkeepalive/-/agentkeepalive-2.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-2.2.0.tgz",
"integrity": "sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8="
},
"aggregate-error": {
@@ -5447,7 +5417,7 @@
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "~1.0.2"
}
@@ -5577,8 +5547,7 @@
"async": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
- "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==",
- "dev": true
+ "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
},
"async-each": {
"version": "1.0.3",
@@ -6825,7 +6794,7 @@
},
"brfs": {
"version": "1.6.1",
- "resolved": "http://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz",
+ "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz",
"integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==",
"requires": {
"quote-stream": "^1.0.1",
@@ -9434,7 +9403,7 @@
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"requires": {
"is-arrayish": "^0.2.1"
}
@@ -12578,7 +12547,7 @@
"dependencies": {
"mkdirp": {
"version": "0.3.0",
- "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
"integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4="
},
"nopt": {
@@ -17704,7 +17673,7 @@
},
"magic-string": {
"version": "0.22.5",
- "resolved": "http://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
"integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==",
"requires": {
"vlq": "^0.2.2"
diff --git a/package.json b/package.json
index bb0f35f588..545de58826 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,6 @@
"@babel/preset-env": "^7.12.7",
"@babel/preset-react": "^7.12.7",
"@babel/runtime": "^7.11.2",
- "@github-docs/data-directory": "^1.2.0",
"@github-docs/frontmatter": "^1.3.1",
"@graphql-inspector/core": "^2.3.0",
"@graphql-tools/load": "^6.2.5",
@@ -27,6 +26,7 @@
"@primer/css": "^15.1.0",
"@primer/octicons": "^11.0.0",
"algoliasearch": "^3.35.1",
+ "async": "^3.2.0",
"babel-loader": "^8.1.0",
"babel-preset-env": "^1.7.0",
"browser-date-formatter": "^3.0.3",
@@ -94,7 +94,6 @@
"devDependencies": {
"@actions/core": "^1.2.6",
"ajv": "^6.11.0",
- "async": "^3.2.0",
"await-sleep": "0.0.1",
"aws-sdk": "^2.610.0",
"babel-eslint": "^10.1.0",
@@ -181,4 +180,4 @@
"pre-push": "npm run prevent-pushes-to-main"
}
}
-}
\ No newline at end of file
+}
diff --git a/script/check-s3-images.js b/script/check-s3-images.js
index 00ef528b7c..6c83a4dc8a 100755
--- a/script/check-s3-images.js
+++ b/script/check-s3-images.js
@@ -12,6 +12,7 @@ const patterns = require('../lib/patterns')
const authenticateToAWS = require('../lib/authenticate-to-aws.js')
const readlineSync = require('readline-sync')
const { execSync } = require('child_process')
+const enterpriseServerVersions = Object.keys(allVersions).filter(v => v.startsWith('enterprise-server@'))
const uploadScript = path.join(process.cwd(), 'script/upload-images-to-s3.js')
// ignore the non-enterprise default version
@@ -51,7 +52,8 @@ async function main () {
page,
site: siteData,
currentVersion: version,
- currentLanguage: 'en'
+ currentLanguage: 'en',
+ enterpriseServerVersions
}
const rendered = await renderContent(page.markdown, context)
diff --git a/server.js b/server.js
index ce00b05169..744d1fa924 100644
--- a/server.js
+++ b/server.js
@@ -18,14 +18,8 @@ if (!module.parent) {
if (status === false) {
// If in a deployed environment, warm the server at the start
if (process.env.NODE_ENV === 'production') {
- // If in a true production environment, wait for the cache to be fully warmed.
- if (process.env.HEROKU_PRODUCTION_APP) {
- await warmServer()
- } else {
- // If not in a true production environment, don't wait for the cache to be fully warmed.
- // This avoids deployment timeouts in environments with slower servers.
- warmServer()
- }
+ // If in a production environment, wait for the cache to be fully warmed.
+ await warmServer()
}
// workaround for https://github.com/expressjs/express/issues/1101
diff --git a/tests/browser/browser.js b/tests/browser/browser.js
index 64fdb083ab..aca6b864a7 100644
--- a/tests/browser/browser.js
+++ b/tests/browser/browser.js
@@ -3,6 +3,8 @@ const sleep = require('await-sleep')
const querystring = require('querystring')
describe('homepage', () => {
+ jest.setTimeout(60 * 1000)
+
test('should be titled "GitHub Documentation"', async () => {
await page.goto('http://localhost:4001')
await expect(page.title()).resolves.toMatch('GitHub Documentation')
@@ -10,6 +12,8 @@ describe('homepage', () => {
})
describe('algolia browser search', () => {
+ jest.setTimeout(60 * 1000)
+
it('works on the homepage', async () => {
await page.goto('http://localhost:4001/en')
await page.click('#search-input-container input[type="search"]')
diff --git a/tests/content/category-pages.js b/tests/content/category-pages.js
index 3eb1214605..218301846e 100644
--- a/tests/content/category-pages.js
+++ b/tests/content/category-pages.js
@@ -42,7 +42,7 @@ describe('category pages', () => {
// Get links included in product index page.
// Each link corresponds to a product subdirectory (category).
// Example: "getting-started-with-github"
- const contents = fs.readFileSync(productIndex, 'utf8')
+ const contents = fs.readFileSync(productIndex, 'utf8') // TODO move to async
const { content } = matter(contents)
const productDir = path.dirname(productIndex)
@@ -50,6 +50,7 @@ describe('category pages', () => {
const categoryLinks = 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...
// Map those to the Markdown file paths that represent that category page index
const categoryPaths = categoryLinks.map(link => getPath(productDir, link, 'index'))
diff --git a/tests/content/crowdin-config.js b/tests/content/crowdin-config.js
index 63301e0993..fd26e74b42 100644
--- a/tests/content/crowdin-config.js
+++ b/tests/content/crowdin-config.js
@@ -4,6 +4,8 @@ const ignoredPagePaths = config.files[0].ignore
const ignoredDataPaths = config.files[2].ignore
describe('crowdin.yml config file', () => {
+ jest.setTimeout(60 * 1000)
+
let pages
beforeAll(async (done) => {
pages = await loadPages()
diff --git a/tests/content/featured-links.js b/tests/content/featured-links.js
index c5b476153b..9829f1abd7 100644
--- a/tests/content/featured-links.js
+++ b/tests/content/featured-links.js
@@ -45,8 +45,9 @@ describe('featuredLinks', () => {
test('featured links respect versioning', async () => {
const $ = await getDOM(`/en/enterprise/${enterpriseServerReleases.latest}/user/packages`)
- const $featuredLinks = $('.featured-links a')
+ const $featuredLinks = $('.all-articles-list a')
expect($featuredLinks.length).toBeGreaterThan(2)
+ expect($featuredLinks.text().includes('Package client guides for GitHub Packages')).toBe(true)
// does not include dotcom-only links
expect($featuredLinks.text().includes('About GitHub Container Registry')).toBe(false)
expect($featuredLinks.text().includes('Getting started with GitHub Container Registry')).toBe(false)
diff --git a/tests/content/glossary.js b/tests/content/glossary.js
index 3e3aea66f9..3f278e5016 100644
--- a/tests/content/glossary.js
+++ b/tests/content/glossary.js
@@ -9,7 +9,10 @@ describe('glossaries', () => {
test('are broken into external, internal, and candidates', async () => {
const keys = Object.keys(glossaries)
- expect(keys).toEqual(['candidates', 'external', 'internal'])
+ expect(keys).toHaveLength(3)
+ expect(keys).toContain('candidates')
+ expect(keys).toContain('external')
+ expect(keys).toContain('internal')
})
test('every entry has a valid term', async () => {
diff --git a/tests/content/remove-liquid-statements.js b/tests/content/remove-liquid-statements.js
index 80fd021669..f94dd5ae5c 100644
--- a/tests/content/remove-liquid-statements.js
+++ b/tests/content/remove-liquid-statements.js
@@ -1,4 +1,4 @@
-const fs = require('fs')
+const fs = require('fs').promises
const path = require('path')
const cheerio = require('cheerio')
const matter = require('gray-matter')
@@ -35,8 +35,8 @@ function processFrontmatter (contents, file) {
}
describe('removing liquid statements only', () => {
- test('removes liquid statements that specify "greater than version to deprecate"', () => {
- let contents = fs.readFileSync(greaterThan, 'utf8')
+ test('removes liquid statements that specify "greater than version to deprecate"', async () => {
+ let contents = await fs.readFile(greaterThan, 'utf8')
contents = removeLiquidStatements(contents, versionToDeprecate, nextOldestVersion)
const $ = cheerio.load(contents)
expect($('.example1').text().trim()).toBe('Alpha')
@@ -57,8 +57,8 @@ Alpha\n\n{% else %}\n\nBravo\n\n{% if currentVersion ver_gt "enterprise-server@2
expect($('.example10').text().trim()).toBe('Alpha')
})
- test('removes liquid statements that specify "and greater than version to deprecate"', () => {
- let contents = fs.readFileSync(andGreaterThan1, 'utf8')
+ test('removes liquid statements that specify "and greater than version to deprecate"', async () => {
+ let contents = await fs.readFile(andGreaterThan1, 'utf8')
contents = removeLiquidStatements(contents, versionToDeprecate, nextOldestVersion)
const $ = cheerio.load(contents)
expect($('.example1').text().trim()).toBe('{% if currentVersion != "free-pro-team@latest" %}\n\nAlpha\n\n{% endif %}')
@@ -71,8 +71,8 @@ Alpha\n\n{% if currentVersion != "free-pro-team@latest" %}\n\nBravo\n\n{% endif
Alpha\n\n{% if currentVersion ver_gt "enterprise-server@2.16" %}\n\nBravo\n\n{% endif %}\n\n{% endif %}`)
})
- test('removes liquid statements that specify "and greater than version to deprecate" (alternate format)', () => {
- let contents = fs.readFileSync(andGreaterThan2, 'utf8')
+ test('removes liquid statements that specify "and greater than version to deprecate" (alternate format)', async () => {
+ let contents = await fs.readFile(andGreaterThan2, 'utf8')
contents = removeLiquidStatements(contents, versionToDeprecate, nextOldestVersion)
const $ = cheerio.load(contents)
expect($('.example1').text().trim()).toBe('{% if currentVersion ver_lt "enterprise-server@2.16" %}\n\nAlpha\n\n{% endif %}')
@@ -85,8 +85,8 @@ Alpha\n\n{% if currentVersion ver_lt "enterprise-server@2.16" %}\n\nBravo\n\n{%
Alpha\n\n{% if currentVersion != "free-pro-team@latest" %}\n\nBravo\n\n{% endif %}\n\n{% endif %}`)
})
- test('removes liquid statements that specify "not equals version to deprecate"', () => {
- let contents = fs.readFileSync(notEquals, 'utf8')
+ test('removes liquid statements that specify "not equals version to deprecate"', async () => {
+ let contents = await fs.readFile(notEquals, 'utf8')
contents = removeLiquidStatements(contents, versionToDeprecate, nextOldestVersion)
const $ = cheerio.load(contents)
expect($('.example1').text().trim()).toBe('Alpha')
@@ -103,8 +103,8 @@ Alpha\n\n{% endif %}`)
})
describe('removing liquid statements and content', () => {
- test('removes interior content and liquid statements that specify "equals version to deprecate"', () => {
- let contents = fs.readFileSync(equals, 'utf8')
+ test('removes interior content and liquid statements that specify "equals version to deprecate"', async () => {
+ let contents = await fs.readFile(equals, 'utf8')
contents = removeLiquidStatements(contents, versionToDeprecate, nextOldestVersion)
const $ = cheerio.load(contents)
expect($('.example1').text().trim()).toBe('')
@@ -117,8 +117,8 @@ Alpha\n\n{% else %}\n\nCharlie\n\n{% endif %}`)
expect($('.example6').text().trim()).toBe('Charlie\n\nBravo')
})
- test('removes interior content and liquid statements that specify "less than next oldest than version to deprecate"', () => {
- let contents = fs.readFileSync(lessThanNextOldest, 'utf8')
+ test('removes interior content and liquid statements that specify "less than next oldest than version to deprecate"', async () => {
+ let contents = await fs.readFile(lessThanNextOldest, 'utf8')
contents = removeLiquidStatements(contents, versionToDeprecate, nextOldestVersion)
const $ = cheerio.load(contents)
expect($('.example1').text().trim()).toBe('Alpha')
@@ -137,8 +137,8 @@ Charlie\n\n{% else %}\n\nDelta\n\n{% endif %}\n\nEcho`)
})
describe('updating frontmatter', () => {
- test('updates frontmatter versions Enterprise if set to greater-than-or-equal-to version to deprecate', () => {
- let contents = fs.readFileSync(frontmatter1, 'utf8')
+ test('updates frontmatter versions Enterprise if set to greater-than-or-equal-to version to deprecate', async () => {
+ let contents = await fs.readFile(frontmatter1, 'utf8')
contents = processFrontmatter(contents, frontmatter1)
const $ = cheerio.load(contents)
// console.log('foo')
@@ -147,8 +147,8 @@ describe('updating frontmatter', () => {
expect($.text().includes('enterprise-server: \'>=2.13\'')).toBe(false)
})
- test('updates frontmatter versions Enterprise if set to greater-than-or-equal-to next oldest version', () => {
- let contents = fs.readFileSync(frontmatter2, 'utf8')
+ test('updates frontmatter versions Enterprise if set to greater-than-or-equal-to next oldest version', async () => {
+ let contents = await fs.readFile(frontmatter2, 'utf8')
contents = processFrontmatter(contents, frontmatter2)
const $ = cheerio.load(contents)
expect($.text().includes('enterprise-server: \'*\'')).toBe(true)
@@ -157,8 +157,8 @@ describe('updating frontmatter', () => {
})
describe('whitespace', () => {
- test('does not add newlines when whitespace control is used', () => {
- let contents = fs.readFileSync(whitespace, 'utf8')
+ test('does not add newlines when whitespace control is used', async () => {
+ let contents = await fs.readFile(whitespace, 'utf8')
contents = removeLiquidStatements(contents, versionToDeprecate, nextOldestVersion)
const $ = cheerio.load(contents)
expect($('.example1').text()).toBe('\n Alpha\n')
@@ -167,8 +167,8 @@ describe('whitespace', () => {
expect($('.example4').text()).toBe('\n Alpha\n')
})
- test('does not add newlines when no newlines are present', () => {
- let contents = fs.readFileSync(whitespace, 'utf8')
+ test('does not add newlines when no newlines are present', async () => {
+ let contents = await fs.readFile(whitespace, 'utf8')
contents = removeLiquidStatements(contents, versionToDeprecate, nextOldestVersion)
const $ = cheerio.load(contents)
expect($('.example5').text()).toBe('\n Alpha\n')
diff --git a/tests/content/site-data-references.js b/tests/content/site-data-references.js
index c6f0a019bd..045e33843b 100644
--- a/tests/content/site-data-references.js
+++ b/tests/content/site-data-references.js
@@ -3,10 +3,12 @@ const loadSiteData = require('../../lib/site-data')
const { loadPages } = require('../../lib/pages')
const getDataReferences = require('../../lib/get-liquid-data-references')
const frontmatter = require('@github-docs/frontmatter')
-const fs = require('fs')
+const fs = require('fs').promises
const path = require('path')
describe('data references', () => {
+ jest.setTimeout(60 * 1000)
+
let data, pages
beforeAll(async (done) => {
@@ -33,34 +35,34 @@ describe('data references', () => {
expect(errors.length, JSON.stringify(errors, null, 2)).toBe(0)
})
- test('every data reference found in metadata of English content files is defined and has a value', () => {
+ test('every data reference found in metadata of English content files is defined and has a value', async () => {
let errors = []
expect(pages.length).toBeGreaterThan(0)
- pages.forEach(page => {
+ await Promise.all(pages.map(async page => {
const metadataFile = path.join('content', page.relativePath)
- const fileContents = fs.readFileSync(path.join(__dirname, '../..', metadataFile))
+ const fileContents = await fs.readFile(path.join(__dirname, '../..', metadataFile))
const { data: metadata } = frontmatter(fileContents, { filepath: page.fullPath })
const metadataRefs = getDataReferences(JSON.stringify(metadata))
metadataRefs.forEach(key => {
const value = get(data.en, key)
if (typeof value !== 'string') errors.push({ key, value, metadataFile })
})
- })
+ }))
errors = uniqWith(errors, isEqual) // remove duplicates
expect(errors.length, JSON.stringify(errors, null, 2)).toBe(0)
})
- test('every data reference found in English reusable files is defined and has a value', () => {
+ test('every data reference found in English reusable files is defined and has a value', async () => {
let errors = []
const allReusables = data.en.site.data.reusables
const reusables = Object.values(allReusables)
expect(reusables.length).toBeGreaterThan(0)
- reusables.forEach(reusablesPerFile => {
+ await Promise.all(reusables.map(async reusablesPerFile => {
let reusableFile = path.join(__dirname, '../../data/reusables/', getFilenameByValue(allReusables, reusablesPerFile))
- reusableFile = getFilepath(reusableFile)
+ reusableFile = await getFilepath(reusableFile)
const reusableRefs = getDataReferences(JSON.stringify(reusablesPerFile))
@@ -68,21 +70,21 @@ describe('data references', () => {
const value = get(data.en, key)
if (typeof value !== 'string') errors.push({ key, value, reusableFile })
})
- })
+ }))
errors = uniqWith(errors, isEqual) // remove duplicates
expect(errors.length, JSON.stringify(errors, null, 2)).toBe(0)
})
- test('every data reference found in English variable files is defined and has a value', () => {
+ test('every data reference found in English variable files is defined and has a value', async () => {
let errors = []
const allVariables = data.en.site.data.variables
const variables = Object.values(allVariables)
expect(variables.length).toBeGreaterThan(0)
- variables.forEach(variablesPerFile => {
+ await Promise.all(variables.map(async variablesPerFile => {
let variableFile = path.join(__dirname, '../../data/variables/', getFilenameByValue(allVariables, variablesPerFile))
- variableFile = getFilepath(variableFile)
+ variableFile = await getFilepath(variableFile)
const variableRefs = getDataReferences(JSON.stringify(variablesPerFile))
@@ -90,7 +92,7 @@ describe('data references', () => {
const value = get(data.en, key)
if (typeof value !== 'string') errors.push({ key, value, variableFile })
})
- })
+ }))
errors = uniqWith(errors, isEqual) // remove duplicates
expect(errors.length, JSON.stringify(errors, null, 2)).toBe(0)
@@ -102,10 +104,13 @@ function getFilenameByValue (object, value) {
}
// if path exists, assume it's a directory; otherwise, assume a YML extension
-function getFilepath (filepath) {
- filepath = fs.existsSync(filepath)
- ? filepath + '/'
- : filepath + '.yml'
+async function getFilepath (filepath) {
+ try {
+ await fs.stat(filepath)
+ filepath = filepath + '/'
+ } catch (_) {
+ filepath = filepath + '.yml'
+ }
// we only need the relative path
return filepath.replace(path.join(__dirname, '../../'), '')
diff --git a/tests/content/site-data.js b/tests/content/site-data.js
index e563df27d3..adbdaa7596 100644
--- a/tests/content/site-data.js
+++ b/tests/content/site-data.js
@@ -45,11 +45,11 @@ describe('siteData module (English)', () => {
// TODO: re-enable once Janky flakyness is resolved
test.skip('backfills missing translated site data with English values', async () => {
const newFile = path.join(__dirname, '../../data/newfile.yml')
- fs.writeFileSync(newFile, 'newvalue: bar')
+ await fs.writeFile(newFile, 'newvalue: bar')
const data = await loadSiteData()
expect(get(data, 'en.site.data.newfile.newvalue')).toEqual('bar')
expect(get(data, 'ja.site.data.newfile.newvalue')).toEqual('bar')
- fs.unlinkSync(newFile)
+ await fs.unlink(newFile)
})
test('all Liquid templating is valid', async () => {
diff --git a/tests/fixtures/rest-redirects.json b/tests/fixtures/rest-redirects.json
index 01470158f7..d6b4e852f2 100644
--- a/tests/fixtures/rest-redirects.json
+++ b/tests/fixtures/rest-redirects.json
@@ -219,7 +219,7 @@
"/en/enterprise/2.20/v3/enterprise-admin/orgs": "/en/enterprise-server@2.20/rest/reference/enterprise-admin#orgs",
"/en/enterprise/2.20/v3/enterprise-admin/pre_receive_environments": "/en/enterprise-server@2.20/rest/reference/enterprise-admin#pre-receive-environments",
"/en/enterprise/2.20/v3/enterprise-admin/pre_receive_hooks": "/en/enterprise-server@2.20/rest/reference/enterprise-admin#pre-receive-hooks",
- "/en/enterprise/2.20/v3/enterprise-admin/repo_pre_receive_hooks": "/en/enterprise-server@2.20/rest/reference/enterprise-admin#repo-pre-receive-hooks",
+ "/en/enterprise/2.20/v3/enterprise-admin/repo_pre_receive_hooks": "/en/enterprise-server@2.20/rest/reference/enterprise-admin#repository-pre-receive-hooks",
"/en/enterprise/2.20/v3/enterprise-admin/search_indexing": "/en/enterprise-server@2.20/rest/reference/enterprise-admin#search-indexing",
"/en/enterprise/2.20/v3/enterprise-admin/users": "/en/enterprise-server@2.20/rest/reference/enterprise-admin#users",
"/en/enterprise/2.20/v3/gists/comments": "/en/enterprise-server@2.20/rest/reference/gists#comments",
@@ -243,7 +243,7 @@
"/en/enterprise/2.20/v3/markdown": "/en/enterprise-server@2.20/rest/reference/markdown",
"/en/enterprise/2.20/v3/meta": "/en/enterprise-server@2.20/rest/reference/meta",
"/en/enterprise/2.20/v3/oauth_authorizations": "/en/enterprise-server@2.20/rest/reference/oauth-authorizations",
- "/en/enterprise/2.20/v3/orgs/hooks": "/en/enterprise-server@2.20/rest/reference/orgs#hooks",
+ "/en/enterprise/2.20/v3/orgs/hooks": "/en/enterprise-server@2.20/rest/reference/orgs#webhooks",
"/en/enterprise/2.20/v3/orgs": "/en/enterprise-server@2.20/rest/reference/orgs",
"/en/enterprise/2.20/v3/orgs/members": "/en/enterprise-server@2.20/rest/reference/orgs#members",
"/en/enterprise/2.20/v3/orgs/outside_collaborators": "/en/enterprise-server@2.20/rest/reference/orgs#outside-collaborators",
@@ -267,13 +267,13 @@
"/en/enterprise/2.20/v3/repos/deployments": "/en/enterprise-server@2.20/rest/reference/repos#deployments",
"/en/enterprise/2.20/v3/repos/downloads": "/en/enterprise-server@2.20/rest/reference/repos#downloads",
"/en/enterprise/2.20/v3/repos/forks": "/en/enterprise-server@2.20/rest/reference/repos#forks",
- "/en/enterprise/2.20/v3/repos/hooks": "/en/enterprise-server@2.20/rest/reference/repos#hooks",
+ "/en/enterprise/2.20/v3/repos/hooks": "/en/enterprise-server@2.20/rest/reference/repos#webhooks",
"/en/enterprise/2.20/v3/repos": "/en/enterprise-server@2.20/rest/reference/repos",
"/en/enterprise/2.20/v3/repos/invitations": "/en/enterprise-server@2.20/rest/reference/repos#invitations",
"/en/enterprise/2.20/v3/repos/keys": "/en/enterprise-server@2.20/rest/reference/repos#keys",
"/en/enterprise/2.20/v3/repos/merging": "/en/enterprise-server@2.20/rest/reference/repos#merging",
"/en/enterprise/2.20/v3/repos/pages": "/en/enterprise-server@2.20/rest/reference/repos#pages",
- "/en/enterprise/2.20/v3/repos/pre_receive_hooks": "/en/enterprise-server@2.20/rest/reference/repos#pre-receive-hooks",
+ "/en/enterprise/2.20/v3/repos/pre_receive_hooks": "/en/enterprise-server@2.20/rest/reference/enterprise-admin#repository-pre-receive-hooks",
"/en/enterprise/2.20/v3/repos/releases": "/en/enterprise-server@2.20/rest/reference/repos#releases",
"/en/enterprise/2.20/v3/repos/statistics": "/en/enterprise-server@2.20/rest/reference/repos#statistics",
"/en/enterprise/2.20/v3/repos/statuses": "/en/enterprise-server@2.20/rest/reference/repos#statuses",
@@ -316,7 +316,7 @@
"/en/enterprise/2.21/v3/enterprise-admin/orgs": "/en/enterprise-server@2.21/rest/reference/enterprise-admin#orgs",
"/en/enterprise/2.21/v3/enterprise-admin/pre_receive_environments": "/en/enterprise-server@2.21/rest/reference/enterprise-admin#pre-receive-environments",
"/en/enterprise/2.21/v3/enterprise-admin/pre_receive_hooks": "/en/enterprise-server@2.21/rest/reference/enterprise-admin#pre-receive-hooks",
- "/en/enterprise/2.21/v3/enterprise-admin/repo_pre_receive_hooks": "/en/enterprise-server@2.21/rest/reference/enterprise-admin#repo-pre-receive-hooks",
+ "/en/enterprise/2.21/v3/enterprise-admin/repo_pre_receive_hooks": "/en/enterprise-server@2.21/rest/reference/enterprise-admin#repository-pre-receive-hooks",
"/en/enterprise/2.21/v3/enterprise-admin/search_indexing": "/en/enterprise-server@2.21/rest/reference/enterprise-admin#search-indexing",
"/en/enterprise/2.21/v3/enterprise-admin/users": "/en/enterprise-server@2.21/rest/reference/enterprise-admin#users",
"/en/enterprise/2.21/v3/gists/comments": "/en/enterprise-server@2.21/rest/reference/gists#comments",
@@ -340,7 +340,7 @@
"/en/enterprise/2.21/v3/markdown": "/en/enterprise-server@2.21/rest/reference/markdown",
"/en/enterprise/2.21/v3/meta": "/en/enterprise-server@2.21/rest/reference/meta",
"/en/enterprise/2.21/v3/oauth_authorizations": "/en/enterprise-server@2.21/rest/reference/oauth-authorizations",
- "/en/enterprise/2.21/v3/orgs/hooks": "/en/enterprise-server@2.21/rest/reference/orgs#hooks",
+ "/en/enterprise/2.21/v3/orgs/hooks": "/en/enterprise-server@2.21/rest/reference/orgs#webhooks",
"/en/enterprise/2.21/v3/orgs": "/en/enterprise-server@2.21/rest/reference/orgs",
"/en/enterprise/2.21/v3/orgs/members": "/en/enterprise-server@2.21/rest/reference/orgs#members",
"/en/enterprise/2.21/v3/orgs/outside_collaborators": "/en/enterprise-server@2.21/rest/reference/orgs#outside-collaborators",
@@ -364,13 +364,13 @@
"/en/enterprise/2.21/v3/repos/deployments": "/en/enterprise-server@2.21/rest/reference/repos#deployments",
"/en/enterprise/2.21/v3/repos/downloads": "/en/enterprise-server@2.21/rest/reference/repos#downloads",
"/en/enterprise/2.21/v3/repos/forks": "/en/enterprise-server@2.21/rest/reference/repos#forks",
- "/en/enterprise/2.21/v3/repos/hooks": "/en/enterprise-server@2.21/rest/reference/repos#hooks",
+ "/en/enterprise/2.21/v3/repos/hooks": "/en/enterprise-server@2.21/rest/reference/repos#webhooks",
"/en/enterprise/2.21/v3/repos": "/en/enterprise-server@2.21/rest/reference/repos",
"/en/enterprise/2.21/v3/repos/invitations": "/en/enterprise-server@2.21/rest/reference/repos#invitations",
"/en/enterprise/2.21/v3/repos/keys": "/en/enterprise-server@2.21/rest/reference/repos#keys",
"/en/enterprise/2.21/v3/repos/merging": "/en/enterprise-server@2.21/rest/reference/repos#merging",
"/en/enterprise/2.21/v3/repos/pages": "/en/enterprise-server@2.21/rest/reference/repos#pages",
- "/en/enterprise/2.21/v3/repos/pre_receive_hooks": "/en/enterprise-server@2.21/rest/reference/repos#pre-receive-hooks",
+ "/en/enterprise/2.21/v3/repos/pre_receive_hooks": "/en/enterprise-server@2.21/rest/reference/enterprise-admin#repository-pre-receive-hooks",
"/en/enterprise/2.21/v3/repos/releases": "/en/enterprise-server@2.21/rest/reference/repos#releases",
"/en/enterprise/2.21/v3/repos/statistics": "/en/enterprise-server@2.21/rest/reference/repos#statistics",
"/en/enterprise/2.21/v3/repos/statuses": "/en/enterprise-server@2.21/rest/reference/repos#statuses",
@@ -453,7 +453,7 @@
"/v3/migrations/users": "/en/free-pro-team@latest/rest/reference/migrations#users",
"/v3/oauth_authorizations": "/en/enterprise-server/rest/reference/oauth-authorizations",
"/v3/orgs/blocking": "/en/free-pro-team@latest/rest/reference/orgs#blocking",
- "/v3/orgs/hooks": "/en/free-pro-team@latest/rest/reference/orgs#hooks",
+ "/v3/orgs/hooks": "/en/free-pro-team@latest/rest/reference/orgs#webhooks",
"/v3/orgs": "/en/free-pro-team@latest/rest/reference/orgs",
"/v3/orgs/members": "/en/free-pro-team@latest/rest/reference/orgs#members",
"/v3/orgs/migrations": "/en/free-pro-team@latest/rest/reference/orgs#migrations",
@@ -479,13 +479,13 @@
"/v3/repos/deployments": "/en/free-pro-team@latest/rest/reference/repos#deployments",
"/v3/repos/downloads": "/en/free-pro-team@latest/rest/reference/repos#downloads",
"/v3/repos/forks": "/en/free-pro-team@latest/rest/reference/repos#forks",
- "/v3/repos/hooks": "/en/free-pro-team@latest/rest/reference/repos#hooks",
+ "/v3/repos/hooks": "/en/free-pro-team@latest/rest/reference/repos#webhooks",
"/v3/repos": "/en/free-pro-team@latest/rest/reference/repos",
"/v3/repos/invitations": "/en/free-pro-team@latest/rest/reference/repos#invitations",
"/v3/repos/keys": "/en/free-pro-team@latest/rest/reference/repos#keys",
"/v3/repos/merging": "/en/free-pro-team@latest/rest/reference/repos#merging",
"/v3/repos/pages": "/en/free-pro-team@latest/rest/reference/repos#pages",
- "/v3/repos/pre_receive_hooks": "/en/free-pro-team@latest/rest/reference/repos#pre-receive-hooks",
+ "/v3/repos/pre_receive_hooks": "/en/enterprise-server/rest/reference/enterprise-admin#repository-pre-receive-hooks",
"/v3/repos/releases": "/en/free-pro-team@latest/rest/reference/repos#releases",
"/v3/repos/statistics": "/en/free-pro-team@latest/rest/reference/repos#statistics",
"/v3/repos/statuses": "/en/free-pro-team@latest/rest/reference/repos#statuses",
diff --git a/tests/graphql/build-changelog-test.js b/tests/graphql/build-changelog-test.js
index 1a55d59bab..4620d4aeee 100644
--- a/tests/graphql/build-changelog-test.js
+++ b/tests/graphql/build-changelog-test.js
@@ -1,6 +1,6 @@
const yaml = require('js-yaml')
const { createChangelogEntry, cleanPreviewTitle, previewAnchor, prependDatedEntry } = require('../../script/graphql/build-changelog')
-const fs = require('fs')
+const fs = require('fs').promises
const MockDate = require('mockdate')
const expectedChangelogEntry = require('../fixtures/changelog-entry')
const expectedUpdatedChangelogFile = require('../fixtures/updated-changelog-file')
@@ -111,18 +111,18 @@ describe('updating the changelog file', () => {
MockDate.reset()
})
- it('modifies the entry object and the file on disk', () => {
+ it('modifies the entry object and the file on disk', async () => {
const testTargetPath = 'tests/graphql/example_changelog.json'
- const previousContents = fs.readFileSync(testTargetPath)
+ const previousContents = await fs.readFile(testTargetPath)
const exampleEntry = { someStuff: true }
const expectedDate = '2020-11-20'
MockDate.set(expectedDate)
prependDatedEntry(exampleEntry, testTargetPath)
- const newContents = fs.readFileSync(testTargetPath, 'utf8')
+ const newContents = await fs.readFile(testTargetPath, 'utf8')
// reset the file:
- fs.writeFileSync(testTargetPath, previousContents)
+ await fs.writeFile(testTargetPath, previousContents)
expect(exampleEntry).toEqual({ someStuff: true, date: expectedDate })
expect(JSON.parse(newContents)).toEqual(expectedUpdatedChangelogFile)
diff --git a/tests/meta/orphan-tests.js b/tests/meta/orphan-tests.js
index 770b10389c..9d561140c6 100644
--- a/tests/meta/orphan-tests.js
+++ b/tests/meta/orphan-tests.js
@@ -1,20 +1,28 @@
-const fs = require('fs')
+const fs = require('fs').promises
const path = require('path')
+const { filter: asyncFilter } = require('async')
describe('check for orphan tests', () => {
- test('all tests are in sub-directories', () => {
+ test('all tests are in sub-directories', async () => {
// A known list of exceptions that can live outside of directories
const EXCEPTIONS = ['README.md']
const pathToTests = path.join(process.cwd(), 'tests')
// Get a list of files/directories in `/tests`
- const testDirectory = fs.readdirSync(pathToTests)
+ const testDirectory = await fs.readdir(pathToTests)
- const filteredList = testDirectory
+ let filteredList = testDirectory
// Filter out our exceptions
.filter(item => !EXCEPTIONS.includes(item))
- // Don't include directories
- .filter(item => !fs.statSync(path.join(pathToTests, item)).isDirectory())
+ // Don't include directories
+ filteredList = await asyncFilter(
+ filteredList,
+ async item => !(
+ await fs.stat(
+ path.join(pathToTests, item)
+ )
+ ).isDirectory()
+ )
expect(filteredList).toHaveLength(0)
})
diff --git a/tests/rendering/rest.js b/tests/rendering/rest.js
index d26e1b74ad..34a9428a04 100644
--- a/tests/rendering/rest.js
+++ b/tests/rendering/rest.js
@@ -1,4 +1,4 @@
-const fs = require('fs')
+const fs = require('fs').promises
const path = require('path')
const { difference, isPlainObject } = require('lodash')
const { getJSON } = require('../helpers/supertest')
@@ -17,7 +17,7 @@ describe('REST references docs', () => {
test('markdown file exists for every operationId prefix in the api.github.com schema', async () => {
const { categories } = require('../../lib/rest')
const referenceDir = path.join(__dirname, '../../content/rest/reference')
- const filenames = fs.readdirSync(referenceDir)
+ const filenames = (await fs.readdir(referenceDir))
.filter(filename => !excludeFromResourceNameCheck.find(excludedFile => filename.endsWith(excludedFile)))
.map(filename => filename.replace('.md', ''))
diff --git a/tests/routing/developer-site-redirects.js b/tests/routing/developer-site-redirects.js
index 7378066dc9..d44d90360b 100644
--- a/tests/routing/developer-site-redirects.js
+++ b/tests/routing/developer-site-redirects.js
@@ -1,7 +1,6 @@
const { eachOfLimit } = require('async')
const enterpriseServerReleases = require('../../lib/enterprise-server-releases')
const { get } = require('../helpers/supertest')
-const { getEnterpriseVersionNumber } = require('../../lib/patterns')
const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version')
const restRedirectFixtures = require('../fixtures/rest-redirects')
const graphqlRedirectFixtures = require('../fixtures/graphql-redirects')
@@ -126,29 +125,6 @@ describe('developer redirects', () => {
)
})
- // TODO temprarily ensure we redirect old links using the new enterprise format
- // for currently supported enterprise releases only
- // EXAMPLE: /en/enterprise-server@2.20/v3/pulls/comments -> /en/enterprise-server@2.20/rest/reference/pulls#comments
- // We can remove test after we update all the old `/v3` links to point to `/rest`
- test('temporary rest reference enterprise redirects', async () => {
- await eachOfLimit(
- restRedirectFixtures,
- MAX_CONCURRENT_REQUESTS,
- async (newPath, oldPath) => {
- const releaseNumber = oldPath.match(getEnterpriseVersionNumber)
- if (!releaseNumber) return
- if (!enterpriseServerReleases.supported.includes(releaseNumber[1])) return
-
- oldPath = oldPath
- .replace(/\/enterprise\/(\d.\d\d)\//, '/enterprise-server@$1/')
- .replace('/user/', '/')
- const res = await get(oldPath)
- expect(res.statusCode, `${oldPath} did not redirect to ${newPath}`).toBe(301)
- expect(res.headers.location).toBe(newPath)
- }
- )
- })
-
// this fixtures file includes /v4 and /enterprise/v4 paths
test('graphql reference redirects', async () => {
await eachOfLimit(
@@ -164,28 +140,5 @@ describe('developer redirects', () => {
}
)
})
-
- // TODO temprarily ensure we redirect old links using the new enterprise format
- // for currently supported enterprise releases only
- // EXAMPLE: /en/enterprise-server@2.20/v4/interface/actor -> /en/enterprise-server@2.20/graphql/reference/interfaces#actor
- // We can remove test after we update all the old `/v4` links to point to `/graphql`
- test('temporary rest reference enterprise redirects', async () => {
- await eachOfLimit(
- graphqlRedirectFixtures,
- MAX_CONCURRENT_REQUESTS,
- async (newPath, oldPath) => {
- const releaseNumber = oldPath.match(getEnterpriseVersionNumber)
- if (!releaseNumber) return
- if (!enterpriseServerReleases.supported.includes(releaseNumber[1])) return
-
- oldPath = oldPath
- .replace(/\/enterprise\/(\d.\d\d)\//, '/enterprise-server@$1/')
- .replace('/user/', '/')
- const res = await get(oldPath)
- expect(res.statusCode, `${oldPath} did not redirect to ${newPath}`).toBe(301)
- expect(res.headers.location).toBe(newPath)
- }
- )
- })
})
})
diff --git a/tests/routing/redirects.js b/tests/routing/redirects.js
index 740cd67d55..b41a0888a8 100644
--- a/tests/routing/redirects.js
+++ b/tests/routing/redirects.js
@@ -17,8 +17,8 @@ describe('redirects', () => {
done()
})
- test('page.redirects is an array', () => {
- const page = new Page({
+ test('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'),
languageCode: 'en'
@@ -26,8 +26,8 @@ describe('redirects', () => {
expect(isPlainObject(page.redirects)).toBe(true)
})
- test('dotcom homepage page.redirects', () => {
- const page = new Page({
+ test('dotcom homepage page.redirects', async () => {
+ const page = await Page.init({
relativePath: 'github/index.md',
basePath: path.join(__dirname, '../../content'),
languageCode: 'en'
@@ -41,7 +41,7 @@ describe('redirects', () => {
})
test('converts single `redirect_from` strings values into arrays', async () => {
- const page = new Page({
+ const page = await Page.init({
relativePath: 'github/collaborating-with-issues-and-pull-requests/about-conversations-on-github.md',
basePath: path.join(__dirname, '../../content'),
languageCode: 'en'
diff --git a/tests/unit/data-directory/filename-to-key.js b/tests/unit/data-directory/filename-to-key.js
new file mode 100644
index 0000000000..052f78cf98
--- /dev/null
+++ b/tests/unit/data-directory/filename-to-key.js
@@ -0,0 +1,15 @@
+const filenameToKey = require('../../../lib/filename-to-key')
+
+describe('filename-to-key', () => {
+ test('converts filenames to object keys', () => {
+ expect(filenameToKey('foo/bar/baz.txt')).toBe('foo.bar.baz')
+ })
+
+ test('ignores leading slash on filenames', () => {
+ expect(filenameToKey('/foo/bar/baz.txt')).toBe('foo.bar.baz')
+ })
+
+ test('supports MS Windows paths', () => {
+ expect(filenameToKey('path\\to\\file.txt')).toBe('path.to.file')
+ })
+})
diff --git a/tests/unit/data-directory/fixtures/README.md b/tests/unit/data-directory/fixtures/README.md
new file mode 100644
index 0000000000..fb62e0bc60
--- /dev/null
+++ b/tests/unit/data-directory/fixtures/README.md
@@ -0,0 +1 @@
+I am a README. I am ignored by default.
\ No newline at end of file
diff --git a/tests/unit/data-directory/fixtures/bar.yaml b/tests/unit/data-directory/fixtures/bar.yaml
new file mode 100644
index 0000000000..d028f54fed
--- /dev/null
+++ b/tests/unit/data-directory/fixtures/bar.yaml
@@ -0,0 +1 @@
+another_markup_language: 'yes'
diff --git a/tests/unit/data-directory/fixtures/foo.json b/tests/unit/data-directory/fixtures/foo.json
new file mode 100644
index 0000000000..8fd3eb5c42
--- /dev/null
+++ b/tests/unit/data-directory/fixtures/foo.json
@@ -0,0 +1 @@
+{"meaningOfLife": 42}
\ No newline at end of file
diff --git a/tests/unit/data-directory/fixtures/nested/baz.md b/tests/unit/data-directory/fixtures/nested/baz.md
new file mode 100644
index 0000000000..c2be33651c
--- /dev/null
+++ b/tests/unit/data-directory/fixtures/nested/baz.md
@@ -0,0 +1 @@
+I am markdown!
\ No newline at end of file
diff --git a/tests/unit/data-directory/index.js b/tests/unit/data-directory/index.js
new file mode 100644
index 0000000000..0a6bdcf793
--- /dev/null
+++ b/tests/unit/data-directory/index.js
@@ -0,0 +1,40 @@
+const path = require('path')
+const dataDirectory = require('../../../lib/data-directory')
+const fixturesDir = path.join(__dirname, 'fixtures')
+
+describe('data-directory', () => {
+ test('works', async () => {
+ const data = await dataDirectory(fixturesDir)
+ const expected = {
+ bar: { another_markup_language: 'yes' },
+ foo: { meaningOfLife: 42 },
+ nested: { baz: 'I am markdown!' }
+ }
+ expect(data).toEqual(expected)
+ })
+
+ test('option: preprocess function', async () => {
+ const preprocess = function (content) {
+ return content.replace('markdown', 'MARKDOWN')
+ }
+ const data = await dataDirectory(fixturesDir, { preprocess })
+ expect(data.nested.baz).toBe('I am MARKDOWN!')
+ })
+
+ test('option: extensions array', async () => {
+ const extensions = ['.yaml', 'markdown']
+ const data = await dataDirectory(fixturesDir, { extensions })
+ expect('bar' in data).toBe(true)
+ expect('foo' in data).toBe(false) // JSON file should be ignored
+ })
+
+ test('option: ignorePatterns', async () => {
+ const ignorePatterns = []
+
+ // README is ignored by default
+ expect('README' in await dataDirectory(fixturesDir)).toBe(false)
+
+ // README can be included by setting empty ignorePatterns array
+ expect('README' in await dataDirectory(fixturesDir, { ignorePatterns })).toBe(true)
+ })
+})
diff --git a/tests/unit/early-access.js b/tests/unit/early-access.js
index f8ad0b3b88..80aa48373f 100644
--- a/tests/unit/early-access.js
+++ b/tests/unit/early-access.js
@@ -1,4 +1,4 @@
-const fs = require('fs')
+const fs = require('fs').promises
const path = require('path')
const { GITHUB_ACTIONS, GITHUB_REPOSITORY } = process.env
@@ -7,17 +7,17 @@ const testViaActionsOnly = runningActionsOnInternalRepo ? test : test.skip
describe('cloning early-access', () => {
testViaActionsOnly('the content directory exists', async () => {
- const eaContentDir = path.join(process.cwd(), 'content/early-access')
- expect(fs.existsSync(eaContentDir)).toBe(true)
+ const eaDir = path.join(process.cwd(), 'content/early-access')
+ expect(await fs.stat(eaDir)).toBeTruthy()
})
testViaActionsOnly('the data directory exists', async () => {
- const eaContentDir = path.join(process.cwd(), 'data/early-access')
- expect(fs.existsSync(eaContentDir)).toBe(true)
+ const eaDir = path.join(process.cwd(), 'data/early-access')
+ expect(await fs.stat(eaDir)).toBeTruthy()
})
testViaActionsOnly('the assets/images directory exists', async () => {
- const eaContentDir = path.join(process.cwd(), 'assets/images/early-access')
- expect(fs.existsSync(eaContentDir)).toBe(true)
+ const eaDir = path.join(process.cwd(), 'assets/images/early-access')
+ expect(await fs.stat(eaDir)).toBeTruthy()
})
})
diff --git a/tests/unit/find-page.js b/tests/unit/find-page.js
index bb422df1b3..5772db5f01 100644
--- a/tests/unit/find-page.js
+++ b/tests/unit/find-page.js
@@ -8,7 +8,7 @@ describe('find page', () => {
jest.setTimeout(1000 * 1000)
test('falls back to the English page if it can\'t find a localized page', async () => {
- const page = new Page({
+ const page = await Page.init({
relativePath: 'page-that-does-not-exist-in-translations-dir.md',
basePath: path.join(__dirname, '../fixtures'),
languageCode: 'en'
@@ -24,7 +24,7 @@ describe('find page', () => {
})
test('follows redirects', async () => {
- const page = new Page({
+ const page = await Page.init({
relativePath: 'page-with-redirects.md',
basePath: path.join(__dirname, '../fixtures'),
languageCode: 'en'
diff --git a/tests/unit/liquid-helpers.js b/tests/unit/liquid-helpers.js
index ee6b8d3ecf..7a66b64a48 100644
--- a/tests/unit/liquid-helpers.js
+++ b/tests/unit/liquid-helpers.js
@@ -1,10 +1,11 @@
const { liquid } = require('../../lib/render-content')
const { loadPageMap } = require('../../lib/pages')
const entities = new (require('html-entities').XmlEntities)()
-const { set } = require('lodash')
const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version')
describe('liquid helper tags', () => {
+ jest.setTimeout(60 * 1000)
+
const context = {}
let pageMap
beforeAll(async (done) => {
@@ -13,11 +14,16 @@ describe('liquid helper tags', () => {
context.currentVersion = nonEnterpriseDefaultVersion
context.pages = pageMap
context.redirects = []
- context.site = {}
+ context.site = {
+ data: {
+ reusables: {
+ example: 'a rose by any other name\nwould smell as sweet'
+ }
+ }
+ }
context.page = {
relativePath: 'desktop/index.md'
}
- set(context.site, 'data.reusables.example', 'a rose by any other name\nwould smell as sweet')
done()
})
@@ -81,8 +87,6 @@ describe('liquid helper tags', () => {
})
describe('indented_data_reference tag', () => {
- set(context.site, 'data.reusables.example', 'a rose by any other name\nwould smell as sweet')
-
test('without any number of spaces specified', async () => {
const template = '{% indented_data_reference site.data.reusables.example %}'
const expected = ` a rose by any other name
@@ -115,4 +119,47 @@ would smell as sweet`
expect(output).toBe(expected)
})
})
+
+ describe('data tag', () => {
+ test(
+ 'handles bracketed array access within for-in loop',
+ async () => {
+ const template = `
+{% for term in site.data.glossaries.external %}
+### {% data glossaries.external[forloop.index0].term %}
+{% data glossaries.external[forloop.index0].description %}
+---
+{% endfor %}`
+
+ const localContext = { ...context }
+ localContext.site = {
+ data: {
+ variables: {
+ fire_emoji: ':fire:'
+ },
+ glossaries: {
+ external: [
+ { term: 'lit', description: 'Awesome things. {% data variables.fire_emoji %}' },
+ { term: 'Zhu Li', description: '_"Zhu Li, do the thing!"_ :point_up:' }
+ ]
+ }
+ }
+ }
+
+ const expected = `
+
+### lit
+Awesome things. :fire:
+---
+
+### Zhu Li
+_"Zhu Li, do the thing!"_ :point_up:
+---
+`
+
+ const output = await liquid.parseAndRender(template, localContext)
+ expect(output).toBe(expected)
+ }
+ )
+ })
})
diff --git a/tests/unit/page.js b/tests/unit/page.js
index 5d1b46a0c3..44f98c94a5 100644
--- a/tests/unit/page.js
+++ b/tests/unit/page.js
@@ -15,14 +15,14 @@ const opts = {
}
describe('Page class', () => {
- test('preserves file path info', () => {
- const page = new Page(opts)
+ 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.fullPath.includes(page.relativePath)).toBe(true)
})
- test('does not error out on translated TOC with no links', () => {
- const page = new Page({
+ test('does not error out on translated TOC with no links', async () => {
+ const page = await Page.init({
relativePath: 'translated-toc-with-no-links-index.md',
basePath: path.join(__dirname, '../fixtures'),
languageCode: 'ja'
@@ -31,30 +31,34 @@ describe('Page class', () => {
})
describe('showMiniToc page property', () => {
- const article = new Page({
- relativePath: 'sample-article.md',
- basePath: path.join(__dirname, '../fixtures'),
- languageCode: 'en'
- })
+ let article, articleWithFM, tocPage, mapTopic
- const articleWithFM = new Page({
- showMiniToc: false,
- relativePath: article.relativePath,
- basePath: article.basePath,
- languageCode: article.languageCode
- })
+ beforeAll(async () => {
+ article = await Page.init({
+ relativePath: 'sample-article.md',
+ basePath: path.join(__dirname, '../fixtures'),
+ languageCode: 'en'
+ })
- const tocPage = new Page({
- relativePath: 'sample-toc-index.md',
- basePath: path.join(__dirname, '../fixtures'),
- languageCode: 'en'
- })
+ articleWithFM = await Page.init({
+ showMiniToc: false,
+ relativePath: article.relativePath,
+ basePath: article.basePath,
+ languageCode: article.languageCode
+ })
- const mapTopic = new Page({
- mapTopic: true,
- relativePath: article.relativePath,
- basePath: article.basePath,
- languageCode: article.languageCode
+ tocPage = await Page.init({
+ relativePath: 'sample-toc-index.md',
+ basePath: path.join(__dirname, '../fixtures'),
+ languageCode: 'en'
+ })
+
+ mapTopic = await Page.init({
+ mapTopic: true,
+ relativePath: article.relativePath,
+ basePath: article.basePath,
+ languageCode: article.languageCode
+ })
})
test('is true by default on articles', () => {
@@ -76,7 +80,7 @@ describe('Page class', () => {
describe('page.render(context)', () => {
test('rewrites links to include the current language prefix and version', async () => {
- const page = new Page(opts)
+ const page = await Page.init(opts)
const context = {
page: { version: nonEnterpriseDefaultVersion },
currentVersion: nonEnterpriseDefaultVersion,
@@ -99,7 +103,7 @@ describe('Page class', () => {
})
test('rewrites links in the intro to include the current language prefix and version', async () => {
- const page = new Page(opts)
+ const page = await Page.init(opts)
page.rawIntro = '[Pull requests](/articles/about-pull-requests)'
const context = {
page: { version: nonEnterpriseDefaultVersion },
@@ -114,7 +118,7 @@ describe('Page class', () => {
})
test('does not rewrite links that include deprecated enterprise release numbers', async () => {
- const page = new Page({
+ const page = await Page.init({
relativePath: 'admin/enterprise-management/migrating-from-github-enterprise-1110x-to-2123.md',
basePath: path.join(__dirname, '../../content'),
languageCode: 'en'
@@ -133,7 +137,7 @@ describe('Page class', () => {
})
test('does not rewrite links to external redirects', async () => {
- const page = new Page(opts)
+ const page = await Page.init(opts)
page.markdown = `${page.markdown}\n\nSee [Capistrano](/capistrano).`
const context = {
page: { version: nonEnterpriseDefaultVersion },
@@ -150,7 +154,7 @@ describe('Page class', () => {
// But they don't have access to our currently supported versions, which we're testing here.
// This test ensures that this works as expected: {% if enterpriseServerVersions contains currentVersion %}
test('renders the expected Enterprise Server versioned content', async () => {
- const page = new Page({
+ const page = await Page.init({
relativePath: 'page-versioned-for-all-enterprise-releases.md',
basePath: path.join(__dirname, '../fixtures'),
languageCode: 'en'
@@ -184,27 +188,27 @@ describe('Page class', () => {
})
})
- test('preserves `languageCode`', () => {
- const page = new Page(opts)
+ test('preserves `languageCode`', async () => {
+ const page = await Page.init(opts)
expect(page.languageCode).toBe('en')
})
- test('parentProductId getter', () => {
- let page = new Page({
+ test('parentProductId getter', async () => {
+ let page = await Page.init({
relativePath: 'github/some-category/some-article.md',
basePath: path.join(__dirname, '../fixtures/products'),
languageCode: 'en'
})
expect(page.parentProductId).toBe('github')
- page = new Page({
+ page = await Page.init({
relativePath: 'actions/some-category/some-article.md',
basePath: path.join(__dirname, '../fixtures/products'),
languageCode: 'en'
})
expect(page.parentProductId).toBe('actions')
- page = new Page({
+ page = await Page.init({
relativePath: 'admin/some-category/some-article.md',
basePath: path.join(__dirname, '../fixtures/products'),
languageCode: 'en'
@@ -213,26 +217,26 @@ describe('Page class', () => {
})
describe('permalinks', () => {
- test('is an array', () => {
- const page = new Page(opts)
+ test('is an array', async () => {
+ const page = await Page.init(opts)
expect(Array.isArray(page.permalinks)).toBe(true)
})
- test('has a key for every supported enterprise version (and no deprecated versions)', () => {
- const page = new Page(opts)
+ test('has a key for every supported enterprise version (and no deprecated versions)', async () => {
+ const page = await Page.init(opts)
const pageVersions = page.permalinks.map(permalink => permalink.pageVersion)
expect(enterpriseServerReleases.supported.every(version => pageVersions.includes(`enterprise-server@${version}`))).toBe(true)
expect(enterpriseServerReleases.deprecated.every(version => !pageVersions.includes(`enterprise-server@${version}`))).toBe(true)
})
- test('sets versioned values', () => {
- const page = new Page(opts)
+ test('sets versioned values', async () => {
+ const page = await Page.init(opts)
expect(page.permalinks.find(permalink => permalink.pageVersion === nonEnterpriseDefaultVersion).href).toBe(`/en/${nonEnterpriseDefaultVersion}/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`)
})
- test('homepage permalinks', () => {
- const page = new Page({
+ test('homepage permalinks', async () => {
+ const page = await Page.init({
relativePath: 'index.md',
basePath: path.join(__dirname, '../../content'),
languageCode: 'en'
@@ -242,8 +246,8 @@ describe('Page class', () => {
expect(page.permalinks.find(permalink => permalink.pageVersion === 'homepage').href).toBe('/en')
})
- test('permalinks for dotcom-only pages', () => {
- const page = new Page({
+ 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',
basePath: path.join(__dirname, '../../content'),
languageCode: 'en'
@@ -252,8 +256,8 @@ describe('Page class', () => {
expect(page.permalinks.length).toBe(1)
})
- test('permalinks for enterprise-only pages', () => {
- const page = new Page({
+ test('permalinks for enterprise-only pages', async () => {
+ const page = await Page.init({
relativePath: 'products/admin/some-category/some-article.md',
basePath: path.join(__dirname, '../fixtures'),
languageCode: 'en'
@@ -264,8 +268,8 @@ describe('Page class', () => {
expect(pageVersions.includes(nonEnterpriseDefaultVersion)).toBe(false)
})
- test('permalinks for non-GitHub.com products without Enterprise versions', () => {
- const page = new Page({
+ test('permalinks for non-GitHub.com products without Enterprise versions', async () => {
+ const page = await Page.init({
relativePath: 'products/actions/some-category/some-article.md',
basePath: path.join(__dirname, '../fixtures'),
languageCode: 'en'
@@ -274,8 +278,8 @@ describe('Page class', () => {
expect(page.permalinks.length).toBe(1)
})
- test('permalinks for non-GitHub.com products with Enterprise versions', () => {
- const page = new Page({
+ 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',
basePath: path.join(__dirname, '../../content'),
languageCode: 'en'
@@ -318,7 +322,7 @@ describe('Page class', () => {
})
test('fixes translated frontmatter that includes verdadero', async () => {
- const page = new Page({
+ const page = await Page.init({
relativePath: 'article-with-mislocalized-frontmatter.md',
basePath: path.join(__dirname, '../fixtures'),
languageCode: 'ja'
@@ -333,7 +337,7 @@ 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 = new Page({
+ const page = await Page.init({
relativePath: 'github/administering-a-repository/comparing-releases.md',
basePath: path.join(__dirname, '../../content'),
languageCode: 'en'
@@ -343,7 +347,7 @@ describe('Page class', () => {
})
test('index page', async () => {
- const page = new Page({
+ const page = await Page.init({
relativePath: 'index.md',
basePath: path.join(__dirname, '../../content'),
languageCode: 'en'
@@ -352,7 +356,7 @@ describe('Page class', () => {
})
test('enterprise admin index page', async () => {
- const page = new Page({
+ const page = await Page.init({
relativePath: 'admin/index.md',
basePath: path.join(__dirname, '../../content'),
languageCode: 'en'
@@ -366,50 +370,50 @@ describe('Page class', () => {
describe('catches errors thrown in Page class', () => {
test('frontmatter parsing error', () => {
- function getPage () {
- return new Page({
+ async function getPage () {
+ return await Page.init({
relativePath: 'page-with-frontmatter-error.md',
basePath: path.join(__dirname, '../fixtures'),
languageCode: 'en'
})
}
- expect(getPage).toThrowError('invalid frontmatter entry')
+ expect(getPage).rejects.toThrowError('invalid frontmatter entry')
})
test('missing versions frontmatter', () => {
- function getPage () {
- return new Page({
+ async function getPage () {
+ return await Page.init({
relativePath: 'page-with-missing-product-versions.md',
basePath: path.join(__dirname, '../fixtures'),
languageCode: 'en'
})
}
- expect(getPage).toThrowError('versions')
+ expect(getPage).rejects.toThrowError('versions')
})
test('English page with a version in frontmatter that its parent product is not available in', () => {
- function getPage () {
- return new Page({
+ async function getPage () {
+ return await Page.init({
relativePath: 'admin/some-category/some-article-with-mismatched-versions-frontmatter.md',
basePath: path.join(__dirname, '../fixtures/products'),
languageCode: 'en'
})
}
- expect(getPage).toThrowError(/`versions` frontmatter.*? product is not available in/)
+ expect(getPage).rejects.toThrowError(/`versions` frontmatter.*? product is not available in/)
})
test('non-English page with a version in frontmatter that its parent product is not available in', () => {
- function getPage () {
- return new Page({
+ async function getPage () {
+ return await Page.init({
relativePath: 'admin/some-category/some-article-with-mismatched-versions-frontmatter.md',
basePath: path.join(__dirname, '../fixtures/products'),
languageCode: 'es'
})
}
- expect(getPage).toThrowError(/`versions` frontmatter.*? product is not available in/)
+ expect(getPage).rejects.toThrowError(/`versions` frontmatter.*? product is not available in/)
})
})
diff --git a/tests/unit/pages.js b/tests/unit/pages.js
index f27b73ff1b..bb405b5e9c 100644
--- a/tests/unit/pages.js
+++ b/tests/unit/pages.js
@@ -10,6 +10,8 @@ const entities = new Entities()
const { chain, difference } = require('lodash')
describe('pages module', () => {
+ jest.setTimeout(60 * 1000)
+
let pages
beforeAll(async (done) => {