mirror of
https://github.com/LukeHagar/prettier-plugin-openapi.git
synced 2025-12-06 12:47:47 +00:00
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:
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
285
src/index.ts
285
src/index.ts
@@ -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,
|
ComponentsKeys,
|
||||||
InfoKeys,
|
ContactKeys,
|
||||||
ContactKeys,
|
DiscriminatorKeys,
|
||||||
LicenseKeys,
|
EncodingKeys,
|
||||||
ComponentsKeys,
|
ExampleKeys,
|
||||||
OperationKeys,
|
ExternalDocsKeys,
|
||||||
ParameterKeys,
|
HeaderKeys,
|
||||||
SchemaKeys,
|
InfoKeys,
|
||||||
ResponseKeys,
|
LicenseKeys,
|
||||||
SecuritySchemeKeys,
|
LinkKeys,
|
||||||
OAuthFlowKeys,
|
MediaTypeKeys,
|
||||||
ServerKeys,
|
OAuthFlowKeys,
|
||||||
ServerVariableKeys,
|
OperationKeys,
|
||||||
TagKeys,
|
ParameterKeys,
|
||||||
ExternalDocsKeys,
|
PathItemKeys,
|
||||||
WebhookKeys,
|
RequestBodyKeys,
|
||||||
PathItemKeys,
|
ResponseKeys,
|
||||||
RequestBodyKeys,
|
RootKeys,
|
||||||
MediaTypeKeys,
|
SchemaKeys,
|
||||||
EncodingKeys,
|
SecuritySchemeKeys,
|
||||||
HeaderKeys,
|
ServerKeys,
|
||||||
LinkKeys,
|
ServerVariableKeys,
|
||||||
ExampleKeys,
|
TagKeys,
|
||||||
DiscriminatorKeys,
|
WebhookKeys,
|
||||||
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');
|
let format: 'json' | 'yaml' | undefined;
|
||||||
const isJsonFile = filePath.endsWith('.json');
|
|
||||||
|
if (options?.filepath) {
|
||||||
// If we can't determine from extension, try to detect from content
|
switch (true) {
|
||||||
let format: 'json' | 'yaml' = 'json'; // default to JSON
|
case options?.filepath.endsWith('.yaml') || options?.filepath.endsWith('.yml'):
|
||||||
if (isYamlFile) {
|
format = 'yaml';
|
||||||
format = 'yaml';
|
break;
|
||||||
} else if (isJsonFile) {
|
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('[')) {
|
||||||
@@ -79,35 +80,61 @@ function parseOpenAPIFile(text: string, options?: any): OpenAPINode {
|
|||||||
format = 'yaml';
|
format = 'yaml';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
let parsed: any;
|
||||||
let parsed: any;
|
|
||||||
|
switch (format) {
|
||||||
if (format === 'json') {
|
case 'yaml':
|
||||||
parsed = JSON.parse(text);
|
try {
|
||||||
} else {
|
|
||||||
parsed = yaml.load(text, {
|
parsed = yaml.load(text, {
|
||||||
schema: yaml.DEFAULT_SCHEMA,
|
schema: yaml.DEFAULT_SCHEMA,
|
||||||
onWarning: (warning) => {
|
onWarning: (warning) => {
|
||||||
// Handle YAML warnings if needed
|
// Handle YAML warnings if needed
|
||||||
console.warn('YAML parsing warning:', warning);
|
console.warn('YAML parsing warning:', warning);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to parse OpenAPI YAML: ${error}`);
|
||||||
// Check if this is an OpenAPI file
|
}
|
||||||
if (!isOpenAPIFile(parsed, filePath)) {
|
break;
|
||||||
throw new Error('Not an OpenAPI file');
|
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 {
|
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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,17 +159,17 @@ function isOpenAPIFile(content: any, filePath?: string): boolean {
|
|||||||
// Only accept files in OpenAPI-related directories
|
// Only accept files in OpenAPI-related directories
|
||||||
if (filePath) {
|
if (filePath) {
|
||||||
const path = filePath.toLowerCase();
|
const path = filePath.toLowerCase();
|
||||||
|
|
||||||
// Check for component directory patterns
|
// Check for component directory patterns
|
||||||
if (path.includes('/components/') ||
|
if (path.includes('/components/') ||
|
||||||
path.includes('/schemas/') ||
|
path.includes('/schemas/') ||
|
||||||
path.includes('/parameters/') ||
|
path.includes('/parameters/') ||
|
||||||
path.includes('/responses/') ||
|
path.includes('/responses/') ||
|
||||||
path.includes('/requestbodies/') ||
|
path.includes('/requestbodies/') ||
|
||||||
path.includes('/headers/') ||
|
path.includes('/headers/') ||
|
||||||
path.includes('/examples/') ||
|
path.includes('/examples/') ||
|
||||||
path.includes('/securityschemes/') ||
|
path.includes('/securityschemes/') ||
|
||||||
path.includes('/links/') ||
|
path.includes('/links/') ||
|
||||||
path.includes('/callbacks/') ||
|
path.includes('/callbacks/') ||
|
||||||
path.includes('/webhooks/') ||
|
path.includes('/webhooks/') ||
|
||||||
path.includes('/paths/')) {
|
path.includes('/paths/')) {
|
||||||
@@ -204,11 +231,11 @@ function isOpenAPIFile(content: any, filePath?: string): boolean {
|
|||||||
// Additional strict check: reject objects that look like generic data
|
// 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
|
// If an object only has simple properties like name, age, etc. without any OpenAPI structure, reject it
|
||||||
const keys = Object.keys(content);
|
const keys = Object.keys(content);
|
||||||
const hasOnlyGenericProperties = keys.every(key =>
|
const hasOnlyGenericProperties = keys.every(key =>
|
||||||
!key.startsWith('x-') && // Not a custom extension
|
!key.startsWith('x-') && // Not a custom extension
|
||||||
!['openapi', 'swagger', 'info', 'paths', 'components', 'definitions', 'parameters', 'responses', 'securityDefinitions', 'tags', 'servers', 'webhooks'].includes(key)
|
!['openapi', 'swagger', 'info', 'paths', 'components', 'definitions', 'parameters', 'responses', 'securityDefinitions', 'tags', 'servers', 'webhooks'].includes(key)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (hasOnlyGenericProperties) {
|
if (hasOnlyGenericProperties) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -229,36 +256,40 @@ 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: [
|
// Accepting all JSON and YAML files so that component files used by $ref work
|
||||||
// Accepting all JSON and YAML files so that component files used by $ref work
|
'.json', '.yaml', '.yml'
|
||||||
'.json', '.yaml', '.yml'
|
],
|
||||||
],
|
parsers: ['openapi-parser'],
|
||||||
parsers: ['openapi-parser'],
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const parsers: Record<string, Parser> = {
|
||||||
|
'openapi-parser': {
|
||||||
|
parse: (text: string, options?: any): OpenAPINode => {
|
||||||
|
return parseOpenAPIFile(text, options);
|
||||||
},
|
},
|
||||||
],
|
astFormat: 'openapi-ast',
|
||||||
parsers: {
|
locStart: (node: OpenAPINode) => 0,
|
||||||
'openapi-parser': {
|
locEnd: (node: OpenAPINode) => node.originalText?.length || 0,
|
||||||
parse: (text: string, options?: any): OpenAPINode => {
|
},
|
||||||
return parseOpenAPIFile(text, options);
|
}
|
||||||
},
|
|
||||||
astFormat: 'openapi-ast',
|
export const printers: Record<string, Printer> = {
|
||||||
locStart: (node: OpenAPINode) => 0,
|
'openapi-ast': {
|
||||||
locEnd: (node: OpenAPINode) => node.originalText?.length || 0,
|
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
|
* 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
|
// 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,
|
||||||
lineWidth: options?.printWidth || 80,
|
lineWidth: options?.printWidth || 80,
|
||||||
noRefs: true,
|
noRefs: true,
|
||||||
quotingType: '"',
|
quotingType: '"',
|
||||||
forceQuotes: false,
|
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 {
|
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;
|
||||||
@@ -316,7 +339,7 @@ function sortOpenAPIKeysEnhanced(obj: any, path: string = ''): any {
|
|||||||
if (typeof obj !== 'object' || obj === null) {
|
if (typeof obj !== 'object' || obj === null) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle arrays by recursively sorting each element
|
// Handle arrays by recursively sorting each element
|
||||||
if (Array.isArray(obj)) {
|
if (Array.isArray(obj)) {
|
||||||
return obj.map((item, index) => sortOpenAPIKeysEnhanced(item, `${path}[${index}]`));
|
return obj.map((item, index) => sortOpenAPIKeysEnhanced(item, `${path}[${index}]`));
|
||||||
@@ -389,11 +412,11 @@ function isSchemaObject(obj: any): boolean {
|
|||||||
if (!obj || typeof obj !== 'object') {
|
if (!obj || typeof obj !== 'object') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for JSON Schema keywords - be very strict
|
// 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 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);
|
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
|
// Only return true if we have clear schema indicators
|
||||||
// Must have either schema keywords OR valid type with schema properties
|
// Must have either schema keywords OR valid type with schema properties
|
||||||
// Also require additional schema-specific properties to be more strict
|
// Also require additional schema-specific properties to be more strict
|
||||||
@@ -414,8 +437,8 @@ function isServerObject(obj: any): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isTagObject(obj: any): boolean {
|
function isTagObject(obj: any): boolean {
|
||||||
return obj && typeof obj === 'object' && 'name' in obj && typeof obj.name === 'string' &&
|
return obj && typeof obj === 'object' && 'name' in obj && typeof obj.name === 'string' &&
|
||||||
(Object.keys(obj).length === 1 || // Only name
|
(Object.keys(obj).length === 1 || // Only name
|
||||||
'description' in obj || // name + description
|
'description' in obj || // name + description
|
||||||
'externalDocs' in obj); // name + externalDocs
|
'externalDocs' in obj); // name + externalDocs
|
||||||
}
|
}
|
||||||
@@ -546,7 +569,7 @@ function getContextKey(path: string, obj: any): string {
|
|||||||
if (path === 'webhooks') return 'webhook';
|
if (path === 'webhooks') return 'webhook';
|
||||||
if (path === 'definitions') return 'definitions';
|
if (path === 'definitions') return 'definitions';
|
||||||
if (path === 'securityDefinitions') return 'securityDefinitions';
|
if (path === 'securityDefinitions') return 'securityDefinitions';
|
||||||
|
|
||||||
// Check if this is a path operation (e.g., "paths./users.get")
|
// Check if this is a path operation (e.g., "paths./users.get")
|
||||||
if (path.includes('.') && path.split('.').length >= 3) {
|
if (path.includes('.') && path.split('.').length >= 3) {
|
||||||
const pathParts = path.split('.');
|
const pathParts = path.split('.');
|
||||||
@@ -648,6 +671,4 @@ function getStandardKeysForContext(contextKey: string): readonly string[] {
|
|||||||
case 'securityDefinitions': return SecuritySchemeKeys; // Security definitions use security scheme keys
|
case 'securityDefinitions': return SecuritySchemeKeys; // Security definitions use security scheme keys
|
||||||
default: return RootKeys;
|
default: return RootKeys;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default plugin;
|
|
||||||
@@ -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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -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,9 +211,9 @@ 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') {
|
||||||
const formatted = JSON.parse(result);
|
const formatted = JSON.parse(result);
|
||||||
const pathKeys = Object.keys(formatted.paths);
|
const pathKeys = Object.keys(formatted.paths);
|
||||||
@@ -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,9 +250,9 @@ 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') {
|
||||||
const formatted = JSON.parse(result);
|
const formatted = JSON.parse(result);
|
||||||
const responseKeys = Object.keys(formatted.paths['/test'].get.responses);
|
const responseKeys = Object.keys(formatted.paths['/test'].get.responses);
|
||||||
@@ -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,9 +291,9 @@ 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') {
|
||||||
const formatted = JSON.parse(result);
|
const formatted = JSON.parse(result);
|
||||||
expect(formatted.components.schemas.User).toBeDefined();
|
expect(formatted.components.schemas.User).toBeDefined();
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
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
|
||||||
info:
|
info:
|
||||||
title: Test API
|
title: Test API
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
@@ -17,20 +17,20 @@ paths:
|
|||||||
'200':
|
'200':
|
||||||
description: Success`;
|
description: Success`;
|
||||||
|
|
||||||
// @ts-expect-error We are mocking things here
|
// @ts-expect-error We are mocking things here
|
||||||
const result = parser?.parse(testYaml, { filepath: 'openapi.yaml' });
|
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');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect partial schema files', () => {
|
expect(result).toBeDefined();
|
||||||
const parser = plugin.parsers?.['openapi-parser'];
|
expect(result?.isOpenAPI).toBeTrue();
|
||||||
expect(parser).toBeDefined();
|
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:
|
properties:
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
@@ -40,40 +40,40 @@ required:
|
|||||||
- id
|
- id
|
||||||
- name`;
|
- name`;
|
||||||
|
|
||||||
// @ts-expect-error We are mocking things here
|
// @ts-expect-error We are mocking things here
|
||||||
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?.type).toBe('openapi');
|
|
||||||
expect(result?.format).toBe('yaml');
|
|
||||||
expect(result?.content.type).toBe('object');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect parameter files', () => {
|
expect(result).toBeDefined();
|
||||||
const parser = plugin.parsers?.['openapi-parser'];
|
expect(result?.isOpenAPI).toBeTrue();
|
||||||
expect(parser).toBeDefined();
|
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
|
in: path
|
||||||
required: true
|
required: true
|
||||||
description: User ID
|
description: User ID
|
||||||
schema:
|
schema:
|
||||||
type: integer`;
|
type: integer`;
|
||||||
|
|
||||||
// @ts-expect-error We are mocking things here
|
// @ts-expect-error We are mocking things here
|
||||||
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?.type).toBe('openapi');
|
|
||||||
expect(result?.format).toBe('yaml');
|
|
||||||
expect(result?.content.name).toBe('id');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect response files', () => {
|
expect(result).toBeDefined();
|
||||||
const parser = plugin.parsers?.['openapi-parser'];
|
expect(result?.isOpenAPI).toBeTrue();
|
||||||
expect(parser).toBeDefined();
|
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:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
@@ -84,20 +84,20 @@ content:
|
|||||||
name:
|
name:
|
||||||
type: string`;
|
type: string`;
|
||||||
|
|
||||||
// @ts-expect-error We are mocking things here
|
// @ts-expect-error We are mocking things here
|
||||||
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?.type).toBe('openapi');
|
|
||||||
expect(result?.format).toBe('yaml');
|
|
||||||
expect(result?.content.description).toBe('User response');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect path files', () => {
|
expect(result).toBeDefined();
|
||||||
const parser = plugin.parsers?.['openapi-parser'];
|
expect(result?.isOpenAPI).toBeTrue();
|
||||||
expect(parser).toBeDefined();
|
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
|
summary: Get users
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
@@ -110,91 +110,100 @@ post:
|
|||||||
schema:
|
schema:
|
||||||
type: object`;
|
type: object`;
|
||||||
|
|
||||||
// @ts-expect-error We are mocking things here
|
// @ts-expect-error We are mocking things here
|
||||||
const result = parser?.parse(pathYaml, { filepath: 'paths/users.yaml' });
|
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();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should detect security scheme files', () => {
|
expect(result).toBeDefined();
|
||||||
const parser = plugin.parsers?.['openapi-parser'];
|
expect(result?.isOpenAPI).toBeTrue();
|
||||||
expect(parser).toBeDefined();
|
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
|
scheme: bearer
|
||||||
bearerFormat: JWT
|
bearerFormat: JWT
|
||||||
description: JWT authentication`;
|
description: JWT authentication`;
|
||||||
|
|
||||||
// @ts-expect-error We are mocking things here
|
// @ts-expect-error We are mocking things here
|
||||||
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?.type).toBe('openapi');
|
|
||||||
expect(result?.format).toBe('yaml');
|
|
||||||
expect(result?.content.type).toBe('http');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reject non-OpenAPI files', () => {
|
expect(result).toBeDefined();
|
||||||
const parser = plugin.parsers?.['openapi-parser'];
|
expect(result?.isOpenAPI).toBeTrue();
|
||||||
expect(parser).toBeDefined();
|
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
|
age: 30
|
||||||
city: New York`;
|
city: New York`;
|
||||||
|
|
||||||
// @ts-expect-error We are mocking things here
|
const parsedYaml = {
|
||||||
expect(() => parser?.parse(nonOpenAPIYaml, { filepath: 'config/data.yaml' })).toThrow('Not an OpenAPI file');
|
name: 'John',
|
||||||
});
|
age: 30,
|
||||||
|
city: 'New York'
|
||||||
|
}
|
||||||
|
|
||||||
it('should accept files in OpenAPI directories even with simple content', () => {
|
// @ts-expect-error We are mocking things here
|
||||||
const parser = plugin.parsers?.['openapi-parser'];
|
const parsedData = parser?.parse(nonOpenAPIYaml, { filepath: 'config/data.yaml' })
|
||||||
expect(parser).toBeDefined();
|
|
||||||
|
|
||||||
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
|
age: 30
|
||||||
city: New York`;
|
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
|
||||||
properties:
|
properties:
|
||||||
message:
|
message:
|
||||||
type: string`;
|
type: string`;
|
||||||
|
|
||||||
// Test various component directory patterns
|
// Test various component directory patterns
|
||||||
const paths = [
|
const paths = [
|
||||||
'components/schemas/Error.yaml',
|
'components/schemas/Error.yaml',
|
||||||
'components/parameters/CommonPagination.yaml',
|
'components/parameters/CommonPagination.yaml',
|
||||||
'components/responses/ErrorResponse.yaml',
|
'components/responses/ErrorResponse.yaml',
|
||||||
'components/requestBodies/UserCreateBody.yaml',
|
'components/requestBodies/UserCreateBody.yaml',
|
||||||
'components/headers/RateLimitHeaders.yaml',
|
'components/headers/RateLimitHeaders.yaml',
|
||||||
'components/examples/UserExample.yaml',
|
'components/examples/UserExample.yaml',
|
||||||
'components/securitySchemes/BearerAuth.yaml',
|
'components/securitySchemes/BearerAuth.yaml',
|
||||||
'components/links/UserCreatedLink.yaml',
|
'components/links/UserCreatedLink.yaml',
|
||||||
'components/callbacks/NewMessageCallback.yaml',
|
'components/callbacks/NewMessageCallback.yaml',
|
||||||
'webhooks/messageCreated.yaml',
|
'webhooks/messageCreated.yaml',
|
||||||
'paths/users.yaml'
|
'paths/users.yaml'
|
||||||
];
|
];
|
||||||
|
|
||||||
paths.forEach(path => {
|
paths.forEach(path => {
|
||||||
// @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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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) {
|
||||||
@@ -32,7 +32,7 @@ describe('Key Ordering Tests', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resultString = result.toString();
|
const resultString = result.toString();
|
||||||
|
|
||||||
// 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');
|
||||||
@@ -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) {
|
||||||
@@ -90,7 +90,7 @@ describe('Key Ordering Tests', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resultString = result.toString();
|
const resultString = result.toString();
|
||||||
|
|
||||||
// Check that operation keys appear in the correct order
|
// Check that operation keys appear in the correct order
|
||||||
const summaryIndex = resultString.indexOf('summary');
|
const summaryIndex = resultString.indexOf('summary');
|
||||||
const operationIdIndex = resultString.indexOf('operationId');
|
const operationIdIndex = resultString.indexOf('operationId');
|
||||||
@@ -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) {
|
||||||
@@ -177,7 +180,7 @@ describe('Key Ordering Tests', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resultString = result.toString();
|
const resultString = result.toString();
|
||||||
|
|
||||||
const typeIndex = resultString.indexOf('type:');
|
const typeIndex = resultString.indexOf('type:');
|
||||||
const formatIndex = resultString.indexOf('format:');
|
const formatIndex = resultString.indexOf('format:');
|
||||||
const titleIndex = resultString.indexOf('title:');
|
const titleIndex = resultString.indexOf('title:');
|
||||||
@@ -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) {
|
||||||
@@ -335,7 +342,7 @@ describe('Key Ordering Tests', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resultString = result.toString();
|
const resultString = result.toString();
|
||||||
|
|
||||||
const nameIndex = resultString.indexOf('name:');
|
const nameIndex = resultString.indexOf('name:');
|
||||||
const inIndex = resultString.indexOf('in:');
|
const inIndex = resultString.indexOf('in:');
|
||||||
const descriptionIndex = resultString.indexOf('description:');
|
const descriptionIndex = resultString.indexOf('description:');
|
||||||
@@ -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' },
|
||||||
@@ -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
|
// @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) {
|
||||||
@@ -451,7 +462,7 @@ describe('Key Ordering Tests', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resultString = result.toString();
|
const resultString = result.toString();
|
||||||
|
|
||||||
const nameIndex = resultString.indexOf('name');
|
const nameIndex = resultString.indexOf('name');
|
||||||
const descriptionIndex = resultString.indexOf('description');
|
const descriptionIndex = resultString.indexOf('description');
|
||||||
const urlIndex = resultString.indexOf('url');
|
const urlIndex = resultString.indexOf('url');
|
||||||
@@ -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' },
|
||||||
@@ -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
|
// @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) {
|
||||||
@@ -490,7 +503,7 @@ describe('Key Ordering Tests', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resultString = result.toString();
|
const resultString = result.toString();
|
||||||
|
|
||||||
const nameIndex = resultString.indexOf('name');
|
const nameIndex = resultString.indexOf('name');
|
||||||
const descriptionIndex = resultString.indexOf('description');
|
const descriptionIndex = resultString.indexOf('description');
|
||||||
const externalDocsIndex = resultString.indexOf('externalDocs');
|
const externalDocsIndex = resultString.indexOf('externalDocs');
|
||||||
@@ -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' },
|
||||||
@@ -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
|
// @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) {
|
||||||
@@ -524,7 +539,7 @@ describe('Key Ordering Tests', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resultString = result.toString();
|
const resultString = result.toString();
|
||||||
|
|
||||||
// Check that external docs keys appear in the correct order
|
// Check that external docs keys appear in the correct order
|
||||||
const descriptionIndex = resultString.indexOf('description');
|
const descriptionIndex = resultString.indexOf('description');
|
||||||
const urlIndex = resultString.indexOf('url');
|
const urlIndex = resultString.indexOf('url');
|
||||||
@@ -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' },
|
||||||
@@ -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
|
// @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) {
|
||||||
@@ -559,7 +576,7 @@ describe('Key Ordering Tests', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resultString = result.toString();
|
const resultString = result.toString();
|
||||||
|
|
||||||
// Check that paths are sorted by specificity (fewer parameters first)
|
// Check that paths are sorted by specificity (fewer parameters first)
|
||||||
const usersIndex = resultString.indexOf('/users');
|
const usersIndex = resultString.indexOf('/users');
|
||||||
const usersIdIndex = resultString.indexOf('/users/{id}');
|
const usersIdIndex = resultString.indexOf('/users/{id}');
|
||||||
@@ -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' },
|
||||||
@@ -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
|
// @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) {
|
||||||
@@ -605,7 +624,7 @@ describe('Key Ordering Tests', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resultString = result.toString();
|
const resultString = result.toString();
|
||||||
|
|
||||||
// Check that response codes are sorted numerically
|
// Check that response codes are sorted numerically
|
||||||
const code200Index = resultString.indexOf('200');
|
const code200Index = resultString.indexOf('200');
|
||||||
const code400Index = resultString.indexOf('400');
|
const code400Index = resultString.indexOf('400');
|
||||||
|
|||||||
@@ -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
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
@@ -135,8 +141,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');
|
||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user