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 { import {
RootKeys, RootKeys,
InfoKeys, InfoKeys,
ComponentsKeys,
OperationKeys, OperationKeys,
ParameterKeys, ParameterKeys,
SchemaKeys, SchemaKeys,
@@ -18,13 +19,21 @@ import {
TagKeys, TagKeys,
ExternalDocsKeys, ExternalDocsKeys,
WebhookKeys, WebhookKeys,
OAuthFlowKeys, PathItemKeys,
RequestBodyKeys,
MediaTypeKeys,
EncodingKeys,
HeaderKeys,
LinkKeys,
ExampleKeys,
DiscriminatorKeys,
XMLKeys,
ContactKeys, ContactKeys,
LicenseKeys, LicenseKeys,
ComponentsKeys, OAuthFlowKeys,
ServerVariableKeys, ServerVariableKeys,
} from '../keys.js'; } from '../keys.js';
import { getVendorExtensions, type VendorModule } from './vendor-loader.js'; import { type VendorModule } from './vendor-loader.js';
/** /**
* Type-safe context-specific extension functions * Type-safe context-specific extension functions
@@ -528,6 +537,307 @@ export interface VendorExtensions {
* ``` * ```
*/ */
'securityDefinitions'?: ContextExtensionFunction<'securityDefinitions'>; '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 = { export const KeyMap = {
'top-level': RootKeys, 'top-level': RootKeys,
'info': InfoKeys, 'info': InfoKeys,
'components': ComponentsKeys,
'operation': OperationKeys, 'operation': OperationKeys,
'parameter': ParameterKeys, 'parameter': ParameterKeys,
'schema': SchemaKeys, 'schema': SchemaKeys,
@@ -618,6 +929,19 @@ export const KeyMap = {
'tag': TagKeys, 'tag': TagKeys,
'externalDocs': ExternalDocsKeys, 'externalDocs': ExternalDocsKeys,
'webhook': WebhookKeys, '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, 'definitions': SchemaKeys,
'securityDefinitions': SecuritySchemeKeys, 'securityDefinitions': SecuritySchemeKeys,
} }

View File

@@ -4,7 +4,7 @@
* Loads vendor extensions using static imports for ES module compatibility. * 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 vendor extensions statically
import { speakeasy } from './vendor/speakeasy.js'; 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 * as yaml from 'js-yaml';
import type{ AstPath, Doc, Parser, ParserOptions, Printer, SupportLanguage } from 'prettier';
import { getVendorExtensions } from './extensions/vendor-loader.js'; import { getVendorExtensions } from './extensions/vendor-loader.js';
export type PrintFn = (path: AstPath) => Doc;
import { import {
RootKeys,
InfoKeys,
ContactKeys,
LicenseKeys,
ComponentsKeys, ComponentsKeys,
ContactKeys,
DiscriminatorKeys,
EncodingKeys,
ExampleKeys,
ExternalDocsKeys,
HeaderKeys,
InfoKeys,
LicenseKeys,
LinkKeys,
MediaTypeKeys,
OAuthFlowKeys,
OperationKeys, OperationKeys,
ParameterKeys, ParameterKeys,
SchemaKeys, PathItemKeys,
RequestBodyKeys,
ResponseKeys, ResponseKeys,
RootKeys,
SchemaKeys,
SecuritySchemeKeys, SecuritySchemeKeys,
OAuthFlowKeys,
ServerKeys, ServerKeys,
ServerVariableKeys, ServerVariableKeys,
TagKeys, TagKeys,
ExternalDocsKeys,
WebhookKeys, WebhookKeys,
PathItemKeys,
RequestBodyKeys,
MediaTypeKeys,
EncodingKeys,
HeaderKeys,
LinkKeys,
ExampleKeys,
DiscriminatorKeys,
XMLKeys, XMLKeys,
} from './keys.js'; } from './keys.js';
// Type definitions for better type safety // Type definitions for better type safety
interface OpenAPINode { interface OpenAPINode {
type: 'openapi'; isOpenAPI: boolean;
content: any; content: any;
originalText: string; originalText: string;
format: 'json' | 'yaml'; format: 'json' | 'yaml';
} }
interface PrettierPath {
getValue(): OpenAPINode;
}
interface OpenAPIPluginOptions { interface OpenAPIPluginOptions {
tabWidth?: number; tabWidth?: number;
printWidth?: number; printWidth?: number;
@@ -59,18 +56,22 @@ const vendorExtensions = getVendorExtensions();
* Unified parser that can handle both JSON and YAML OpenAPI files * Unified parser that can handle both JSON and YAML OpenAPI files
*/ */
function parseOpenAPIFile(text: string, options?: any): OpenAPINode { function parseOpenAPIFile(text: string, options?: any): OpenAPINode {
// Try to detect the format based on file extension or content console.debug('parseOpenAPIFile', text, options);
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' | undefined;
let format: 'json' | 'yaml' = 'json'; // default to JSON
if (isYamlFile) { if (options?.filepath) {
switch (true) {
case options?.filepath.endsWith('.yaml') || options?.filepath.endsWith('.yml'):
format = 'yaml'; format = 'yaml';
} else if (isJsonFile) { break;
case options?.filepath.endsWith('.json'):
format = 'json'; format = 'json';
} else { break;
}
}
if (!format) {
// Try to detect format from content // Try to detect format from content
const trimmedText = text.trim(); const trimmedText = text.trim();
if (trimmedText.startsWith('{') || trimmedText.startsWith('[')) { if (trimmedText.startsWith('{') || trimmedText.startsWith('[')) {
@@ -80,12 +81,11 @@ function parseOpenAPIFile(text: string, options?: any): OpenAPINode {
} }
} }
try {
let parsed: any; let parsed: any;
if (format === 'json') { switch (format) {
parsed = JSON.parse(text); case 'yaml':
} else { try {
parsed = yaml.load(text, { parsed = yaml.load(text, {
schema: yaml.DEFAULT_SCHEMA, schema: yaml.DEFAULT_SCHEMA,
onWarning: (warning) => { onWarning: (warning) => {
@@ -93,21 +93,48 @@ function parseOpenAPIFile(text: string, options?: any): OpenAPINode {
console.warn('YAML parsing warning:', warning); console.warn('YAML parsing warning:', warning);
} }
}); });
} 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;
} }
// Check if this is an OpenAPI file let isOpenAPI: boolean;
if (!isOpenAPIFile(parsed, filePath)) {
throw new Error('Not an OpenAPI file');
}
try {
isOpenAPI = isOpenAPIFile(parsed, options?.filepath);
} catch (error) {
return { return {
type: 'openapi', isOpenAPI: false,
content: parsed, content: parsed,
originalText: text, originalText: text,
format: format, 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,
}
} }
} }
@@ -229,8 +256,7 @@ function isPathObject(obj: any): boolean {
return Object.keys(obj).some(key => httpMethods.includes(key.toLowerCase())); return Object.keys(obj).some(key => httpMethods.includes(key.toLowerCase()));
} }
const plugin: Plugin = { export const languages: Partial<SupportLanguage>[] = [
languages: [
{ {
name: 'openapi', name: 'openapi',
extensions: [ extensions: [
@@ -239,8 +265,9 @@ const plugin: Plugin = {
], ],
parsers: ['openapi-parser'], parsers: ['openapi-parser'],
}, },
], ];
parsers: {
export const parsers: Record<string, Parser> = {
'openapi-parser': { 'openapi-parser': {
parse: (text: string, options?: any): OpenAPINode => { parse: (text: string, options?: any): OpenAPINode => {
return parseOpenAPIFile(text, options); return parseOpenAPIFile(text, options);
@@ -249,16 +276,20 @@ const plugin: Plugin = {
locStart: (node: OpenAPINode) => 0, locStart: (node: OpenAPINode) => 0,
locEnd: (node: OpenAPINode) => node.originalText?.length || 0, locEnd: (node: OpenAPINode) => node.originalText?.length || 0,
}, },
}, }
printers: {
export const printers: Record<string, Printer> = {
'openapi-ast': { 'openapi-ast': {
print: (path: PrettierPath, options?: any, print?: any, ...rest: any[]): string => { print: (path: AstPath, options: ParserOptions, print: PrintFn): string => {
const node = path.getValue(); const node = path.getNode();
if (!node.isOpenAPI || node.isOpenAPI === false) {
// Return original text unchanged
return options.originalText;
}
return formatOpenAPI(node.content, node.format, options); return formatOpenAPI(node.content, node.format, options);
}, },
}, },
}, }
};
/** /**
* Unified formatter that outputs in the detected format * Unified formatter that outputs in the detected format
@@ -267,10 +298,10 @@ function formatOpenAPI(content: any, format: 'json' | 'yaml', options?: OpenAPIP
// Sort keys for better organization // Sort keys for better organization
const sortedContent = sortOpenAPIKeys(content); const sortedContent = sortOpenAPIKeys(content);
if (format === 'json') { switch (format) {
// Format with proper indentation case 'json':
return JSON.stringify(sortedContent, null, options?.tabWidth || 2); return JSON.stringify(sortedContent, null, options?.tabWidth || 2);
} else { case 'yaml':
// Format YAML with proper indentation and line breaks // Format YAML with proper indentation and line breaks
return yaml.dump(sortedContent, { return yaml.dump(sortedContent, {
indent: options?.tabWidth || 2, indent: options?.tabWidth || 2,
@@ -282,14 +313,6 @@ function formatOpenAPI(content: any, format: 'json' | 'yaml', options?: OpenAPIP
} }
} }
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 { function sortOpenAPIKeys(obj: any): any {
if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) { if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
return obj; return obj;
@@ -649,5 +672,3 @@ function getStandardKeysForContext(contextKey: string): readonly string[] {
default: return RootKeys; 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 { describe, expect, it } from 'bun:test';
import plugin from '../src/index'; import { parsers, printers } from '../src/index';
describe('Coverage Tests', () => { 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', () => { describe('File path detection', () => {
it('should detect OpenAPI files in component directories', () => { it('should detect OpenAPI files in component directories', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const testYaml = `type: object const testYaml = `type: object
@@ -100,13 +31,13 @@ properties:
// @ts-expect-error We are testing edge cases // @ts-expect-error We are testing edge cases
const result = parser?.parse(testYaml, { filepath: path }); const result = parser?.parse(testYaml, { filepath: path });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml'); expect(result?.format).toBe('yaml');
}); });
}); });
it('should handle files without filepath', () => { it('should handle files without filepath', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const testYaml = `openapi: 3.0.0 const testYaml = `openapi: 3.0.0
@@ -117,13 +48,13 @@ info:
// @ts-expect-error We are testing edge cases // @ts-expect-error We are testing edge cases
const result = parser?.parse(testYaml, { filepath: 'test.yaml' }); const result = parser?.parse(testYaml, { filepath: 'test.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
}); });
}); });
describe('Object type detection', () => { describe('Object type detection', () => {
it('should detect operation objects', () => { it('should detect operation objects', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const operationYaml = `get: const operationYaml = `get:
@@ -135,11 +66,11 @@ info:
// @ts-expect-error We are testing edge cases // @ts-expect-error We are testing edge cases
const result = parser?.parse(operationYaml, { filepath: 'paths/users.yaml' }); const result = parser?.parse(operationYaml, { filepath: 'paths/users.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
}); });
it('should detect parameter objects', () => { it('should detect parameter objects', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const parameterYaml = `name: id const parameterYaml = `name: id
@@ -151,11 +82,11 @@ schema:
// @ts-expect-error We are testing edge cases // @ts-expect-error We are testing edge cases
const result = parser?.parse(parameterYaml, { filepath: 'components/parameters/UserId.yaml' }); const result = parser?.parse(parameterYaml, { filepath: 'components/parameters/UserId.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
}); });
it('should detect schema objects', () => { it('should detect schema objects', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const schemaYaml = `type: object const schemaYaml = `type: object
@@ -170,11 +101,11 @@ required:
// @ts-expect-error We are testing edge cases // @ts-expect-error We are testing edge cases
const result = parser?.parse(schemaYaml, { filepath: 'components/schemas/User.yaml' }); const result = parser?.parse(schemaYaml, { filepath: 'components/schemas/User.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
}); });
it('should detect response objects', () => { it('should detect response objects', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const responseYaml = `description: User response const responseYaml = `description: User response
@@ -186,11 +117,11 @@ content:
// @ts-expect-error We are testing edge cases // @ts-expect-error We are testing edge cases
const result = parser?.parse(responseYaml, { filepath: 'components/responses/UserResponse.yaml' }); const result = parser?.parse(responseYaml, { filepath: 'components/responses/UserResponse.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
}); });
it('should detect security scheme objects', () => { it('should detect security scheme objects', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const securityYaml = `type: http const securityYaml = `type: http
@@ -200,11 +131,11 @@ bearerFormat: JWT`;
// @ts-expect-error We are testing edge cases // @ts-expect-error We are testing edge cases
const result = parser?.parse(securityYaml, { filepath: 'components/securitySchemes/BearerAuth.yaml' }); const result = parser?.parse(securityYaml, { filepath: 'components/securitySchemes/BearerAuth.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
}); });
it('should detect server objects', () => { it('should detect server objects', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const serverYaml = `url: https://api.example.com const serverYaml = `url: https://api.example.com
@@ -213,11 +144,11 @@ description: Production server`;
// @ts-expect-error We are testing edge cases // @ts-expect-error We are testing edge cases
const result = parser?.parse(serverYaml, { filepath: 'servers/production.yaml' }); const result = parser?.parse(serverYaml, { filepath: 'servers/production.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
}); });
it('should detect tag objects', () => { it('should detect tag objects', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const tagYaml = `name: users const tagYaml = `name: users
@@ -226,11 +157,11 @@ description: User management operations`;
// @ts-expect-error We are testing edge cases // @ts-expect-error We are testing edge cases
const result = parser?.parse(tagYaml, { filepath: 'tags/users.yaml' }); const result = parser?.parse(tagYaml, { filepath: 'tags/users.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
}); });
it('should detect external docs objects', () => { it('should detect external docs objects', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const externalDocsYaml = `url: https://example.com/docs const externalDocsYaml = `url: https://example.com/docs
@@ -239,11 +170,11 @@ description: External documentation`;
// @ts-expect-error We are testing edge cases // @ts-expect-error We are testing edge cases
const result = parser?.parse(externalDocsYaml, { filepath: 'externalDocs/api.yaml' }); const result = parser?.parse(externalDocsYaml, { filepath: 'externalDocs/api.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
}); });
it('should detect webhook objects', () => { it('should detect webhook objects', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const webhookYaml = `post: const webhookYaml = `post:
@@ -255,17 +186,17 @@ description: External documentation`;
// @ts-expect-error We are testing edge cases // @ts-expect-error We are testing edge cases
const result = parser?.parse(webhookYaml, { filepath: 'webhooks/messageCreated.yaml' }); const result = parser?.parse(webhookYaml, { filepath: 'webhooks/messageCreated.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
}); });
}); });
describe('Sorting functions', () => { describe('Sorting functions', () => {
it('should handle path sorting by specificity', () => { it('should handle path sorting by specificity', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
type: 'openapi', isOpenAPI: true,
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' }, info: { title: 'Test API', version: '1.0.0' },
@@ -280,7 +211,7 @@ description: External documentation`;
}; };
// @ts-expect-error We are testing edge cases // @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(); expect(result).toBeDefined();
if (result && typeof result === 'string') { if (result && typeof result === 'string') {
@@ -294,11 +225,11 @@ description: External documentation`;
}); });
it('should handle response code sorting', () => { it('should handle response code sorting', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
type: 'openapi', isOpenAPI: true,
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' }, info: { title: 'Test API', version: '1.0.0' },
@@ -319,7 +250,7 @@ description: External documentation`;
}; };
// @ts-expect-error We are testing edge cases // @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(); expect(result).toBeDefined();
if (result && typeof result === 'string') { if (result && typeof result === 'string') {
@@ -335,11 +266,11 @@ description: External documentation`;
describe('Context key detection', () => { describe('Context key detection', () => {
it('should handle nested path contexts', () => { it('should handle nested path contexts', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
type: 'openapi', isOpenAPI: true,
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' }, info: { title: 'Test API', version: '1.0.0' },
@@ -360,7 +291,7 @@ description: External documentation`;
}; };
// @ts-expect-error We are testing edge cases // @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(); expect(result).toBeDefined();
if (result && typeof result === 'string') { if (result && typeof result === 'string') {

View File

@@ -1,9 +1,9 @@
import { describe, expect, it } from 'bun:test'; import { describe, expect, it } from 'bun:test';
import plugin from '../src/index'; import {parsers, printers} from '../src/index';
describe('Custom Extensions Support', () => { describe('Custom Extensions Support', () => {
it('should handle custom extensions in top-level keys', () => { it('should handle custom extensions in top-level keys', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const testJson = { const testJson = {
@@ -21,7 +21,7 @@ describe('Custom Extensions Support', () => {
}); });
it('should handle custom extensions in info section', () => { it('should handle custom extensions in info section', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const testJson = { const testJson = {
@@ -43,7 +43,7 @@ describe('Custom Extensions Support', () => {
}); });
it('should handle custom extensions in operation objects', () => { it('should handle custom extensions in operation objects', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const testJson = { const testJson = {
@@ -68,7 +68,7 @@ describe('Custom Extensions Support', () => {
}); });
it('should handle custom extensions in schema objects', () => { it('should handle custom extensions in schema objects', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const testJson = { const testJson = {
@@ -95,10 +95,12 @@ describe('Custom Extensions Support', () => {
}); });
it('should format JSON with custom extensions', () => { it('should format JSON with custom extensions', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'json',
content: { content: {
'x-custom-field': 'value', 'x-custom-field': 'value',
'openapi': '3.0.0', '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 // @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).toBeDefined();
expect(result).toContain('x-custom-field'); expect(result).toContain('x-custom-field');
expect(result).toContain('openapi'); expect(result).toContain('openapi');
}); });
it('should format YAML with custom extensions', () => { it('should format YAML with custom extensions', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'yaml',
content: { content: {
'x-custom-field': 'value', 'x-custom-field': 'value',
'openapi': '3.0.0', '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 // @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).toBeDefined();
expect(result).toContain('x-custom-field:'); expect(result).toContain('x-custom-field:');
expect(result).toContain('openapi:'); expect(result).toContain('openapi:');
}); });
it('should handle unknown keys alphabetically at the end', () => { it('should handle unknown keys alphabetically at the end', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const testJson = { const testJson = {
@@ -156,10 +160,12 @@ describe('Custom Extensions Support', () => {
describe('Custom extension positioning', () => { describe('Custom extension positioning', () => {
it('should position custom extensions correctly in top-level', () => { it('should position custom extensions correctly in top-level', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'json',
content: { content: {
'x-custom-field': 'value', 'x-custom-field': 'value',
'openapi': '3.0.0', 'openapi': '3.0.0',
@@ -170,7 +176,7 @@ describe('Custom Extensions Support', () => {
}; };
// @ts-expect-error We are mocking things here // @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(); expect(result).toBeDefined();
if (!result) { if (!result) {
@@ -193,10 +199,12 @@ describe('Custom Extensions Support', () => {
}); });
it('should position custom extensions correctly in info section', () => { it('should position custom extensions correctly in info section', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'yaml',
content: { content: {
'openapi': '3.0.0', 'openapi': '3.0.0',
'info': { 'info': {
@@ -210,7 +218,7 @@ describe('Custom Extensions Support', () => {
}; };
// @ts-expect-error We are mocking things here // @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(); expect(result).toBeDefined();
if (!result) { if (!result) {
@@ -233,10 +241,12 @@ describe('Custom Extensions Support', () => {
}); });
it('should position custom extensions correctly in operation objects', () => { it('should position custom extensions correctly in operation objects', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'yaml',
content: { content: {
'openapi': '3.0.0', 'openapi': '3.0.0',
'info': { 'title': 'Test API', 'version': '1.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 // @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(); expect(result).toBeDefined();
if (!result) { if (!result) {
@@ -274,10 +284,12 @@ describe('Custom Extensions Support', () => {
}); });
it('should position custom extensions correctly in schema objects', () => { it('should position custom extensions correctly in schema objects', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'yaml',
content: { content: {
'openapi': '3.0.0', 'openapi': '3.0.0',
'info': { 'title': 'Test API', 'version': '1.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 // @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(); expect(result).toBeDefined();
if (!result) { if (!result) {
@@ -318,10 +330,12 @@ describe('Custom Extensions Support', () => {
describe('Unknown key handling', () => { describe('Unknown key handling', () => {
it('should sort unknown keys alphabetically at the end', () => { it('should sort unknown keys alphabetically at the end', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'json',
content: { content: {
'openapi': '3.0.0', 'openapi': '3.0.0',
'info': { 'title': 'Test API', 'version': '1.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 // @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(); expect(result).toBeDefined();
if (!result) { if (!result) {
@@ -358,10 +372,12 @@ describe('Custom Extensions Support', () => {
}); });
it('should handle mixed custom extensions and unknown keys', () => { it('should handle mixed custom extensions and unknown keys', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'json',
content: { content: {
'openapi': '3.0.0', 'openapi': '3.0.0',
'info': { 'title': 'Test API', 'version': '1.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 // @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(); expect(result).toBeDefined();
if (!result) { if (!result) {

View File

@@ -1,9 +1,9 @@
import { describe, expect, it } from 'bun:test'; import { describe, expect, it } from 'bun:test';
import plugin from '../src/index'; import { parsers } from '../src/index';
describe('File Detection Tests', () => { describe('File Detection Tests', () => {
it('should detect OpenAPI root files', () => { it('should detect OpenAPI root files', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const testYaml = `openapi: 3.0.0 const testYaml = `openapi: 3.0.0
@@ -21,13 +21,13 @@ paths:
const result = parser?.parse(testYaml, { filepath: 'openapi.yaml' }); const result = parser?.parse(testYaml, { filepath: 'openapi.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml'); expect(result?.format).toBe('yaml');
expect(result?.content.openapi).toBe('3.0.0'); expect(result?.content.openapi).toBe('3.0.0');
}); });
it('should detect partial schema files', () => { it('should detect partial schema files', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const schemaYaml = `type: object const schemaYaml = `type: object
@@ -44,13 +44,13 @@ required:
const result = parser?.parse(schemaYaml, { filepath: 'components/schemas/User.yaml' }); const result = parser?.parse(schemaYaml, { filepath: 'components/schemas/User.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml'); expect(result?.format).toBe('yaml');
expect(result?.content.type).toBe('object'); expect(result?.content.type).toBe('object');
}); });
it('should detect parameter files', () => { it('should detect parameter files', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const parameterYaml = `name: id const parameterYaml = `name: id
@@ -64,13 +64,13 @@ schema:
const result = parser?.parse(parameterYaml, { filepath: 'components/parameters/UserId.yaml' }); const result = parser?.parse(parameterYaml, { filepath: 'components/parameters/UserId.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml'); expect(result?.format).toBe('yaml');
expect(result?.content.name).toBe('id'); expect(result?.content.name).toBe('id');
}); });
it('should detect response files', () => { it('should detect response files', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const responseYaml = `description: User response const responseYaml = `description: User response
@@ -88,13 +88,13 @@ content:
const result = parser?.parse(responseYaml, { filepath: 'components/responses/UserResponse.yaml' }); const result = parser?.parse(responseYaml, { filepath: 'components/responses/UserResponse.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml'); expect(result?.format).toBe('yaml');
expect(result?.content.description).toBe('User response'); expect(result?.content.description).toBe('User response');
}); });
it('should detect path files', () => { it('should detect path files', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const pathYaml = `get: const pathYaml = `get:
@@ -114,14 +114,14 @@ post:
const result = parser?.parse(pathYaml, { filepath: 'paths/users.yaml' }); const result = parser?.parse(pathYaml, { filepath: 'paths/users.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml'); expect(result?.format).toBe('yaml');
expect(result?.content.get).toBeDefined(); expect(result?.content.get).toBeDefined();
expect(result?.content.post).toBeDefined(); expect(result?.content.post).toBeDefined();
}); });
it('should detect security scheme files', () => { it('should detect security scheme files', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const securityYaml = `type: http const securityYaml = `type: http
@@ -133,25 +133,34 @@ description: JWT authentication`;
const result = parser?.parse(securityYaml, { filepath: 'components/securitySchemes/BearerAuth.yaml' }); const result = parser?.parse(securityYaml, { filepath: 'components/securitySchemes/BearerAuth.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml'); expect(result?.format).toBe('yaml');
expect(result?.content.type).toBe('http'); expect(result?.content.type).toBe('http');
}); });
it('should reject non-OpenAPI files', () => { it('should handle non-OpenAPI files', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const nonOpenAPIYaml = `name: John const nonOpenAPIYaml = `name: John
age: 30 age: 30
city: New York`; city: New York`;
const parsedYaml = {
name: 'John',
age: 30,
city: 'New York'
}
// @ts-expect-error We are mocking things here // @ts-expect-error We are mocking things here
expect(() => parser?.parse(nonOpenAPIYaml, { filepath: 'config/data.yaml' })).toThrow('Not an OpenAPI file'); const parsedData = parser?.parse(nonOpenAPIYaml, { filepath: 'config/data.yaml' })
expect(parsedData).toBeDefined()
expect(parsedData?.isOpenAPI).toBeFalse();
}); });
it('should accept files in OpenAPI directories even with simple content', () => { it('should accept files in OpenAPI directories even with simple content', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const simpleYaml = `name: John const simpleYaml = `name: John
@@ -161,12 +170,12 @@ city: New York`;
// @ts-expect-error We are mocking things here // @ts-expect-error We are mocking things here
const result = parser?.parse(simpleYaml, { filepath: 'components/schemas/User.yaml' }); const result = parser?.parse(simpleYaml, { filepath: 'components/schemas/User.yaml' });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml'); expect(result?.format).toBe('yaml');
}); });
it('should support component directory patterns', () => { it('should support component directory patterns', () => {
const parser = plugin.parsers?.['openapi-parser']; const parser = parsers?.['openapi-parser'];
expect(parser).toBeDefined(); expect(parser).toBeDefined();
const componentYaml = `type: object const componentYaml = `type: object
@@ -193,7 +202,7 @@ properties:
// @ts-expect-error We are mocking things here // @ts-expect-error We are mocking things here
const result = parser?.parse(componentYaml, { filepath: path }); const result = parser?.parse(componentYaml, { filepath: path });
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result?.type).toBe('openapi'); expect(result?.isOpenAPI).toBeTrue();
expect(result?.format).toBe('yaml'); expect(result?.format).toBe('yaml');
}); });
}); });

View File

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

View File

@@ -1,14 +1,15 @@
import { describe, it, expect } from 'bun:test'; import { describe, it, expect } from 'bun:test';
import plugin from '../src/index'; import { printers } from '../src/index';
describe('Key Ordering Tests', () => { describe('Key Ordering Tests', () => {
describe('Info section key ordering', () => { describe('Info section key ordering', () => {
it('should sort info keys correctly', () => { it('should sort info keys correctly', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
type: 'openapi', isOpenAPI: true,
format: 'json',
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { info: {
@@ -21,10 +22,9 @@ describe('Key Ordering Tests', () => {
} }
}, },
originalText: '', originalText: '',
format: 'json'
}; };
// @ts-expect-error We are mocking things here so we don't need to pass a print function // @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).toBeDefined();
if (!result) { if (!result) {
@@ -51,11 +51,12 @@ describe('Key Ordering Tests', () => {
describe('Operation key ordering', () => { describe('Operation key ordering', () => {
it('should sort operation keys correctly', () => { it('should sort operation keys correctly', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
type: 'openapi', isOpenAPI: true,
format: 'yaml',
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' }, info: { title: 'Test API', version: '1.0.0' },
@@ -77,12 +78,11 @@ describe('Key Ordering Tests', () => {
} }
} }
}, },
originalText: '', originalText: ''
format: 'json'
}; };
// @ts-expect-error We are mocking things here so we don't need to pass a print function // @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).toBeDefined();
if (!result) { if (!result) {
@@ -119,10 +119,12 @@ describe('Key Ordering Tests', () => {
describe('Schema key ordering', () => { describe('Schema key ordering', () => {
it('should sort schema keys correctly', () => { it('should sort schema keys correctly', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'yaml',
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { title: 'Test API', version: '1.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 // @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(); expect(result).toBeDefined();
if (!result) { if (!result) {
@@ -247,10 +250,12 @@ describe('Key Ordering Tests', () => {
describe('Response key ordering', () => { describe('Response key ordering', () => {
it('should sort response keys correctly', () => { it('should sort response keys correctly', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'json',
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { title: 'Test API', version: '1.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 // @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).toBeDefined();
if (!result) { if (!result) {
@@ -294,10 +299,12 @@ describe('Key Ordering Tests', () => {
describe('Parameter key ordering', () => { describe('Parameter key ordering', () => {
it('should sort parameter keys correctly', () => { it('should sort parameter keys correctly', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'yaml',
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { title: 'Test API', version: '1.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 // @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).toBeDefined();
if (!result) { if (!result) {
@@ -366,10 +373,12 @@ describe('Key Ordering Tests', () => {
describe('Security scheme key ordering', () => { describe('Security scheme key ordering', () => {
it('should sort security scheme keys correctly', () => { it('should sort security scheme keys correctly', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'yaml',
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { title: 'Test API', version: '1.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 // @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).toBeDefined();
if (!result) { if (!result) {
@@ -426,10 +435,12 @@ describe('Key Ordering Tests', () => {
describe('Server key ordering', () => { describe('Server key ordering', () => {
it('should sort server keys correctly', () => { it('should sort server keys correctly', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'json',
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' }, info: { title: 'Test API', version: '1.0.0' },
@@ -443,7 +454,7 @@ describe('Key Ordering Tests', () => {
} }
}; };
// @ts-expect-error We are mocking things here so we don't need to pass a print function // @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).toBeDefined();
if (!result) { if (!result) {
@@ -465,10 +476,12 @@ describe('Key Ordering Tests', () => {
describe('Tag key ordering', () => { describe('Tag key ordering', () => {
it('should sort tag keys correctly', () => { it('should sort tag keys correctly', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'json',
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' }, info: { title: 'Test API', version: '1.0.0' },
@@ -482,7 +495,7 @@ describe('Key Ordering Tests', () => {
} }
}; };
// @ts-expect-error We are mocking things here so we don't need to pass a print function // @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).toBeDefined();
if (!result) { if (!result) {
@@ -502,10 +515,12 @@ describe('Key Ordering Tests', () => {
describe('External docs key ordering', () => { describe('External docs key ordering', () => {
it('should sort external docs keys correctly', () => { it('should sort external docs keys correctly', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'json',
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' }, info: { title: 'Test API', version: '1.0.0' },
@@ -516,7 +531,7 @@ describe('Key Ordering Tests', () => {
} }
}; };
// @ts-expect-error We are mocking things here so we don't need to pass a print function // @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).toBeDefined();
if (!result) { if (!result) {
@@ -535,10 +550,12 @@ describe('Key Ordering Tests', () => {
describe('Path sorting', () => { describe('Path sorting', () => {
it('should sort paths by specificity', () => { it('should sort paths by specificity', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'json',
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' }, info: { title: 'Test API', version: '1.0.0' },
@@ -551,7 +568,7 @@ describe('Key Ordering Tests', () => {
} }
}; };
// @ts-expect-error We are mocking things here so we don't need to pass a print function // @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).toBeDefined();
if (!result) { if (!result) {
@@ -574,10 +591,12 @@ describe('Key Ordering Tests', () => {
describe('Response code sorting', () => { describe('Response code sorting', () => {
it('should sort response codes numerically', () => { it('should sort response codes numerically', () => {
const printer = plugin.printers?.['openapi-ast']; const printer = printers?.['openapi-ast'];
expect(printer).toBeDefined(); expect(printer).toBeDefined();
const testData = { const testData = {
isOpenAPI: true,
format: 'json',
content: { content: {
openapi: '3.0.0', openapi: '3.0.0',
info: { title: 'Test API', version: '1.0.0' }, info: { title: 'Test API', version: '1.0.0' },
@@ -597,7 +616,7 @@ describe('Key Ordering Tests', () => {
} }
}; };
// @ts-expect-error We are mocking things here so we don't need to pass a print function // @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).toBeDefined();
if (!result) { if (!result) {

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 // @ts-expect-error We are mocking things here
const parsed = parser?.parse(inputJson, { filepath: 'test.json' }); const parsed = parser?.parse(inputJson, { filepath: 'test.json' });
expect(parsed).toBeDefined(); expect(parsed).toBeDefined();
expect(parsed?.type).toBe('openapi'); expect(parsed?.isOpenAPI).toBeTrue();
expect(parsed?.format).toBe('json'); expect(parsed?.format).toBe('json');
expect(parsed?.content).toBeDefined(); expect(parsed?.content).toBeDefined();
// Format the parsed content // Format the parsed content
// @ts-expect-error We are mocking things here // @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).toBeDefined();
expect(result).toContain('"openapi"'); expect(result).toContain('"openapi"');
@@ -80,13 +80,13 @@ openapi: 3.0.0`;
// @ts-expect-error We are mocking things here // @ts-expect-error We are mocking things here
const parsed = parser?.parse(inputYaml, { filepath: 'test.yaml' }); const parsed = parser?.parse(inputYaml, { filepath: 'test.yaml' });
expect(parsed).toBeDefined(); expect(parsed).toBeDefined();
expect(parsed?.type).toBe('openapi'); expect(parsed?.isOpenAPI).toBeTrue();
expect(parsed?.format).toBe('yaml'); expect(parsed?.format).toBe('yaml');
expect(parsed?.content).toBeDefined(); expect(parsed?.content).toBeDefined();
// Format the parsed content // Format the parsed content
// @ts-expect-error We are mocking things here // @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).toBeDefined();
expect(result).toContain('openapi:'); expect(result).toContain('openapi:');
@@ -148,13 +148,13 @@ openapi: 3.0.0`;
// @ts-expect-error We are mocking things here // @ts-expect-error We are mocking things here
const parsed = parser?.parse(inputJson, { filepath: 'test.json' }); const parsed = parser?.parse(inputJson, { filepath: 'test.json' });
expect(parsed).toBeDefined(); expect(parsed).toBeDefined();
expect(parsed?.type).toBe('openapi'); expect(parsed?.isOpenAPI).toBeTrue();
expect(parsed?.format).toBe('json'); expect(parsed?.format).toBe('json');
expect(parsed?.content).toBeDefined(); expect(parsed?.content).toBeDefined();
// Format the parsed content // Format the parsed content
// @ts-expect-error We are mocking things here // @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).toBeDefined();
expect(result).toContain('"swagger"'); expect(result).toContain('"swagger"');
@@ -201,13 +201,13 @@ describe('Key Ordering Tests', () => {
// @ts-expect-error We are mocking things here // @ts-expect-error We are mocking things here
const parsed = parser?.parse(inputJson, { filepath: 'test.json' }); const parsed = parser?.parse(inputJson, { filepath: 'test.json' });
expect(parsed).toBeDefined(); expect(parsed).toBeDefined();
expect(parsed?.type).toBe('openapi'); expect(parsed?.isOpenAPI).toBeTrue();
expect(parsed?.format).toBe('json'); expect(parsed?.format).toBe('json');
expect(parsed?.content).toBeDefined(); expect(parsed?.content).toBeDefined();
// Format the parsed content // Format the parsed content
// @ts-expect-error We are mocking things here // @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).toBeDefined();
@@ -261,13 +261,13 @@ describe('Key Ordering Tests', () => {
// @ts-expect-error We are mocking things here // @ts-expect-error We are mocking things here
const parsed = parser?.parse(inputJson, { filepath: 'test.json' }); const parsed = parser?.parse(inputJson, { filepath: 'test.json' });
expect(parsed).toBeDefined(); expect(parsed).toBeDefined();
expect(parsed?.type).toBe('openapi'); expect(parsed?.isOpenAPI).toBeTrue();
expect(parsed?.format).toBe('json'); expect(parsed?.format).toBe('json');
expect(parsed?.content).toBeDefined(); expect(parsed?.content).toBeDefined();
// Format the parsed content // Format the parsed content
// @ts-expect-error We are mocking things here // @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).toBeDefined();

View File

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