Skip to content

Commit dfef542

Browse files
authored
Support autogenerated GitHub App pages in Article API (#58869)
1 parent 9644e43 commit dfef542

16 files changed

+816
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# {{ page.title }}
2+
3+
{% if page.intro %}
4+
{{ page.intro }}
5+
{% endif %}
6+
7+
{% if manualContent %}
8+
{{ manualContent }}
9+
{% endif %}
10+
11+
{% if isListPage %}
12+
{% comment %}
13+
Render endpoints as categorized bullet lists
14+
{% endcomment %}
15+
{% for item in items %}
16+
{% if item.operations.size > 0 %}
17+
## {{ item.category }}
18+
19+
{% for operation in item.operations %}
20+
* [`{{ operation.verb }} {{ operation.requestPath }}`](/{{ currentLanguage }}/{% if currentVersion != 'free-pro-team@latest' %}{{ currentVersion }}/{% endif %}rest/{{ operation.category }}#{{ operation.slug }})
21+
{% endfor %}
22+
23+
{% endif %}
24+
{% endfor %}
25+
{% endif %}
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
import { beforeAll, describe, expect, test } from 'vitest'
2+
3+
import { get } from '@/tests/helpers/e2etest'
4+
5+
const makeURL = (pathname: string, apiVersion?: string): string => {
6+
const params = new URLSearchParams({ pathname })
7+
if (apiVersion) {
8+
params.set('apiVersion', apiVersion)
9+
}
10+
return `/api/article/body?${params}`
11+
}
12+
13+
describe('GitHub Apps transformer', () => {
14+
beforeAll(() => {
15+
if (!process.env.ROOT) {
16+
console.warn(
17+
'WARNING: The GitHub Apps transformer tests require the ROOT environment variable to be set to the fixture root',
18+
)
19+
}
20+
})
21+
22+
describe('Endpoints pages (list format)', () => {
23+
test('server-to-server-rest page renders with markdown structure', async () => {
24+
const res = await get(
25+
makeURL(
26+
'/en/rest/authentication/endpoints-available-for-github-app-installation-access-tokens',
27+
),
28+
)
29+
expect(res.statusCode).toBe(200)
30+
expect(res.headers['content-type']).toContain('text/markdown')
31+
32+
// Check for the main heading
33+
expect(res.body).toContain('# Endpoints available for GitHub App installation access tokens')
34+
35+
// Should have category headings as h2
36+
expect(res.body).toMatch(/^## /m)
37+
38+
// Should not contain HTML comments
39+
expect(res.body).not.toMatch(/<!--.*?-->/)
40+
})
41+
42+
test('user-to-server-rest page renders with markdown structure', async () => {
43+
const res = await get(
44+
makeURL('/en/rest/authentication/endpoints-available-for-github-app-user-access-tokens'),
45+
)
46+
expect(res.statusCode).toBe(200)
47+
expect(res.headers['content-type']).toContain('text/markdown')
48+
49+
// Check for the main heading
50+
expect(res.body).toContain('# Endpoints available for GitHub App user access tokens')
51+
52+
// Should have category headings as h2
53+
expect(res.body).toMatch(/^## /m)
54+
})
55+
56+
test('fine-grained-pat page renders with markdown structure', async () => {
57+
const res = await get(
58+
makeURL(
59+
'/en/rest/authentication/endpoints-available-for-fine-grained-personal-access-tokens',
60+
),
61+
)
62+
expect(res.statusCode).toBe(200)
63+
expect(res.headers['content-type']).toContain('text/markdown')
64+
65+
// Check for the main heading
66+
expect(res.body).toContain('# Endpoints available for fine-grained personal access tokens')
67+
68+
// Should have category headings as h2
69+
expect(res.body).toMatch(/^## /m)
70+
})
71+
72+
test('endpoints are formatted as bullet lists', async () => {
73+
const res = await get(
74+
makeURL(
75+
'/en/rest/authentication/endpoints-available-for-github-app-installation-access-tokens',
76+
),
77+
)
78+
expect(res.statusCode).toBe(200)
79+
80+
// Check for bullet list items with asterisks (per content guidelines)
81+
expect(res.body).toContain('*')
82+
expect(res.body).toMatch(/\* \[`[A-Z]+ \//)
83+
})
84+
85+
test('endpoints have correct HTTP verbs', async () => {
86+
const res = await get(
87+
makeURL(
88+
'/en/rest/authentication/endpoints-available-for-github-app-installation-access-tokens',
89+
),
90+
)
91+
expect(res.statusCode).toBe(200)
92+
93+
// Check for common HTTP verbs
94+
expect(res.body).toMatch(/`GET \//)
95+
// May also have POST, PUT, PATCH, DELETE depending on data
96+
})
97+
98+
test('endpoints link to REST API documentation', async () => {
99+
const res = await get(
100+
makeURL(
101+
'/en/rest/authentication/endpoints-available-for-github-app-installation-access-tokens',
102+
),
103+
)
104+
expect(res.statusCode).toBe(200)
105+
106+
// Links should point to /en/rest/ paths with anchors
107+
expect(res.body).toMatch(/\(\/en\/rest\/[^)]+#[^)]+\)/)
108+
})
109+
})
110+
111+
describe('Permissions pages (table format)', () => {
112+
test('server-to-server-permissions page renders with markdown structure', async () => {
113+
const res = await get(makeURL('/en/rest/authentication/permissions-required-for-github-apps'))
114+
expect(res.statusCode).toBe(200)
115+
expect(res.headers['content-type']).toContain('text/markdown')
116+
117+
// Check for the main heading
118+
expect(res.body).toContain('# Permissions required for GitHub Apps')
119+
120+
// Should have permission group headings as h2
121+
expect(res.body).toMatch(/^## /m)
122+
})
123+
124+
test('fine-grained-pat-permissions page renders with markdown structure', async () => {
125+
const res = await get(
126+
makeURL(
127+
'/en/rest/authentication/permissions-required-for-fine-grained-personal-access-tokens',
128+
),
129+
)
130+
expect(res.statusCode).toBe(200)
131+
expect(res.headers['content-type']).toContain('text/markdown')
132+
133+
// Check for the main heading
134+
expect(res.body).toContain('# Permissions required for fine-grained personal access tokens')
135+
136+
// Should have permission group headings as h2
137+
expect(res.body).toMatch(/^## /m)
138+
})
139+
140+
test('permissions are formatted as markdown tables', async () => {
141+
const res = await get(makeURL('/en/rest/authentication/permissions-required-for-github-apps'))
142+
expect(res.statusCode).toBe(200)
143+
144+
// Check for table structure
145+
expect(res.body).toContain('| Endpoint | Access | Tokens | Additional Permissions |')
146+
expect(res.body).toContain('|----------|--------|--------|------------------------|')
147+
})
148+
149+
test('table includes access levels', async () => {
150+
const res = await get(makeURL('/en/rest/authentication/permissions-required-for-github-apps'))
151+
expect(res.statusCode).toBe(200)
152+
153+
// Check for access levels in table cells
154+
expect(res.body).toMatch(/\| read \|/)
155+
expect(res.body).toMatch(/\| write \|/)
156+
// May also have admin depending on data
157+
})
158+
159+
test('table includes token types (IAT/UAT)', async () => {
160+
const res = await get(makeURL('/en/rest/authentication/permissions-required-for-github-apps'))
161+
expect(res.statusCode).toBe(200)
162+
163+
// Check for IAT and UAT links in Tokens column
164+
expect(res.body).toMatch(/\[IAT\]\(\/en\/apps\/creating-github-apps\//)
165+
expect(res.body).toMatch(/\[UAT\]\(\/en\/apps\/creating-github-apps\//)
166+
})
167+
168+
test('table shows additional permissions with checkmark/cross', async () => {
169+
const res = await get(makeURL('/en/rest/authentication/permissions-required-for-github-apps'))
170+
expect(res.statusCode).toBe(200)
171+
172+
// Check for checkmark (✓) or cross (✗) symbols in Additional Permissions column
173+
expect(res.body).toMatch(/\| [] \|/)
174+
})
175+
176+
test('permissions are grouped by permission name', async () => {
177+
const res = await get(makeURL('/en/rest/authentication/permissions-required-for-github-apps'))
178+
expect(res.statusCode).toBe(200)
179+
180+
// Should have multiple permission group headings
181+
const headings = res.body.match(/^## .* permissions for .*/gm)
182+
expect(headings).toBeTruthy()
183+
if (headings) {
184+
expect(headings.length).toBeGreaterThan(1)
185+
}
186+
})
187+
})
188+
189+
describe('Common functionality', () => {
190+
test('manual content is included', async () => {
191+
const res = await get(makeURL('/en/rest/authentication/permissions-required-for-github-apps'))
192+
expect(res.statusCode).toBe(200)
193+
194+
// Check for manual content that should be in the markdown
195+
expect(res.body).toContain('GitHub Apps are created with a set of permissions')
196+
})
197+
198+
test('intro is rendered', async () => {
199+
const res = await get(makeURL('/en/rest/authentication/permissions-required-for-github-apps'))
200+
expect(res.statusCode).toBe(200)
201+
202+
// The intro should be present (check frontmatter intro)
203+
expect(res.body).toMatch(/For each permission granted/)
204+
})
205+
206+
test('Liquid tags are rendered in content', async () => {
207+
const res = await get(makeURL('/en/rest/authentication/permissions-required-for-github-apps'))
208+
expect(res.statusCode).toBe(200)
209+
210+
// Liquid tags should be rendered (fixture might use simplified names)
211+
expect(res.body).not.toContain('{% data variables.product.prodname_github_app %}')
212+
})
213+
214+
test('AUTOTITLE links are resolved', async () => {
215+
const res = await get(makeURL('/en/rest/authentication/permissions-required-for-github-apps'))
216+
expect(res.statusCode).toBe(200)
217+
218+
// Check that AUTOTITLE has been resolved
219+
expect(res.body).not.toContain('[AUTOTITLE]')
220+
// Should have actual link text
221+
expect(res.body).toMatch(/\[.*?\]\(\/en\/apps\//)
222+
})
223+
224+
test('API version parameter is supported', async () => {
225+
const res = await get(
226+
makeURL('/en/rest/authentication/permissions-required-for-github-apps', '2022-11-28'),
227+
)
228+
expect(res.statusCode).toBe(200)
229+
expect(res.headers['content-type']).toContain('text/markdown')
230+
})
231+
232+
test('Invalid apiVersion returns 400 error', async () => {
233+
const res = await get(
234+
makeURL('/en/rest/authentication/permissions-required-for-github-apps', 'invalid-version'),
235+
)
236+
237+
expect(res.statusCode).toBe(400)
238+
const parsed = JSON.parse(res.body)
239+
expect(parsed.error).toContain("Invalid apiVersion 'invalid-version'")
240+
})
241+
242+
test('Missing apiVersion defaults to latest', async () => {
243+
const res = await get(makeURL('/en/rest/authentication/permissions-required-for-github-apps'))
244+
expect(res.statusCode).toBe(200)
245+
// Should work without explicit apiVersion
246+
})
247+
248+
test('Non-GitHub Apps pages are not transformed', async () => {
249+
const res = await get(makeURL('/en/get-started/start-your-journey/hello-world'))
250+
expect(res.statusCode).toBe(200)
251+
252+
// Regular article pages should still work, they just won't use the GitHub Apps transformer
253+
expect(res.body).toContain('## Introduction')
254+
})
255+
})
256+
})

0 commit comments

Comments
 (0)