UNPKG

@mintlify/scraping

Version:

Scrape documentation frameworks to Mintlify docs

397 lines (349 loc) 10.9 kB
import type { DocumentV3 } from '@mintlify/common'; import { findNavGroup } from '@mintlify/common'; import type { DecoratedNavigationPage } from '@mintlify/models'; import type { OpenAPIV3 } from 'openapi-types'; import { describe, it, expect } from 'vitest'; import { OpenApiExtensions, processOpenApiPath } from '../src/openapi/common.js'; import { simpleDoc } from './fixtures/openapi.js'; type TagWithExtensions = OpenAPIV3.TagObject & { 'x-group'?: string; }; type OperationObject = OpenAPIV3.OperationObject<OpenApiExtensions>; const createOperation = (overrides: Partial<OperationObject>) => { return { responses: { 200: { description: 'OK', }, }, ...overrides, }; }; describe('processOpenApiPath', () => { const schema: DocumentV3 = simpleDoc; const defaultOptions = { writeFiles: false, } as const; it('should add a page and nav entry for a standard operation', () => { const nav: DecoratedNavigationPage[] = []; const decoratedNav: DecoratedNavigationPage[] = []; const writePromises: Promise<void>[] = []; const pagesAcc: Record<string, DecoratedNavigationPage> = {}; const pathItemObject = { get: createOperation({ summary: 'List Pets', tags: ['Pets'] }), }; processOpenApiPath( '/pets', pathItemObject, schema, nav, decoratedNav, writePromises, pagesAcc, defaultOptions, findNavGroup ); expect(nav).toHaveLength(1); expect(nav[0]).toHaveProperty('group', 'Pets'); expect(nav[0]).toHaveProperty('pages'); expect(nav[0]?.pages).toHaveLength(1); expect(decoratedNav).toHaveLength(1); expect(decoratedNav[0]?.pages).toHaveLength(1); expect(Object.keys(pagesAcc)).toHaveLength(1); expect(writePromises).toHaveLength(0); }); it('should skip operations marked with x-excluded', () => { const nav: DecoratedNavigationPage[] = []; const decoratedNav: DecoratedNavigationPage[] = []; const writePromises: Promise<void>[] = []; const pagesAcc: Record<string, DecoratedNavigationPage> = {}; const operation: Partial<OperationObject> = { summary: 'List Pets', tags: ['Pets'], 'x-excluded': true, }; const pathItemObject = { get: createOperation(operation), }; processOpenApiPath( '/pets', pathItemObject, schema, nav, decoratedNav, writePromises, pagesAcc, defaultOptions, findNavGroup ); expect(nav).toHaveLength(0); expect(Object.keys(pagesAcc)).toHaveLength(0); }); it('should use x-mint.metadata.title and description to override defaults', () => { const nav: DecoratedNavigationPage[] = []; const decoratedNav: DecoratedNavigationPage[] = []; const writePromises: Promise<void>[] = []; const pagesAcc: Record<string, DecoratedNavigationPage> = {}; const operation: Partial<OperationObject> = { summary: 'List Pets', tags: ['Pets'], 'x-mint': { metadata: { title: 'Custom Pet Title', description: 'Custom description', }, }, }; const pathItemObject = { get: createOperation(operation), }; processOpenApiPath( '/pets', pathItemObject, schema, nav, decoratedNav, writePromises, pagesAcc, defaultOptions, findNavGroup ); const page = (decoratedNav[0]?.pages as DecoratedNavigationPage[])[0]; expect(page?.title).toBe('Custom Pet Title'); expect(page?.description).toBe('Custom description'); const accPage = pagesAcc[Object.keys(pagesAcc)[0]!] as DecoratedNavigationPage; expect(accPage.title).toBe('Custom Pet Title'); expect(accPage.description).toBe('Custom description'); }); it('should resolve title from OpenAPI summary when openApiFilePath is set', () => { const nav: DecoratedNavigationPage[] = []; const decoratedNav: DecoratedNavigationPage[] = []; const writePromises: Promise<void>[] = []; const pagesAcc: Record<string, DecoratedNavigationPage> = {}; const pingSchema: DocumentV3 = { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, paths: { '/ping': { get: { summary: 'Check API server status', description: 'This endpoint allows you to check the API server status', tags: ['Ping'], responses: { 200: { description: 'OK' } }, }, }, }, }; const pathItemObject = { get: createOperation({ summary: 'Check API server status', description: 'This endpoint allows you to check the API server status', tags: ['Ping'], 'x-mint': { href: '/reference/ping-server', }, }), }; processOpenApiPath( '/ping', pathItemObject, pingSchema, nav, decoratedNav, writePromises, pagesAcc, { ...defaultOptions, openApiFilePath: 'openapi.json' }, findNavGroup ); const page = pagesAcc[Object.keys(pagesAcc)[0]!] as DecoratedNavigationPage; expect(page.title).toBe('Check API server status'); expect(page.sidebarTitle).toBe('Ping server'); expect(page.description).toBe('This endpoint allows you to check the API server status'); }); it('should use x-mint.href to override the generated path', () => { const nav: DecoratedNavigationPage[] = []; const decoratedNav: DecoratedNavigationPage[] = []; const writePromises: Promise<void>[] = []; const pagesAcc: Record<string, DecoratedNavigationPage> = {}; const operation: Partial<OperationObject> = { summary: 'List Pets', tags: ['Pets'], 'x-mint': { href: '/custom/list', }, }; const pathItemObject = { get: createOperation(operation), }; processOpenApiPath( '/pets', pathItemObject, schema, nav, decoratedNav, writePromises, pagesAcc, defaultOptions, findNavGroup ); expect(nav).toHaveLength(1); expect(nav[0]?.pages).toHaveLength(1); expect((nav[0]?.pages as DecoratedNavigationPage[])[0]).toBe('custom/list'); expect(decoratedNav).toHaveLength(1); expect(decoratedNav[0]?.pages).toHaveLength(1); expect((decoratedNav[0]?.pages as DecoratedNavigationPage[])[0]?.href).toBe('/custom/list'); }); it('should use x-group from tag definition for group display name', () => { const nav: DecoratedNavigationPage[] = []; const decoratedNav: DecoratedNavigationPage[] = []; const writePromises: Promise<void>[] = []; const pagesAcc: Record<string, DecoratedNavigationPage> = {}; const schemaWithXGroup: DocumentV3 = { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, tags: [ { name: 'CRMActions', description: 'CRM related actions', 'x-group': 'CRM Actions', } as TagWithExtensions, ], paths: { '/actions': { get: { summary: 'Get CRM Actions', description: 'Retrieve CRM actions', tags: ['CRMActions'], responses: { 200: { description: 'OK' } }, }, }, }, }; const pathItemObject = { get: createOperation({ summary: 'Get CRM Actions', description: 'Retrieve CRM actions', tags: ['CRMActions'], }), }; processOpenApiPath( '/actions', pathItemObject, schemaWithXGroup, nav, decoratedNav, writePromises, pagesAcc, defaultOptions, findNavGroup ); expect(nav).toHaveLength(1); expect(nav[0]).toHaveProperty('group', 'CRM Actions'); }); it('should fall back to tag name when x-group is not defined', () => { const nav: DecoratedNavigationPage[] = []; const decoratedNav: DecoratedNavigationPage[] = []; const writePromises: Promise<void>[] = []; const pagesAcc: Record<string, DecoratedNavigationPage> = {}; const schemaWithoutXGroup: DocumentV3 = { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, tags: [ { name: 'CustomTag', description: 'A custom tag', // No x-group defined }, ], paths: { '/custom': { get: { summary: 'Get Custom', description: 'Get custom resource', tags: ['CustomTag'], responses: { 200: { description: 'OK' } }, }, }, }, }; const pathItemObject = { get: createOperation({ summary: 'Get Custom', description: 'Get custom resource', tags: ['CustomTag'], }), }; processOpenApiPath( '/custom', pathItemObject, schemaWithoutXGroup, nav, decoratedNav, writePromises, pagesAcc, defaultOptions, findNavGroup ); expect(nav).toHaveLength(1); expect(nav[0]).toHaveProperty('group', 'CustomTag'); }); it('should use x-group display name for multiple operations in the same group', () => { const nav: DecoratedNavigationPage[] = []; const decoratedNav: DecoratedNavigationPage[] = []; const writePromises: Promise<void>[] = []; const pagesAcc: Record<string, DecoratedNavigationPage> = {}; const schemaWithMultipleOps: DocumentV3 = { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, tags: [ { name: 'EmailVerification', description: 'Email verification endpoints', 'x-group': 'Email Verification', } as TagWithExtensions, ], paths: { '/verify-email': { get: { summary: 'Verify Email', description: 'Verify an email', tags: ['EmailVerification'], responses: { 200: { description: 'OK' } }, }, post: { summary: 'Send Verification', description: 'Send verification code', tags: ['EmailVerification'], responses: { 200: { description: 'OK' } }, }, }, }, }; const pathItemObject = { get: createOperation({ summary: 'Verify Email', description: 'Verify an email', tags: ['EmailVerification'], }), post: createOperation({ summary: 'Send Verification', description: 'Send verification code', tags: ['EmailVerification'], }), }; processOpenApiPath( '/verify-email', pathItemObject, schemaWithMultipleOps, nav, decoratedNav, writePromises, pagesAcc, defaultOptions, findNavGroup ); expect(nav).toHaveLength(1); expect(nav[0]).toHaveProperty('group', 'Email Verification'); expect((nav[0]?.pages as string[]).length).toBe(2); }); });