Refactor OpenAPI plugin structure by consolidating parser and printer exports, enhancing type safety, and improving error handling in parsing functions. Update tests to reflect new structure and ensure consistent handling of OpenAPI file detection.

This commit is contained in:
Luke Hagar
2025-09-26 21:03:08 +00:00
parent 0901e96671
commit d34e24583b
12 changed files with 796 additions and 754 deletions

View File

@@ -9,6 +9,7 @@
import {
RootKeys,
InfoKeys,
ComponentsKeys,
OperationKeys,
ParameterKeys,
SchemaKeys,
@@ -18,13 +19,21 @@ import {
TagKeys,
ExternalDocsKeys,
WebhookKeys,
OAuthFlowKeys,
PathItemKeys,
RequestBodyKeys,
MediaTypeKeys,
EncodingKeys,
HeaderKeys,
LinkKeys,
ExampleKeys,
DiscriminatorKeys,
XMLKeys,
ContactKeys,
LicenseKeys,
ComponentsKeys,
OAuthFlowKeys,
ServerVariableKeys,
} from '../keys.js';
import { getVendorExtensions, type VendorModule } from './vendor-loader.js';
import { type VendorModule } from './vendor-loader.js';
/**
* Type-safe context-specific extension functions
@@ -528,6 +537,307 @@ export interface VendorExtensions {
* ```
*/
'securityDefinitions'?: ContextExtensionFunction<'securityDefinitions'>;
/** Components section extensions
*
* Available keys:
* - `securitySchemes`
* - `pathItems`
* - `parameters`
* - `headers`
* - `requestBodies`
* - `responses`
* - `callbacks`
* - `links`
* - `schemas`
* - `examples`
*
* @example
* ```typescript
* 'components': (before, after) => {
* return {
* 'x-my-extension': before('schemas'),
* 'x-my-config': after('examples')
* };
* }
* ```
*/
'components'?: ContextExtensionFunction<'components'>;
/** Path item extensions
*
* Available keys:
* - `$ref`
* - `summary`
* - `description`
* - `servers`
* - `parameters`
* - `get`
* - `put`
* - `post`
* - `patch`
* - `delete`
* - `options`
* - `head`
* - `trace`
*
* @example
* ```typescript
* 'pathItem': (before, after) => {
* return {
* 'x-my-extension': before('summary'),
* 'x-my-config': after('parameters')
* };
* }
* ```
*/
'pathItem'?: ContextExtensionFunction<'pathItem'>;
/** Request body extensions
*
* Available keys:
* - `description`
* - `required`
* - `content`
*
* @example
* ```typescript
* 'requestBody': (before, after) => {
* return {
* 'x-my-extension': before('description'),
* 'x-my-config': after('content')
* };
* }
* ```
*/
'requestBody'?: ContextExtensionFunction<'requestBody'>;
/** Media type extensions
*
* Available keys:
* - `schema`
* - `example`
* - `examples`
* - `encoding`
*
* @example
* ```typescript
* 'mediaType': (before, after) => {
* return {
* 'x-my-extension': before('schema'),
* 'x-my-config': after('example')
* };
* }
* ```
*/
'mediaType'?: ContextExtensionFunction<'mediaType'>;
/** Encoding extensions
*
* Available keys:
* - `contentType`
* - `style`
* - `explode`
* - `allowReserved`
* - `headers`
*
* @example
* ```typescript
* 'encoding': (before, after) => {
* return {
* 'x-my-extension': before('contentType'),
* 'x-my-config': after('headers')
* };
* }
* ```
*/
'encoding'?: ContextExtensionFunction<'encoding'>;
/** Header extensions
*
* Available keys:
* - `description`
* - `required`
* - `deprecated`
* - `schema`
* - `content`
* - `type`
* - `format`
* - `style`
* - `explode`
* - `enum`
* - `default`
* - `example`
* - `examples`
* - `items`
* - `collectionFormat`
* - `maxItems`
* - `minItems`
* - `uniqueItems`
* - `minimum`
* - `multipleOf`
* - `exclusiveMinimum`
* - `maximum`
* - `exclusiveMaximum`
* - `pattern`
* - `minLength`
* - `maxLength`
*
* @example
* ```typescript
* 'header': (before, after) => {
* return {
* 'x-my-extension': before('description'),
* 'x-my-config': after('schema')
* };
* }
* ```
*/
'header'?: ContextExtensionFunction<'header'>;
/** Link extensions
*
* Available keys:
* - `operationId`
* - `description`
* - `server`
* - `operationRef`
* - `parameters`
* - `requestBody`
*
* @example
* ```typescript
* 'link': (before, after) => {
* return {
* 'x-my-extension': before('operationId'),
* 'x-my-config': after('parameters')
* };
* }
* ```
*/
'link'?: ContextExtensionFunction<'link'>;
/** Example extensions
*
* Available keys:
* - `summary`
* - `description`
* - `value`
* - `externalValue`
*
* @example
* ```typescript
* 'example': (before, after) => {
* return {
* 'x-my-extension': before('summary'),
* 'x-my-config': after('value')
* };
* }
* ```
*/
'example'?: ContextExtensionFunction<'example'>;
/** Discriminator extensions
*
* Available keys:
* - `propertyName`
* - `mapping`
*
* @example
* ```typescript
* 'discriminator': (before, after) => {
* return {
* 'x-my-extension': before('propertyName'),
* 'x-my-config': after('mapping')
* };
* }
* ```
*/
'discriminator'?: ContextExtensionFunction<'discriminator'>;
/** XML extensions
*
* Available keys:
* - `name`
* - `namespace`
* - `prefix`
* - `attribute`
* - `wrapped`
*
* @example
* ```typescript
* 'xml': (before, after) => {
* return {
* 'x-my-extension': before('name'),
* 'x-my-config': after('namespace')
* };
* }
* ```
*/
'xml'?: ContextExtensionFunction<'xml'>;
/** Contact extensions
*
* Available keys:
* - `name`
* - `email`
* - `url`
*
* @example
* ```typescript
* 'contact': (before, after) => {
* return {
* 'x-my-extension': before('name'),
* 'x-my-config': after('email')
* };
* }
* ```
*/
'contact'?: ContextExtensionFunction<'contact'>;
/** License extensions
*
* Available keys:
* - `name`
* - `identifier`
* - `url`
*
* @example
* ```typescript
* 'license': (before, after) => {
* return {
* 'x-my-extension': before('name'),
* 'x-my-config': after('identifier')
* };
* }
* ```
*/
'license'?: ContextExtensionFunction<'license'>;
/** OAuth flow extensions
*
* Available keys:
* - `authorizationUrl`
* - `tokenUrl`
* - `refreshUrl`
* - `scopes`
*
* @example
* ```typescript
* 'oauthFlow': (before, after) => {
* return {
* 'x-my-extension': before('authorizationUrl'),
* 'x-my-config': after('tokenUrl')
* };
* }
* ```
*/
'oauthFlow'?: ContextExtensionFunction<'oauthFlow'>;
/** Server variable extensions
*
* Available keys:
* - `description`
* - `default`
* - `enum`
*
* @example
* ```typescript
* 'serverVariable': (before, after) => {
* return {
* 'x-my-extension': before('description'),
* 'x-my-config': after('default')
* };
* }
* ```
*/
'serverVariable'?: ContextExtensionFunction<'serverVariable'>;
}
/**
@@ -609,6 +919,7 @@ export function createPositionHelpers<T extends keyof typeof KeyMap>(context: T)
export const KeyMap = {
'top-level': RootKeys,
'info': InfoKeys,
'components': ComponentsKeys,
'operation': OperationKeys,
'parameter': ParameterKeys,
'schema': SchemaKeys,
@@ -618,6 +929,19 @@ export const KeyMap = {
'tag': TagKeys,
'externalDocs': ExternalDocsKeys,
'webhook': WebhookKeys,
'pathItem': PathItemKeys,
'requestBody': RequestBodyKeys,
'mediaType': MediaTypeKeys,
'encoding': EncodingKeys,
'header': HeaderKeys,
'link': LinkKeys,
'example': ExampleKeys,
'discriminator': DiscriminatorKeys,
'xml': XMLKeys,
'contact': ContactKeys,
'license': LicenseKeys,
'oauthFlow': OAuthFlowKeys,
'serverVariable': ServerVariableKeys,
'definitions': SchemaKeys,
'securityDefinitions': SecuritySchemeKeys,
}

View File

@@ -4,7 +4,7 @@
* Loads vendor extensions using static imports for ES module compatibility.
*/
import { before, after, KeyMap, VendorExtensions } from './index.js';
import { before, after, type KeyMap, type VendorExtensions } from './index.js';
// Import vendor extensions statically
import { speakeasy } from './vendor/speakeasy.js';

View File

@@ -1,48 +1,45 @@
import { SupportLanguage, Parser, Printer } from 'prettier';
import type { Plugin } from 'prettier';
import * as yaml from 'js-yaml';
import type{ AstPath, Doc, Parser, ParserOptions, Printer, SupportLanguage } from 'prettier';
import { getVendorExtensions } from './extensions/vendor-loader.js';
export type PrintFn = (path: AstPath) => Doc;
import {
RootKeys,
InfoKeys,
ContactKeys,
LicenseKeys,
ComponentsKeys,
OperationKeys,
ParameterKeys,
SchemaKeys,
ResponseKeys,
SecuritySchemeKeys,
OAuthFlowKeys,
ServerKeys,
ServerVariableKeys,
TagKeys,
ExternalDocsKeys,
WebhookKeys,
PathItemKeys,
RequestBodyKeys,
MediaTypeKeys,
EncodingKeys,
HeaderKeys,
LinkKeys,
ExampleKeys,
DiscriminatorKeys,
XMLKeys,
ComponentsKeys,
ContactKeys,
DiscriminatorKeys,
EncodingKeys,
ExampleKeys,
ExternalDocsKeys,
HeaderKeys,
InfoKeys,
LicenseKeys,
LinkKeys,
MediaTypeKeys,
OAuthFlowKeys,
OperationKeys,
ParameterKeys,
PathItemKeys,
RequestBodyKeys,
ResponseKeys,
RootKeys,
SchemaKeys,
SecuritySchemeKeys,
ServerKeys,
ServerVariableKeys,
TagKeys,
WebhookKeys,
XMLKeys,
} from './keys.js';
// Type definitions for better type safety
interface OpenAPINode {
type: 'openapi';
isOpenAPI: boolean;
content: any;
originalText: string;
format: 'json' | 'yaml';
}
interface PrettierPath {
getValue(): OpenAPINode;
}
interface OpenAPIPluginOptions {
tabWidth?: number;
printWidth?: number;
@@ -59,18 +56,22 @@ const vendorExtensions = getVendorExtensions();
* Unified parser that can handle both JSON and YAML OpenAPI files
*/
function parseOpenAPIFile(text: string, options?: any): OpenAPINode {
// Try to detect the format based on file extension or content
const filePath = options?.filepath || '';
const isYamlFile = filePath.endsWith('.yaml') || filePath.endsWith('.yml');
const isJsonFile = filePath.endsWith('.json');
// If we can't determine from extension, try to detect from content
let format: 'json' | 'yaml' = 'json'; // default to JSON
if (isYamlFile) {
format = 'yaml';
} else if (isJsonFile) {
format = 'json';
} else {
console.debug('parseOpenAPIFile', text, options);
let format: 'json' | 'yaml' | undefined;
if (options?.filepath) {
switch (true) {
case options?.filepath.endsWith('.yaml') || options?.filepath.endsWith('.yml'):
format = 'yaml';
break;
case options?.filepath.endsWith('.json'):
format = 'json';
break;
}
}
if (!format) {
// Try to detect format from content
const trimmedText = text.trim();
if (trimmedText.startsWith('{') || trimmedText.startsWith('[')) {
@@ -79,35 +80,61 @@ function parseOpenAPIFile(text: string, options?: any): OpenAPINode {
format = 'yaml';
}
}
try {
let parsed: any;
if (format === 'json') {
parsed = JSON.parse(text);
} else {
let parsed: any;
switch (format) {
case 'yaml':
try {
parsed = yaml.load(text, {
schema: yaml.DEFAULT_SCHEMA,
onWarning: (warning) => {
// Handle YAML warnings if needed
console.warn('YAML parsing warning:', warning);
}
});
}
// Check if this is an OpenAPI file
if (!isOpenAPIFile(parsed, filePath)) {
throw new Error('Not an OpenAPI file');
}
}
});
} catch (error) {
throw new Error(`Failed to parse OpenAPI YAML: ${error}`);
}
break;
case 'json':
try {
parsed = JSON.parse(text);
} catch (error) {
throw new Error(`Failed to parse OpenAPI JSON: ${error}`);
}
break;
}
let isOpenAPI: boolean;
try {
isOpenAPI = isOpenAPIFile(parsed, options?.filepath);
} catch (error) {
return {
type: 'openapi',
isOpenAPI: false,
content: parsed,
originalText: text,
format: format,
};
} catch (error) {
throw new Error(`Failed to parse OpenAPI ${format.toUpperCase()}: ${error}`);
}
}
switch (isOpenAPI) {
case true:
return {
isOpenAPI: true,
content: parsed,
originalText: text,
format: format,
}
case false:
default:
return {
isOpenAPI: false,
content: parsed,
originalText: text,
format: format,
}
}
}
@@ -132,17 +159,17 @@ function isOpenAPIFile(content: any, filePath?: string): boolean {
// Only accept files in OpenAPI-related directories
if (filePath) {
const path = filePath.toLowerCase();
// Check for component directory patterns
if (path.includes('/components/') ||
path.includes('/schemas/') ||
path.includes('/parameters/') ||
path.includes('/responses/') ||
path.includes('/requestbodies/') ||
path.includes('/headers/') ||
path.includes('/examples/') ||
path.includes('/securityschemes/') ||
path.includes('/links/') ||
if (path.includes('/components/') ||
path.includes('/schemas/') ||
path.includes('/parameters/') ||
path.includes('/responses/') ||
path.includes('/requestbodies/') ||
path.includes('/headers/') ||
path.includes('/examples/') ||
path.includes('/securityschemes/') ||
path.includes('/links/') ||
path.includes('/callbacks/') ||
path.includes('/webhooks/') ||
path.includes('/paths/')) {
@@ -204,11 +231,11 @@ function isOpenAPIFile(content: any, filePath?: string): boolean {
// Additional strict check: reject objects that look like generic data
// If an object only has simple properties like name, age, etc. without any OpenAPI structure, reject it
const keys = Object.keys(content);
const hasOnlyGenericProperties = keys.every(key =>
const hasOnlyGenericProperties = keys.every(key =>
!key.startsWith('x-') && // Not a custom extension
!['openapi', 'swagger', 'info', 'paths', 'components', 'definitions', 'parameters', 'responses', 'securityDefinitions', 'tags', 'servers', 'webhooks'].includes(key)
);
if (hasOnlyGenericProperties) {
return false;
}
@@ -229,36 +256,40 @@ function isPathObject(obj: any): boolean {
return Object.keys(obj).some(key => httpMethods.includes(key.toLowerCase()));
}
const plugin: Plugin = {
languages: [
{
name: 'openapi',
extensions: [
// Accepting all JSON and YAML files so that component files used by $ref work
'.json', '.yaml', '.yml'
],
parsers: ['openapi-parser'],
export const languages: Partial<SupportLanguage>[] = [
{
name: 'openapi',
extensions: [
// Accepting all JSON and YAML files so that component files used by $ref work
'.json', '.yaml', '.yml'
],
parsers: ['openapi-parser'],
},
];
export const parsers: Record<string, Parser> = {
'openapi-parser': {
parse: (text: string, options?: any): OpenAPINode => {
return parseOpenAPIFile(text, options);
},
],
parsers: {
'openapi-parser': {
parse: (text: string, options?: any): OpenAPINode => {
return parseOpenAPIFile(text, options);
},
astFormat: 'openapi-ast',
locStart: (node: OpenAPINode) => 0,
locEnd: (node: OpenAPINode) => node.originalText?.length || 0,
astFormat: 'openapi-ast',
locStart: (node: OpenAPINode) => 0,
locEnd: (node: OpenAPINode) => node.originalText?.length || 0,
},
}
export const printers: Record<string, Printer> = {
'openapi-ast': {
print: (path: AstPath, options: ParserOptions, print: PrintFn): string => {
const node = path.getNode();
if (!node.isOpenAPI || node.isOpenAPI === false) {
// Return original text unchanged
return options.originalText;
}
return formatOpenAPI(node.content, node.format, options);
},
},
printers: {
'openapi-ast': {
print: (path: PrettierPath, options?: any, print?: any, ...rest: any[]): string => {
const node = path.getValue();
return formatOpenAPI(node.content, node.format, options);
},
},
},
};
}
/**
* Unified formatter that outputs in the detected format
@@ -267,29 +298,21 @@ function formatOpenAPI(content: any, format: 'json' | 'yaml', options?: OpenAPIP
// Sort keys for better organization
const sortedContent = sortOpenAPIKeys(content);
if (format === 'json') {
// Format with proper indentation
return JSON.stringify(sortedContent, null, options?.tabWidth || 2);
} else {
// Format YAML with proper indentation and line breaks
return yaml.dump(sortedContent, {
indent: options?.tabWidth || 2,
lineWidth: options?.printWidth || 80,
noRefs: true,
quotingType: '"',
forceQuotes: false,
});
switch (format) {
case 'json':
return JSON.stringify(sortedContent, null, options?.tabWidth || 2);
case 'yaml':
// Format YAML with proper indentation and line breaks
return yaml.dump(sortedContent, {
indent: options?.tabWidth || 2,
lineWidth: options?.printWidth || 80,
noRefs: true,
quotingType: '"',
forceQuotes: false,
});
}
}
function formatOpenAPIJSON(content: any, options?: OpenAPIPluginOptions): string {
return formatOpenAPI(content, 'json', options);
}
function formatOpenAPIYAML(content: any, options?: OpenAPIPluginOptions): string {
return formatOpenAPI(content, 'yaml', options);
}
function sortOpenAPIKeys(obj: any): any {
if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
return obj;
@@ -316,7 +339,7 @@ function sortOpenAPIKeysEnhanced(obj: any, path: string = ''): any {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
// Handle arrays by recursively sorting each element
if (Array.isArray(obj)) {
return obj.map((item, index) => sortOpenAPIKeysEnhanced(item, `${path}[${index}]`));
@@ -389,11 +412,11 @@ function isSchemaObject(obj: any): boolean {
if (!obj || typeof obj !== 'object') {
return false;
}
// Check for JSON Schema keywords - be very strict
const hasSchemaKeywords = '$ref' in obj || 'allOf' in obj || 'oneOf' in obj || 'anyOf' in obj || 'not' in obj;
const hasValidType = 'type' in obj && obj.type && ['object', 'array', 'string', 'number', 'integer', 'boolean', 'null'].includes(obj.type);
// Only return true if we have clear schema indicators
// Must have either schema keywords OR valid type with schema properties
// Also require additional schema-specific properties to be more strict
@@ -414,8 +437,8 @@ function isServerObject(obj: any): boolean {
}
function isTagObject(obj: any): boolean {
return obj && typeof obj === 'object' && 'name' in obj && typeof obj.name === 'string' &&
(Object.keys(obj).length === 1 || // Only name
return obj && typeof obj === 'object' && 'name' in obj && typeof obj.name === 'string' &&
(Object.keys(obj).length === 1 || // Only name
'description' in obj || // name + description
'externalDocs' in obj); // name + externalDocs
}
@@ -546,7 +569,7 @@ function getContextKey(path: string, obj: any): string {
if (path === 'webhooks') return 'webhook';
if (path === 'definitions') return 'definitions';
if (path === 'securityDefinitions') return 'securityDefinitions';
// Check if this is a path operation (e.g., "paths./users.get")
if (path.includes('.') && path.split('.').length >= 3) {
const pathParts = path.split('.');
@@ -648,6 +671,4 @@ function getStandardKeysForContext(contextKey: string): readonly string[] {
case 'securityDefinitions': return SecuritySchemeKeys; // Security definitions use security scheme keys
default: return RootKeys;
}
}
export default plugin;
}

View File

@@ -1,189 +0,0 @@
import { describe, expect, it } from 'bun:test';
import * as fs from 'fs';
import * as path from 'path';
describe('Build Tests', () => {
describe('Build artifacts', () => {
it('should create dist directory', () => {
const distPath = path.join(process.cwd(), 'dist');
expect(fs.existsSync(distPath)).toBe(true);
});
it('should create main index.js file', () => {
const indexPath = path.join(process.cwd(), 'dist', 'index.js');
expect(fs.existsSync(indexPath)).toBe(true);
});
it('should create TypeScript declaration files', () => {
const dtsPath = path.join(process.cwd(), 'dist', 'index.d.ts');
expect(fs.existsSync(dtsPath)).toBe(true);
});
it('should create source map files', () => {
const mapPath = path.join(process.cwd(), 'dist', 'index.js.map');
expect(fs.existsSync(mapPath)).toBe(true);
});
it('should have valid JavaScript in dist/index.js', () => {
const indexPath = path.join(process.cwd(), 'dist', 'index.js');
const content = fs.readFileSync(indexPath, 'utf-8');
// Should not contain TypeScript syntax
expect(content).not.toContain(': string');
expect(content).not.toContain(': number');
expect(content).not.toContain('interface ');
// Note: 'type ' might appear in comments or strings, so we check for type annotations instead
expect(content).not.toMatch(/\btype\s+[A-Z]/);
// Should be valid JavaScript (but may contain ES module syntax)
// We can't use new Function() with ES modules, so we just check it's not empty
expect(content.length).toBeGreaterThan(0);
});
it('should export the plugin as default export', () => {
const indexPath = path.join(process.cwd(), 'dist', 'index.js');
const content = fs.readFileSync(indexPath, 'utf-8');
// Should have module.exports (CommonJS format)
expect(content).toContain('module.exports');
});
it('should have proper module structure', () => {
const indexPath = path.join(process.cwd(), 'dist', 'index.js');
const content = fs.readFileSync(indexPath, 'utf-8');
// Should be CommonJS module
expect(content).toContain('require');
expect(content).toContain('module.exports');
});
});
describe('Package.json validation', () => {
it('should have correct main field', () => {
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
expect(packageJson.main).toBe('dist/index.js');
});
it('should have correct types field', () => {
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
expect(packageJson.types).toBe('./dist/index.d.ts');
});
it('should have correct exports field', () => {
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
expect(packageJson.exports).toBeDefined();
expect(packageJson.exports['.']).toBeDefined();
expect(packageJson.exports['.'].default).toBe('./dist/index.js');
});
it('should include required files in files array', () => {
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
expect(packageJson.files).toContain('dist/index.js');
expect(packageJson.files).toContain('dist/index.d.ts');
});
it('should have required metadata', () => {
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
expect(packageJson.name).toBeDefined();
expect(packageJson.version).toBeDefined();
expect(packageJson.description).toBeDefined();
expect(packageJson.author).toBeDefined();
expect(packageJson.license).toBeDefined();
expect(packageJson.keywords).toBeDefined();
expect(packageJson.repository).toBeDefined();
expect(packageJson.bugs).toBeDefined();
expect(packageJson.homepage).toBeDefined();
});
it('should have correct peer dependencies', () => {
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
expect(packageJson.peerDependencies).toBeDefined();
expect(packageJson.peerDependencies.prettier).toBeDefined();
});
it('should have correct engines requirement', () => {
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
expect(packageJson.engines).toBeDefined();
expect(packageJson.engines.node).toBe('>=18.0.0');
});
});
describe('NPM package validation', () => {
it('should have all required files for npm publish', () => {
const requiredFiles = [
'dist/index.js',
'dist/index.d.ts',
'dist/index.js.map',
'README.md',
'package.json'
];
requiredFiles.forEach(file => {
const filePath = path.join(process.cwd(), file);
expect(fs.existsSync(filePath)).toBe(true);
});
});
it('should not include development files in npm package', () => {
const excludedFiles = [
'src/',
'test/',
'.github/',
'.husky/',
'.eslintrc.js',
'.prettierrc.js',
'tsconfig.json',
'bunfig.toml'
];
// These files should not be in the npm package
// (This is handled by .npmignore, but we can verify the ignore file exists)
expect(fs.existsSync('.npmignore')).toBe(true);
});
it('should have valid package.json for npm', () => {
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
// Required fields for npm
expect(packageJson.name).toBeTruthy();
expect(packageJson.version).toBeTruthy();
expect(packageJson.main).toBeTruthy();
expect(packageJson.license).toBeTruthy();
// Should not have private field (or it should be false)
if (packageJson.private !== undefined) {
expect(packageJson.private).toBe(false);
}
});
});
describe('TypeScript compilation', () => {
it('should compile without errors', () => {
// This test assumes the build has already been run
// In a real scenario, you might want to run tsc programmatically
const distPath = path.join(process.cwd(), 'dist');
expect(fs.existsSync(distPath)).toBe(true);
});
it('should generate declaration files', () => {
const dtsPath = path.join(process.cwd(), 'dist', 'index.d.ts');
const content = fs.readFileSync(dtsPath, 'utf-8');
// Should contain type declarations
expect(content).toContain('declare');
expect(content).toContain('export');
});
it('should generate source maps', () => {
const mapPath = path.join(process.cwd(), 'dist', 'index.js.map');
const content = fs.readFileSync(mapPath, 'utf-8');
const sourceMap = JSON.parse(content);
// Should be valid source map
expect(sourceMap.version).toBeDefined();
expect(sourceMap.sources).toBeDefined();
expect(sourceMap.mappings).toBeDefined();
});
});
});

View File

@@ -1,79 +1,10 @@
import { describe, expect, it } from 'bun:test';
import plugin from '../src/index';
import { parsers, printers } from '../src/index';
describe('Coverage Tests', () => {
describe('Error handling and edge cases', () => {
it('should handle null and undefined content', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
// Test with null content
expect(() => {
// @ts-expect-error We are testing edge cases
parser?.parse('null', { filepath: 'test.json' });
}).toThrow();
// Test with undefined content
expect(() => {
// @ts-expect-error We are testing edge cases
parser?.parse('undefined', { filepath: 'test.json' });
}).toThrow();
});
it('should handle non-object content', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
// Test with string content
expect(() => {
// @ts-expect-error We are testing edge cases
parser?.parse('"string"', { filepath: 'test.json' });
}).toThrow();
// Test with number content
expect(() => {
// @ts-expect-error We are testing edge cases
parser?.parse('123', { filepath: 'test.json' });
}).toThrow();
});
it('should handle array content', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
// Test with array content
expect(() => {
// @ts-expect-error We are testing edge cases
parser?.parse('[]', { filepath: 'test.json' });
}).toThrow();
});
it('should handle malformed JSON', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
// Test with malformed JSON
expect(() => {
// @ts-expect-error We are testing edge cases
parser?.parse('{invalid json}', { filepath: 'test.json' });
}).toThrow('Failed to parse OpenAPI JSON');
});
it('should handle malformed YAML', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
// Test with malformed YAML
expect(() => {
// @ts-expect-error We are testing edge cases
parser?.parse('invalid: yaml: content:', { filepath: 'test.yaml' });
}).toThrow('Failed to parse OpenAPI YAML');
});
});
describe('File path detection', () => {
it('should detect OpenAPI files in component directories', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const testYaml = `type: object
@@ -100,13 +31,13 @@ properties:
// @ts-expect-error We are testing edge cases
const result = parser?.parse(testYaml, { filepath: path });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml');
});
});
it('should handle files without filepath', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const testYaml = `openapi: 3.0.0
@@ -117,13 +48,13 @@ info:
// @ts-expect-error We are testing edge cases
const result = parser?.parse(testYaml, { filepath: 'test.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.isOpenAPI).toBeTrue();
});
});
describe('Object type detection', () => {
it('should detect operation objects', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const operationYaml = `get:
@@ -135,11 +66,11 @@ info:
// @ts-expect-error We are testing edge cases
const result = parser?.parse(operationYaml, { filepath: 'paths/users.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.isOpenAPI).toBeTrue();
});
it('should detect parameter objects', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const parameterYaml = `name: id
@@ -151,11 +82,11 @@ schema:
// @ts-expect-error We are testing edge cases
const result = parser?.parse(parameterYaml, { filepath: 'components/parameters/UserId.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.isOpenAPI).toBeTrue();
});
it('should detect schema objects', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const schemaYaml = `type: object
@@ -170,11 +101,11 @@ required:
// @ts-expect-error We are testing edge cases
const result = parser?.parse(schemaYaml, { filepath: 'components/schemas/User.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.isOpenAPI).toBeTrue();
});
it('should detect response objects', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const responseYaml = `description: User response
@@ -186,11 +117,11 @@ content:
// @ts-expect-error We are testing edge cases
const result = parser?.parse(responseYaml, { filepath: 'components/responses/UserResponse.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.isOpenAPI).toBeTrue();
});
it('should detect security scheme objects', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const securityYaml = `type: http
@@ -200,11 +131,11 @@ bearerFormat: JWT`;
// @ts-expect-error We are testing edge cases
const result = parser?.parse(securityYaml, { filepath: 'components/securitySchemes/BearerAuth.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.isOpenAPI).toBeTrue();
});
it('should detect server objects', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const serverYaml = `url: https://api.example.com
@@ -213,11 +144,11 @@ description: Production server`;
// @ts-expect-error We are testing edge cases
const result = parser?.parse(serverYaml, { filepath: 'servers/production.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.isOpenAPI).toBeTrue();
});
it('should detect tag objects', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const tagYaml = `name: users
@@ -226,11 +157,11 @@ description: User management operations`;
// @ts-expect-error We are testing edge cases
const result = parser?.parse(tagYaml, { filepath: 'tags/users.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.isOpenAPI).toBeTrue();
});
it('should detect external docs objects', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const externalDocsYaml = `url: https://example.com/docs
@@ -239,11 +170,11 @@ description: External documentation`;
// @ts-expect-error We are testing edge cases
const result = parser?.parse(externalDocsYaml, { filepath: 'externalDocs/api.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.isOpenAPI).toBeTrue();
});
it('should detect webhook objects', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const webhookYaml = `post:
@@ -255,17 +186,17 @@ description: External documentation`;
// @ts-expect-error We are testing edge cases
const result = parser?.parse(webhookYaml, { filepath: 'webhooks/messageCreated.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.isOpenAPI).toBeTrue();
});
});
describe('Sorting functions', () => {
it('should handle path sorting by specificity', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
type: 'openapi',
isOpenAPI: true,
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -280,9 +211,9 @@ description: External documentation`;
};
// @ts-expect-error We are testing edge cases
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (result && typeof result === 'string') {
const formatted = JSON.parse(result);
const pathKeys = Object.keys(formatted.paths);
@@ -294,11 +225,11 @@ description: External documentation`;
});
it('should handle response code sorting', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
type: 'openapi',
isOpenAPI: true,
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -319,9 +250,9 @@ description: External documentation`;
};
// @ts-expect-error We are testing edge cases
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (result && typeof result === 'string') {
const formatted = JSON.parse(result);
const responseKeys = Object.keys(formatted.paths['/test'].get.responses);
@@ -335,11 +266,11 @@ description: External documentation`;
describe('Context key detection', () => {
it('should handle nested path contexts', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
type: 'openapi',
isOpenAPI: true,
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -360,9 +291,9 @@ description: External documentation`;
};
// @ts-expect-error We are testing edge cases
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (result && typeof result === 'string') {
const formatted = JSON.parse(result);
expect(formatted.components.schemas.User).toBeDefined();

View File

@@ -1,9 +1,9 @@
import { describe, expect, it } from 'bun:test';
import plugin from '../src/index';
import {parsers, printers} from '../src/index';
describe('Custom Extensions Support', () => {
it('should handle custom extensions in top-level keys', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const testJson = {
@@ -21,7 +21,7 @@ describe('Custom Extensions Support', () => {
});
it('should handle custom extensions in info section', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const testJson = {
@@ -43,7 +43,7 @@ describe('Custom Extensions Support', () => {
});
it('should handle custom extensions in operation objects', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const testJson = {
@@ -68,7 +68,7 @@ describe('Custom Extensions Support', () => {
});
it('should handle custom extensions in schema objects', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const testJson = {
@@ -95,10 +95,12 @@ describe('Custom Extensions Support', () => {
});
it('should format JSON with custom extensions', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'json',
content: {
'x-custom-field': 'value',
'openapi': '3.0.0',
@@ -109,17 +111,19 @@ describe('Custom Extensions Support', () => {
};
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
expect(result).toContain('x-custom-field');
expect(result).toContain('openapi');
});
it('should format YAML with custom extensions', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'yaml',
content: {
'x-custom-field': 'value',
'openapi': '3.0.0',
@@ -130,14 +134,14 @@ describe('Custom Extensions Support', () => {
};
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
expect(result).toContain('x-custom-field:');
expect(result).toContain('openapi:');
});
it('should handle unknown keys alphabetically at the end', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const testJson = {
@@ -156,10 +160,12 @@ describe('Custom Extensions Support', () => {
describe('Custom extension positioning', () => {
it('should position custom extensions correctly in top-level', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'json',
content: {
'x-custom-field': 'value',
'openapi': '3.0.0',
@@ -170,7 +176,7 @@ describe('Custom Extensions Support', () => {
};
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (!result) {
@@ -193,10 +199,12 @@ describe('Custom Extensions Support', () => {
});
it('should position custom extensions correctly in info section', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'yaml',
content: {
'openapi': '3.0.0',
'info': {
@@ -210,7 +218,7 @@ describe('Custom Extensions Support', () => {
};
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (!result) {
@@ -233,10 +241,12 @@ describe('Custom Extensions Support', () => {
});
it('should position custom extensions correctly in operation objects', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'yaml',
content: {
'openapi': '3.0.0',
'info': { 'title': 'Test API', 'version': '1.0.0' },
@@ -254,7 +264,7 @@ describe('Custom Extensions Support', () => {
};
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (!result) {
@@ -274,10 +284,12 @@ describe('Custom Extensions Support', () => {
});
it('should position custom extensions correctly in schema objects', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'yaml',
content: {
'openapi': '3.0.0',
'info': { 'title': 'Test API', 'version': '1.0.0' },
@@ -295,7 +307,7 @@ describe('Custom Extensions Support', () => {
};
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (!result) {
@@ -318,10 +330,12 @@ describe('Custom Extensions Support', () => {
describe('Unknown key handling', () => {
it('should sort unknown keys alphabetically at the end', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'json',
content: {
'openapi': '3.0.0',
'info': { 'title': 'Test API', 'version': '1.0.0' },
@@ -332,7 +346,7 @@ describe('Custom Extensions Support', () => {
};
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (!result) {
@@ -358,10 +372,12 @@ describe('Custom Extensions Support', () => {
});
it('should handle mixed custom extensions and unknown keys', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'json',
content: {
'openapi': '3.0.0',
'info': { 'title': 'Test API', 'version': '1.0.0' },
@@ -374,7 +390,7 @@ describe('Custom Extensions Support', () => {
};
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (!result) {

View File

@@ -1,12 +1,12 @@
import { describe, expect, it } from 'bun:test';
import plugin from '../src/index';
import { parsers } from '../src/index';
describe('File Detection Tests', () => {
it('should detect OpenAPI root files', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
it('should detect OpenAPI root files', () => {
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const testYaml = `openapi: 3.0.0
const testYaml = `openapi: 3.0.0
info:
title: Test API
version: 1.0.0
@@ -17,20 +17,20 @@ paths:
'200':
description: Success`;
// @ts-expect-error We are mocking things here
const result = parser?.parse(testYaml, { filepath: 'openapi.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.format).toBe('yaml');
expect(result?.content.openapi).toBe('3.0.0');
});
// @ts-expect-error We are mocking things here
const result = parser?.parse(testYaml, { filepath: 'openapi.yaml' });
it('should detect partial schema files', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
expect(result).toBeDefined();
expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml');
expect(result?.content.openapi).toBe('3.0.0');
});
const schemaYaml = `type: object
it('should detect partial schema files', () => {
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const schemaYaml = `type: object
properties:
id:
type: integer
@@ -40,40 +40,40 @@ required:
- id
- name`;
// @ts-expect-error We are mocking things here
const result = parser?.parse(schemaYaml, { filepath: 'components/schemas/User.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.format).toBe('yaml');
expect(result?.content.type).toBe('object');
});
// @ts-expect-error We are mocking things here
const result = parser?.parse(schemaYaml, { filepath: 'components/schemas/User.yaml' });
it('should detect parameter files', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
expect(result).toBeDefined();
expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml');
expect(result?.content.type).toBe('object');
});
const parameterYaml = `name: id
it('should detect parameter files', () => {
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const parameterYaml = `name: id
in: path
required: true
description: User ID
schema:
type: integer`;
// @ts-expect-error We are mocking things here
const result = parser?.parse(parameterYaml, { filepath: 'components/parameters/UserId.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.format).toBe('yaml');
expect(result?.content.name).toBe('id');
});
// @ts-expect-error We are mocking things here
const result = parser?.parse(parameterYaml, { filepath: 'components/parameters/UserId.yaml' });
it('should detect response files', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
expect(result).toBeDefined();
expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml');
expect(result?.content.name).toBe('id');
});
const responseYaml = `description: User response
it('should detect response files', () => {
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const responseYaml = `description: User response
content:
application/json:
schema:
@@ -84,20 +84,20 @@ content:
name:
type: string`;
// @ts-expect-error We are mocking things here
const result = parser?.parse(responseYaml, { filepath: 'components/responses/UserResponse.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.format).toBe('yaml');
expect(result?.content.description).toBe('User response');
});
// @ts-expect-error We are mocking things here
const result = parser?.parse(responseYaml, { filepath: 'components/responses/UserResponse.yaml' });
it('should detect path files', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
expect(result).toBeDefined();
expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml');
expect(result?.content.description).toBe('User response');
});
const pathYaml = `get:
it('should detect path files', () => {
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const pathYaml = `get:
summary: Get users
responses:
'200':
@@ -110,91 +110,100 @@ post:
schema:
type: object`;
// @ts-expect-error We are mocking things here
const result = parser?.parse(pathYaml, { filepath: 'paths/users.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.format).toBe('yaml');
expect(result?.content.get).toBeDefined();
expect(result?.content.post).toBeDefined();
});
// @ts-expect-error We are mocking things here
const result = parser?.parse(pathYaml, { filepath: 'paths/users.yaml' });
it('should detect security scheme files', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
expect(result).toBeDefined();
expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml');
expect(result?.content.get).toBeDefined();
expect(result?.content.post).toBeDefined();
});
const securityYaml = `type: http
it('should detect security scheme files', () => {
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const securityYaml = `type: http
scheme: bearer
bearerFormat: JWT
description: JWT authentication`;
// @ts-expect-error We are mocking things here
const result = parser?.parse(securityYaml, { filepath: 'components/securitySchemes/BearerAuth.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.format).toBe('yaml');
expect(result?.content.type).toBe('http');
});
// @ts-expect-error We are mocking things here
const result = parser?.parse(securityYaml, { filepath: 'components/securitySchemes/BearerAuth.yaml' });
it('should reject non-OpenAPI files', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
expect(result).toBeDefined();
expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml');
expect(result?.content.type).toBe('http');
});
const nonOpenAPIYaml = `name: John
it('should handle non-OpenAPI files', () => {
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const nonOpenAPIYaml = `name: John
age: 30
city: New York`;
// @ts-expect-error We are mocking things here
expect(() => parser?.parse(nonOpenAPIYaml, { filepath: 'config/data.yaml' })).toThrow('Not an OpenAPI file');
});
const parsedYaml = {
name: 'John',
age: 30,
city: 'New York'
}
it('should accept files in OpenAPI directories even with simple content', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
// @ts-expect-error We are mocking things here
const parsedData = parser?.parse(nonOpenAPIYaml, { filepath: 'config/data.yaml' })
const simpleYaml = `name: John
expect(parsedData).toBeDefined()
expect(parsedData?.isOpenAPI).toBeFalse();
});
it('should accept files in OpenAPI directories even with simple content', () => {
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const simpleYaml = `name: John
age: 30
city: New York`;
// @ts-expect-error We are mocking things here
const result = parser?.parse(simpleYaml, { filepath: 'components/schemas/User.yaml' });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.format).toBe('yaml');
});
// @ts-expect-error We are mocking things here
const result = parser?.parse(simpleYaml, { filepath: 'components/schemas/User.yaml' });
expect(result).toBeDefined();
expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml');
});
it('should support component directory patterns', () => {
const parser = plugin.parsers?.['openapi-parser'];
expect(parser).toBeDefined();
it('should support component directory patterns', () => {
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const componentYaml = `type: object
const componentYaml = `type: object
properties:
message:
type: string`;
// Test various component directory patterns
const paths = [
'components/schemas/Error.yaml',
'components/parameters/CommonPagination.yaml',
'components/responses/ErrorResponse.yaml',
'components/requestBodies/UserCreateBody.yaml',
'components/headers/RateLimitHeaders.yaml',
'components/examples/UserExample.yaml',
'components/securitySchemes/BearerAuth.yaml',
'components/links/UserCreatedLink.yaml',
'components/callbacks/NewMessageCallback.yaml',
'webhooks/messageCreated.yaml',
'paths/users.yaml'
];
// Test various component directory patterns
const paths = [
'components/schemas/Error.yaml',
'components/parameters/CommonPagination.yaml',
'components/responses/ErrorResponse.yaml',
'components/requestBodies/UserCreateBody.yaml',
'components/headers/RateLimitHeaders.yaml',
'components/examples/UserExample.yaml',
'components/securitySchemes/BearerAuth.yaml',
'components/links/UserCreatedLink.yaml',
'components/callbacks/NewMessageCallback.yaml',
'webhooks/messageCreated.yaml',
'paths/users.yaml'
];
paths.forEach(path => {
// @ts-expect-error We are mocking things here
const result = parser?.parse(componentYaml, { filepath: path });
expect(result).toBeDefined();
expect(result?.type).toBe('openapi');
expect(result?.format).toBe('yaml');
paths.forEach(path => {
// @ts-expect-error We are mocking things here
const result = parser?.parse(componentYaml, { filepath: path });
expect(result).toBeDefined();
expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml');
});
});
});
});

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from 'bun:test';
import plugin from '../src/index';
import {parsers, printers} from '../src/index';
describe('Integration Tests', () => {
describe('Real OpenAPI file processing', () => {
@@ -213,18 +213,18 @@ describe('Integration Tests', () => {
]
};
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
type: 'openapi',
isOpenAPI: true,
content: openApiContent,
originalText: '',
format: 'json'
};
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (result && typeof result === 'string') {
@@ -365,18 +365,18 @@ describe('Integration Tests', () => {
]
};
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
type: 'openapi',
isOpenAPI: true,
content: swaggerContent,
originalText: '',
format: 'json'
};
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (result && typeof result === 'string') {
@@ -419,18 +419,18 @@ describe('Integration Tests', () => {
}
};
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
type: 'openapi',
isOpenAPI: true,
content: yamlContent,
originalText: '',
format: 'yaml'
};
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (result) {
@@ -451,7 +451,7 @@ describe('Integration Tests', () => {
describe('Error handling', () => {
it('should handle malformed JSON gracefully', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const malformedJson = '{"openapi": "3.0.0", "info": {';
@@ -461,7 +461,7 @@ describe('Integration Tests', () => {
});
it('should handle malformed YAML gracefully', () => {
const parser = plugin.parsers?.['openapi-parser'];
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const malformedYaml = 'openapi: 3.0.0\ninfo:\n title: Test\n version: 1.0.0\n invalid: [';
@@ -470,14 +470,23 @@ describe('Integration Tests', () => {
expect(() => parser?.parse(malformedYaml, { filepath: 'test.yaml' })).toThrow();
});
it('should reject non-OpenAPI content', () => {
const parser = plugin.parsers?.['openapi-parser'];
it('should handle non-OpenAPI content', () => {
const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined();
const nonOpenAPI = '{"name": "John", "age": 30}';
const parsedJSON = {
name: 'John',
age: 30
}
// @ts-expect-error We are mocking things here
expect(() => parser?.parse(nonOpenAPI, { filepath: 'test.json' })).toThrow('Not an OpenAPI file');
const parsedData = parser?.parse(nonOpenAPI, { filepath: 'test.json' })
expect(parsedData).toBeDefined();
expect(parsedData?.isOpenAPI).toBeFalse();
expect(parsedData?.content).toBeDefined();
expect(parsedData?.content).toEqual(parsedJSON);
});
});
@@ -506,11 +515,11 @@ describe('Integration Tests', () => {
};
}
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
type: 'openapi',
isOpenAPI: true,
content: largeOpenAPI,
originalText: '',
format: 'json'
@@ -519,7 +528,7 @@ describe('Integration Tests', () => {
const startTime = Date.now();
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
const endTime = Date.now();
const duration = endTime - startTime;

View File

@@ -1,14 +1,15 @@
import { describe, it, expect } from 'bun:test';
import plugin from '../src/index';
import { printers } from '../src/index';
describe('Key Ordering Tests', () => {
describe('Info section key ordering', () => {
it('should sort info keys correctly', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
type: 'openapi',
isOpenAPI: true,
format: 'json',
content: {
openapi: '3.0.0',
info: {
@@ -21,10 +22,9 @@ describe('Key Ordering Tests', () => {
}
},
originalText: '',
format: 'json'
};
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 });
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
expect(result).toBeDefined();
if (!result) {
@@ -32,7 +32,7 @@ describe('Key Ordering Tests', () => {
}
const resultString = result.toString();
// Check that info keys appear in the correct order
const titleIndex = resultString.indexOf('title');
const versionIndex = resultString.indexOf('version');
@@ -51,11 +51,12 @@ describe('Key Ordering Tests', () => {
describe('Operation key ordering', () => {
it('should sort operation keys correctly', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
type: 'openapi',
isOpenAPI: true,
format: 'yaml',
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -77,12 +78,11 @@ describe('Key Ordering Tests', () => {
}
}
},
originalText: '',
format: 'json'
originalText: ''
};
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 });
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
expect(result).toBeDefined();
if (!result) {
@@ -90,7 +90,7 @@ describe('Key Ordering Tests', () => {
}
const resultString = result.toString();
// Check that operation keys appear in the correct order
const summaryIndex = resultString.indexOf('summary');
const operationIdIndex = resultString.indexOf('operationId');
@@ -119,10 +119,12 @@ describe('Key Ordering Tests', () => {
describe('Schema key ordering', () => {
it('should sort schema keys correctly', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'yaml',
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -169,7 +171,8 @@ describe('Key Ordering Tests', () => {
};
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 });
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
console.log('result', result);
expect(result).toBeDefined();
if (!result) {
@@ -177,7 +180,7 @@ describe('Key Ordering Tests', () => {
}
const resultString = result.toString();
const typeIndex = resultString.indexOf('type:');
const formatIndex = resultString.indexOf('format:');
const titleIndex = resultString.indexOf('title:');
@@ -247,10 +250,12 @@ describe('Key Ordering Tests', () => {
describe('Response key ordering', () => {
it('should sort response keys correctly', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'json',
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -271,7 +276,7 @@ describe('Key Ordering Tests', () => {
}
};
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 });
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
expect(result).toBeDefined();
if (!result) {
@@ -294,10 +299,12 @@ describe('Key Ordering Tests', () => {
describe('Parameter key ordering', () => {
it('should sort parameter keys correctly', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'yaml',
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -327,7 +334,7 @@ describe('Key Ordering Tests', () => {
}
};
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 });
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
expect(result).toBeDefined();
if (!result) {
@@ -335,7 +342,7 @@ describe('Key Ordering Tests', () => {
}
const resultString = result.toString();
const nameIndex = resultString.indexOf('name:');
const inIndex = resultString.indexOf('in:');
const descriptionIndex = resultString.indexOf('description:');
@@ -366,10 +373,12 @@ describe('Key Ordering Tests', () => {
describe('Security scheme key ordering', () => {
it('should sort security scheme keys correctly', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'yaml',
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -395,7 +404,7 @@ describe('Key Ordering Tests', () => {
}
};
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 });
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
expect(result).toBeDefined();
if (!result) {
@@ -426,10 +435,12 @@ describe('Key Ordering Tests', () => {
describe('Server key ordering', () => {
it('should sort server keys correctly', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'json',
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -442,8 +453,8 @@ describe('Key Ordering Tests', () => {
]
}
};
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 });
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
expect(result).toBeDefined();
if (!result) {
@@ -451,7 +462,7 @@ describe('Key Ordering Tests', () => {
}
const resultString = result.toString();
const nameIndex = resultString.indexOf('name');
const descriptionIndex = resultString.indexOf('description');
const urlIndex = resultString.indexOf('url');
@@ -465,10 +476,12 @@ describe('Key Ordering Tests', () => {
describe('Tag key ordering', () => {
it('should sort tag keys correctly', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'json',
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -481,8 +494,8 @@ describe('Key Ordering Tests', () => {
]
}
};
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 });
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
expect(result).toBeDefined();
if (!result) {
@@ -490,7 +503,7 @@ describe('Key Ordering Tests', () => {
}
const resultString = result.toString();
const nameIndex = resultString.indexOf('name');
const descriptionIndex = resultString.indexOf('description');
const externalDocsIndex = resultString.indexOf('externalDocs');
@@ -502,10 +515,12 @@ describe('Key Ordering Tests', () => {
describe('External docs key ordering', () => {
it('should sort external docs keys correctly', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'json',
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -515,8 +530,8 @@ describe('Key Ordering Tests', () => {
}
}
};
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 });
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
expect(result).toBeDefined();
if (!result) {
@@ -524,7 +539,7 @@ describe('Key Ordering Tests', () => {
}
const resultString = result.toString();
// Check that external docs keys appear in the correct order
const descriptionIndex = resultString.indexOf('description');
const urlIndex = resultString.indexOf('url');
@@ -535,10 +550,12 @@ describe('Key Ordering Tests', () => {
describe('Path sorting', () => {
it('should sort paths by specificity', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'json',
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -550,8 +567,8 @@ describe('Key Ordering Tests', () => {
}
}
};
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 });
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
expect(result).toBeDefined();
if (!result) {
@@ -559,7 +576,7 @@ describe('Key Ordering Tests', () => {
}
const resultString = result.toString();
// Check that paths are sorted by specificity (fewer parameters first)
const usersIndex = resultString.indexOf('/users');
const usersIdIndex = resultString.indexOf('/users/{id}');
@@ -574,10 +591,12 @@ describe('Key Ordering Tests', () => {
describe('Response code sorting', () => {
it('should sort response codes numerically', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'json',
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -596,8 +615,8 @@ describe('Key Ordering Tests', () => {
}
}
};
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 });
// @ts-expect-error We are mocking things here so we don't need to pass a print function
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 });
expect(result).toBeDefined();
if (!result) {
@@ -605,7 +624,7 @@ describe('Key Ordering Tests', () => {
}
const resultString = result.toString();
// Check that response codes are sorted numerically
const code200Index = resultString.indexOf('200');
const code400Index = resultString.indexOf('400');

View File

@@ -1,105 +0,0 @@
import { describe, it, expect } from 'bun:test';
import plugin from '../src/index';
describe('Plugin Options', () => {
it('should use custom tabWidth for JSON formatting', () => {
const testData = {
openapi: '3.0.0',
info: {
title: 'Test',
version: '1.0.0'
},
paths: {}
};
const printer = plugin.printers?.['openapi-ast'];
expect(printer).toBeDefined();
const result = printer?.print({ getValue: () => ({ type: 'openapi', content: testData, originalText: '', format: 'json' }) }, { tabWidth: 4 }, () => '');
expect(result).toBeDefined();
if (!result) {
throw new Error('Result is undefined');
}
// Check that 4-space indentation is used
expect(result).toContain(' "openapi"');
expect(result).toContain(' "info"');
expect(result).toContain(' "title"');
expect(result).toContain(' "version"');
});
it('should use custom tabWidth for YAML formatting', () => {
const testData = {
openapi: '3.0.0',
info: {
title: 'Test',
version: '1.0.0'
},
paths: {}
};
const printer = plugin.printers?.['openapi-ast'];
expect(printer).toBeDefined();
const result = printer?.print({ getValue: () => ({ type: 'openapi', content: testData, originalText: '', format: 'yaml' }) }, { tabWidth: 4 }, () => '');
expect(result).toBeDefined();
if (!result) {
throw new Error('Result is undefined');
}
// Check that 4-space indentation is used
expect(result).toContain(' title: Test');
expect(result).toContain(' version: 1.0.0');
});
it('should use default tabWidth when not specified', () => {
const testData = {
openapi: '3.0.0',
info: {
title: 'Test',
version: '1.0.0'
},
paths: {}
};
const printer = plugin.printers?.['openapi-ast'];
expect(printer).toBeDefined();
const result = printer?.print({ getValue: () => ({ type: 'openapi', content: testData, originalText: '', format: 'json' }) }, {}, () => '');
expect(result).toBeDefined();
if (!result) {
throw new Error('Result is undefined');
}
// Check that 2-space indentation is used (default)
expect(result).toContain(' "openapi"');
expect(result).toContain(' "info"');
expect(result).toContain(' "title"');
expect(result).toContain(' "version"');
});
it('should use custom printWidth for YAML formatting', () => {
const testData = {
openapi: '3.0.0',
info: {
title: 'This is a very long title that should be wrapped according to printWidth',
version: '1.0.0'
},
paths: {}
};
const printer = plugin.printers?.['openapi-ast'];
expect(printer).toBeDefined();
const result = printer?.print({ getValue: () => ({ type: 'openapi', content: testData, originalText: '', format: 'yaml' }) }, { printWidth: 20 }, () => '');
expect(result).toBeDefined();
// The YAML should be formatted with the custom line width
expect(result).toBeDefined();
// Note: js-yaml doesn't always respect lineWidth for all content types,
// but we can verify the option is passed through
});
});

View File

@@ -32,13 +32,13 @@ describe('Prettier OpenAPI Plugin', () => {
// @ts-expect-error We are mocking things here
const parsed = parser?.parse(inputJson, { filepath: 'test.json' });
expect(parsed).toBeDefined();
expect(parsed?.type).toBe('openapi');
expect(parsed?.isOpenAPI).toBeTrue();
expect(parsed?.format).toBe('json');
expect(parsed?.content).toBeDefined();
// Format the parsed content
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => parsed }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
expect(result).toContain('"openapi"');
@@ -80,13 +80,13 @@ openapi: 3.0.0`;
// @ts-expect-error We are mocking things here
const parsed = parser?.parse(inputYaml, { filepath: 'test.yaml' });
expect(parsed).toBeDefined();
expect(parsed?.type).toBe('openapi');
expect(parsed?.isOpenAPI).toBeTrue();
expect(parsed?.format).toBe('yaml');
expect(parsed?.content).toBeDefined();
// Format the parsed content
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => parsed }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
expect(result).toContain('openapi:');
@@ -148,13 +148,13 @@ openapi: 3.0.0`;
// @ts-expect-error We are mocking things here
const parsed = parser?.parse(inputJson, { filepath: 'test.json' });
expect(parsed).toBeDefined();
expect(parsed?.type).toBe('openapi');
expect(parsed?.isOpenAPI).toBeTrue();
expect(parsed?.format).toBe('json');
expect(parsed?.content).toBeDefined();
// Format the parsed content
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => parsed }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
expect(result).toContain('"swagger"');
@@ -201,13 +201,13 @@ describe('Key Ordering Tests', () => {
// @ts-expect-error We are mocking things here
const parsed = parser?.parse(inputJson, { filepath: 'test.json' });
expect(parsed).toBeDefined();
expect(parsed?.type).toBe('openapi');
expect(parsed?.isOpenAPI).toBeTrue();
expect(parsed?.format).toBe('json');
expect(parsed?.content).toBeDefined();
// Format the parsed content
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => parsed }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
@@ -261,13 +261,13 @@ describe('Key Ordering Tests', () => {
// @ts-expect-error We are mocking things here
const parsed = parser?.parse(inputJson, { filepath: 'test.json' });
expect(parsed).toBeDefined();
expect(parsed?.type).toBe('openapi');
expect(parsed?.isOpenAPI).toBeTrue();
expect(parsed?.format).toBe('json');
expect(parsed?.content).toBeDefined();
// Format the parsed content
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => parsed }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();

View File

@@ -1,12 +1,14 @@
import { describe, expect, it } from 'bun:test';
import plugin from '../src/index';
import { printers } from '../src/index';
describe('Simple Key Ordering Tests', () => {
it('should sort top-level OpenAPI keys correctly', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'yaml',
content: {
paths: { '/test': { get: {} } },
components: { schemas: {} },
@@ -19,7 +21,7 @@ describe('Simple Key Ordering Tests', () => {
};
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (!result) {
@@ -46,10 +48,12 @@ describe('Simple Key Ordering Tests', () => {
});
it('should sort operation keys correctly', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'json',
content: {
openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' },
@@ -74,7 +78,7 @@ describe('Simple Key Ordering Tests', () => {
};
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (!result) {
@@ -109,10 +113,12 @@ describe('Simple Key Ordering Tests', () => {
});
it('should sort info keys correctly', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'json',
content: {
openapi: '3.0.0',
info: {
@@ -127,7 +133,7 @@ describe('Simple Key Ordering Tests', () => {
};
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
expect(result).toBeDefined();
if (!result) {
@@ -135,8 +141,6 @@ describe('Simple Key Ordering Tests', () => {
}
const resultString = result.toString();
console.log(resultString);
// Check that info keys appear in the correct order
const titleIndex = resultString.indexOf('title');
@@ -154,10 +158,12 @@ describe('Simple Key Ordering Tests', () => {
});
it('should handle custom extensions correctly', () => {
const printer = plugin.printers?.['openapi-ast'];
const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined();
const testData = {
isOpenAPI: true,
format: 'json',
content: {
'x-custom-field': 'value',
'openapi': '3.0.0',
@@ -168,7 +174,8 @@ describe('Simple Key Ordering Tests', () => {
};
// @ts-expect-error We are mocking things here
const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => '');
const result = printer?.print({ getNode: () => testData }, { tabWidth: 2 }, () => '');
console.log('result', result);
expect(result).toBeDefined();
if (!result) {