import { beforeAll, describe, expect, test } from 'vitest' import { get } from '@/tests/helpers/e2etest' const makeURL = (pathname: string, apiVersion?: string): string => { const params = new URLSearchParams({ pathname }) if (apiVersion) { params.set('apiVersion', apiVersion) } return `/api/article/body?${params}` } describe('REST transformer', () => { beforeAll(() => { if (!process.env.ROOT) { console.warn( 'WARNING: The REST transformer tests require the ROOT environment variable to be set to the fixture root', ) } }) test('REST page renders with markdown structure', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) expect(res.headers['content-type']).toContain('text/markdown') // Check for the main heading expect(res.body).toContain('# GitHub Actions Artifacts') // Check for intro (using fixture's prodname_actions which is 'HubGit Actions') expect(res.body).toContain('Use the REST API to interact with artifacts in HubGit Actions.') // Check for manual content section heading expect(res.body).toContain('## About artifacts in HubGit Actions') }) test('REST operations are formatted correctly', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // Check for operation heading expect(res.body).toContain('## List artifacts for a repository') // Check for HTTP method and endpoint expect(res.body).toContain('GET /repos/{owner}/{repo}/actions/artifacts') // Check for operation description expect(res.body).toContain('Lists all artifacts for a repository.') }) test('Parameters section includes headers', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // Check for parameters heading expect(res.body).toContain('### Parameters') // Check for headers section expect(res.body).toContain('#### Headers') // Check for accept header expect(res.body).toContain('**`accept`** (string)') expect(res.body).toContain('Setting to `application/vnd.github+json` is recommended.') }) test('Path and query parameters are listed', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // Check for path and query parameters section expect(res.body).toContain('#### Path and query parameters') // Check for specific parameters expect(res.body).toContain('**`owner`** (string) (required)') expect(res.body).toContain('The account owner of the repository.') expect(res.body).toContain('**`repo`** (string) (required)') expect(res.body).toContain('**`per_page`** (integer)') expect(res.body).toContain('Default: `30`') }) test('Status codes are formatted correctly', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // Check for status codes section expect(res.body).toContain('### HTTP response status codes') // Check for specific status code expect(res.body).toContain('**200**') expect(res.body).toContain('OK') }) test('Code examples include curl with proper formatting', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // Check for code examples section expect(res.body).toContain('### Code examples') // Check for request/response labels expect(res.body).toContain('**Request:**') expect(res.body).toContain('**Response schema:**') // Check for curl code block expect(res.body).toContain('```curl') expect(res.body).toContain('curl -L \\') expect(res.body).toContain('-X GET \\') expect(res.body).toContain('https://api.github.com/repos/OWNER/REPO/actions/artifacts \\') expect(res.body).toContain('-H "Accept: application/vnd.github.v3+json" \\') expect(res.body).toContain('-H "Authorization: Bearer "') }) test('Code examples include X-GitHub-Api-Version header by default', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // Check for API version header in curl example expect(res.body).toContain('-H "X-GitHub-Api-Version: 2022-11-28"') }) test('Code examples include specified API version', async () => { const res = await get(makeURL('/en/rest/actions/artifacts', '2022-11-28')) expect(res.statusCode).toBe(200) // Check for the specified API version header expect(res.body).toContain('-H "X-GitHub-Api-Version: 2022-11-28"') }) test('Liquid tags are rendered in intro', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // Liquid tags should be rendered, not shown as raw tags (fixture uses 'HubGit Actions') expect(res.body).toContain('HubGit Actions') expect(res.body).not.toContain('{% data variables.product.prodname_actions %}') // Check in both the intro and the manual content section expect(res.body).toMatch(/Use the REST API to interact with artifacts in HubGit Actions/) expect(res.body).toMatch(/About artifacts in HubGit Actions/) }) test('AUTOTITLE links are resolved', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // Check that AUTOTITLE has been resolved to actual link text // The link should have the actual page title, not "AUTOTITLE" expect(res.body).toContain('[Storing workflow data as artifacts]') expect(res.body).toContain('(/en/actions/using-workflows/storing-workflow-data-as-artifacts)') // Make sure the raw AUTOTITLE tag is not present expect(res.body).not.toContain('[AUTOTITLE]') // Verify the link appears in the manual content section expect(res.body).toMatch( /About artifacts in HubGit Actions[\s\S]*Storing workflow data as artifacts/, ) }) test('Markdown links are preserved in descriptions', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // Check that markdown links are preserved expect(res.body).toMatch(/\[.*?\]\(\/en\/.*?\)/) }) test('Response schema is formatted correctly', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // Check for JSON code block with schema label expect(res.body).toContain('**Response schema:**') expect(res.body).toContain('```json') expect(res.body).toContain('Status: 200') // Verify schema structure is present (not an example) expect(res.body).toContain('"type":') expect(res.body).toContain('"properties":') // Check for common schema keywords const schemaMatch = res.body.match(/```json\s+Status: 200\s+([\s\S]*?)```/) expect(schemaMatch).toBeTruthy() if (schemaMatch) { const schemaContent = schemaMatch[1] const schema = JSON.parse(schemaContent) // Verify it's a valid OpenAPI/JSON schema structure expect(schema).toHaveProperty('type') expect(schema.type).toBe('object') expect(schema).toHaveProperty('properties') // Verify it has expected properties for artifacts response expect(schema.properties).toHaveProperty('total_count') expect(schema.properties).toHaveProperty('artifacts') } }) test('Non-REST pages return appropriate error', async () => { const res = await get(makeURL('/en/get-started/start-your-journey/hello-world')) expect(res.statusCode).toBe(200) // Regular article pages should still work, they just won't use the transformer expect(res.body).toContain('## Introduction') }) test('Invalid apiVersion returns 400 error', async () => { // An invalid API version should return a validation error with 400 status const res = await get(makeURL('/en/rest/actions/artifacts', 'invalid-version')) // Returns 400 because the apiVersion is invalid (client error) expect(res.statusCode).toBe(400) const parsed = JSON.parse(res.body) expect(parsed.error).toContain("Invalid apiVersion 'invalid-version'") expect(parsed.error).toContain('Valid API versions are:') expect(parsed.error).toContain('2022-11-28') }) test('Multiple apiVersion query parameters returns 400 error', async () => { // Multiple apiVersion parameters should be rejected const res = await get( '/api/article/body?pathname=/en/rest/actions/artifacts&apiVersion=2022-11-28&apiVersion=2023-01-01', ) expect(res.statusCode).toBe(400) const parsed = JSON.parse(res.body) expect(parsed.error).toBe("Multiple 'apiVersion' keys") }) test('Valid apiVersion passes validation', async () => { // A valid API version should work const res = await get(makeURL('/en/rest/actions/artifacts', '2022-11-28')) expect(res.statusCode).toBe(200) expect(res.body).toContain('-H "X-GitHub-Api-Version: 2022-11-28"') }) test('Missing apiVersion defaults to latest', async () => { // When no apiVersion is provided, it should default to the latest version const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // Should include the default API version header expect(res.body).toContain('-H "X-GitHub-Api-Version: 2022-11-28"') }) test('Multiple operations on a page are all rendered', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // Check for multiple operation headings expect(res.body).toContain('## List artifacts for a repository') expect(res.body).toContain('## Get an artifact') expect(res.body).toContain('## Delete an artifact') }) test('Body parameters are formatted correctly for POST/PUT operations', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // For operations with body parameters, check formatting // (artifacts endpoint is mostly GET/DELETE, but structure should be there) // The transformer handles body parameters when present }) test('Content-type header is included for operations that need it', async () => { const res = await get(makeURL('/en/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // Content-type header appears for operations that require it // The REST transformer adds this based on the operation data }) test('Non-English language paths work correctly', async () => { // Note: This test may fail in dev mode with ENABLED_LANGUAGES=en // but the transformer itself should handle any language path const res = await get(makeURL('/ja/rest/actions/artifacts')) expect(res.statusCode).toBe(200) // The transformer should work regardless of language prefix // because it looks for 'rest' in the path and gets the category/subcategory after it // e.g. /ja/rest/actions/artifacts should work the same as /en/rest/actions/artifacts // Verify the operation content is present (in English, since REST data is not translated) expect(res.body).toContain('## List artifacts for a repository') expect(res.body).toContain('GET /repos/{owner}/{repo}/actions/artifacts') // Check what language is actually being served by examining the response // If Japanese translations are loaded, the title will be in Japanese // Otherwise, it falls back to English const hasJapaneseTitle = res.body.includes('# GitHub Actions アーティファクト') const hasEnglishTitle = res.body.includes('# GitHub Actions Artifacts') // One of them must be present expect(hasJapaneseTitle || hasEnglishTitle).toBe(true) // Verify the appropriate content based on which language was served if (hasJapaneseTitle) { // If Japanese is loaded, expect Japanese intro text expect(res.body).toContain('アーティファクト') } else { // If Japanese is not loaded, expect English fallback expect(res.body).toContain('Use the REST API to interact with artifacts in HubGit Actions') } }) })