From 1d43f13d2e5f3dfc7f70014e407a1bca8022752a Mon Sep 17 00:00:00 2001 From: Vladimir Gorej Date: Tue, 10 Oct 2023 13:51:41 +0200 Subject: [PATCH] feat(ns-openapi-2): add support for Items Object Refs #3097 --- package-lock.json | 1 + packages/apidom-ns-openapi-2/README.md | 2 +- packages/apidom-ns-openapi-2/package.json | 1 + .../apidom-ns-openapi-2/src/elements/Items.ts | 276 ++++++++++++++++++ packages/apidom-ns-openapi-2/src/index.ts | 2 + packages/apidom-ns-openapi-2/src/namespace.ts | 2 + .../apidom-ns-openapi-2/src/predicates.ts | 33 ++- .../src/refractor/index.ts | 4 +- .../src/refractor/registration.ts | 3 + .../src/refractor/specification.ts | 30 ++ .../visitors/open-api-2/items/index.ts | 18 ++ .../src/traversal/visitor.ts | 1 + .../apidom-ns-openapi-2/test/predicates.ts | 59 ++++ .../Items/__snapshots__/index.ts.snap | 20 ++ .../test/refractor/elements/Items/index.ts | 37 +++ 15 files changed, 475 insertions(+), 14 deletions(-) create mode 100644 packages/apidom-ns-openapi-2/src/elements/Items.ts create mode 100644 packages/apidom-ns-openapi-2/src/refractor/visitors/open-api-2/items/index.ts create mode 100644 packages/apidom-ns-openapi-2/test/refractor/elements/Items/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-openapi-2/test/refractor/elements/Items/index.ts diff --git a/package-lock.json b/package-lock.json index c0f347b35b..c567fac6b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34835,6 +34835,7 @@ "dependencies": { "@babel/runtime-corejs3": "^7.20.7", "@swagger-api/apidom-core": "^0.77.0", + "@swagger-api/apidom-error": "^0.77.0", "@swagger-api/apidom-ns-json-schema-draft-4": "^0.77.0", "@types/ramda": "~0.29.3", "ramda": "~0.29.0", diff --git a/packages/apidom-ns-openapi-2/README.md b/packages/apidom-ns-openapi-2/README.md index c02fca04e9..a2c130e58e 100644 --- a/packages/apidom-ns-openapi-2/README.md +++ b/packages/apidom-ns-openapi-2/README.md @@ -193,7 +193,7 @@ Only fully implemented specification objects should be checked here. - [ ] [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-operation-object) - [x] [External Documentation Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-external-documentation-object) - [ ] [Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-parameter-object) -- [ ] [Items Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-items-object) +- [x] [Items Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-items-object) - [ ] [Responses Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-responses-object) - [ ] [Response Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-response-object) - [ ] [Headers Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-headers-object) diff --git a/packages/apidom-ns-openapi-2/package.json b/packages/apidom-ns-openapi-2/package.json index 8ca4908d03..ba948e80c6 100644 --- a/packages/apidom-ns-openapi-2/package.json +++ b/packages/apidom-ns-openapi-2/package.json @@ -44,6 +44,7 @@ "dependencies": { "@babel/runtime-corejs3": "^7.20.7", "@swagger-api/apidom-core": "^0.77.0", + "@swagger-api/apidom-error": "^0.77.0", "@swagger-api/apidom-ns-json-schema-draft-4": "^0.77.0", "@types/ramda": "~0.29.3", "ramda": "~0.29.0", diff --git a/packages/apidom-ns-openapi-2/src/elements/Items.ts b/packages/apidom-ns-openapi-2/src/elements/Items.ts new file mode 100644 index 0000000000..fca26bdae3 --- /dev/null +++ b/packages/apidom-ns-openapi-2/src/elements/Items.ts @@ -0,0 +1,276 @@ +import { + StringElement, + BooleanElement, + Attributes, + Meta, + NumberElement, + ArrayElement, + ObjectElement, +} from '@swagger-api/apidom-core'; +import { UnsupportedOperationError } from '@swagger-api/apidom-error'; +import { + JSONReferenceElement, + JSONSchemaElement, + MediaElement, +} from '@swagger-api/apidom-ns-json-schema-draft-4'; + +/* eslint-disable class-methods-use-this */ +class Items extends JSONSchemaElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'items'; + this.classes.push('json-schema-draft-4'); + } + + /** + * Core vocabulary + * + * URI: https://tools.ietf.org/html/draft-wright-json-schema-00 + */ + get idProp(): StringElement | undefined { + throw new UnsupportedOperationError('idProp getter in Items class is not not supported.'); + } + + set idProp(idProps: StringElement | undefined) { + throw new UnsupportedOperationError('idProp setter in Items class is not not supported.'); + } + + get $schema(): StringElement | undefined { + throw new UnsupportedOperationError('$schema getter in Items class is not not supported.'); + } + + set $schema($schema: StringElement | undefined) { + throw new UnsupportedOperationError('$schema setter in Items class is not not supported.'); + } + + /** + * Validation keywords for arrays + */ + + get additionalItems(): this | JSONReferenceElement | BooleanElement | undefined { + throw new UnsupportedOperationError( + 'additionalItems getter in Items class is not not supported.', + ); + } + + set additionalItems(additionalItems: this | JSONReferenceElement | BooleanElement | undefined) { + throw new UnsupportedOperationError( + 'additionalItems setter in Items class is not not supported.', + ); + } + + get items(): this | undefined { + return this.get('items'); + } + + set items(items: this | undefined) { + this.set('items', items); + } + + /** + * Validation keywords for objects + */ + + get maxProperties(): NumberElement | undefined { + throw new UnsupportedOperationError( + 'maxProperties getter in Items class is not not supported.', + ); + } + + set maxProperties(maxProperties: NumberElement | undefined) { + throw new UnsupportedOperationError( + 'maxProperties setter in Items class is not not supported.', + ); + } + + get minProperties(): NumberElement | undefined { + throw new UnsupportedOperationError( + 'minProperties getter in Items class is not not supported.', + ); + } + + set minProperties(minProperties: NumberElement | undefined) { + throw new UnsupportedOperationError( + 'minProperties setter in Items class is not not supported.', + ); + } + + get required(): ArrayElement | undefined { + throw new UnsupportedOperationError('required getter in Items class is not not supported.'); + } + + set required(required: ArrayElement | undefined) { + throw new UnsupportedOperationError('required setter in Items class is not not supported.'); + } + + get properties(): ObjectElement | undefined { + throw new UnsupportedOperationError('properties getter in Items class is not not supported.'); + } + + set properties(properties: ObjectElement | undefined) { + throw new UnsupportedOperationError('properties setter in Items class is not not supported.'); + } + + get additionalProperties(): this | JSONReferenceElement | BooleanElement | undefined { + throw new UnsupportedOperationError( + 'additionalProperties getter in Items class is not not supported.', + ); + } + + set additionalProperties( + additionalProperties: this | JSONReferenceElement | BooleanElement | undefined, + ) { + throw new UnsupportedOperationError( + 'additionalProperties setter in Items class is not not supported.', + ); + } + + get patternProperties(): ObjectElement | undefined { + throw new UnsupportedOperationError( + 'patternProperties getter in Items class is not not supported.', + ); + } + + set patternProperties(patternProperties: ObjectElement | undefined) { + throw new UnsupportedOperationError( + 'patternProperties setter in Items class is not not supported.', + ); + } + + get dependencies(): ObjectElement | undefined { + throw new UnsupportedOperationError('dependencies getter in Items class is not not supported.'); + } + + set dependencies(dependencies: ObjectElement | undefined) { + throw new UnsupportedOperationError('dependencies setter in Items class is not not supported.'); + } + + /** + * Validation keywords for any instance type + */ + + get type(): StringElement | undefined { + return this.get('type'); + } + + set type(type: StringElement | undefined) { + this.set('type', type); + } + + get allOf(): ArrayElement | undefined { + throw new UnsupportedOperationError('allOf getter in Items class is not not supported.'); + } + + set allOf(allOf: ArrayElement | undefined) { + throw new UnsupportedOperationError('allOf setter in Items class is not not supported.'); + } + + get anyOf(): ArrayElement | undefined { + throw new UnsupportedOperationError('anyOf getter in Items class is not not supported.'); + } + + set anyOf(anyOf: ArrayElement | undefined) { + throw new UnsupportedOperationError('anyOf setter in Items class is not not supported.'); + } + + get oneOf(): ArrayElement | undefined { + throw new UnsupportedOperationError('oneOf getter in Items class is not not supported.'); + } + + set oneOf(oneOf: ArrayElement | undefined) { + throw new UnsupportedOperationError('oneOf setter in Items class is not not supported.'); + } + + get not(): this | JSONReferenceElement | undefined { + throw new UnsupportedOperationError('not getter in Items class is not not supported.'); + } + + set not(not: this | JSONReferenceElement | undefined) { + throw new UnsupportedOperationError('not setter in Items class is not not supported.'); + } + + get definitions(): ObjectElement | undefined { + throw new UnsupportedOperationError('definitions getter in Items class is not not supported.'); + } + + set definitions(definitions: ObjectElement | undefined) { + throw new UnsupportedOperationError('definitions setter in Items class is not not supported.'); + } + + /** + * Metadata keywords + * + * URI: https://datatracker.ietf.org/doc/html/draft-wright-json-schema-validation-00#section-6 + */ + + get title(): StringElement | undefined { + throw new UnsupportedOperationError('title getter in Items class is not not supported.'); + } + + set title(title: StringElement | undefined) { + throw new UnsupportedOperationError('title setter in Items class is not not supported.'); + } + + get description(): StringElement | undefined { + throw new UnsupportedOperationError('description getter in Items class is not not supported.'); + } + + set description(description: StringElement | undefined) { + throw new UnsupportedOperationError('description setter in Items class is not not supported.'); + } + + /** + * Semantic validation with "format" + * + * URI: https://datatracker.ietf.org/doc/html/draft-wright-json-schema-validation-00#section-7 + */ + + get format(): StringElement | undefined { + return this.get('format'); + } + + set format(format: StringElement | undefined) { + this.set('format', format); + } + + /** + * JSON Hyper-Schema + * + * URI: https://datatracker.ietf.org/doc/html/draft-wright-json-schema-hyperschema-00 + */ + + get base(): StringElement | undefined { + throw new UnsupportedOperationError('base getter in Items class is not not supported.'); + } + + set base(base: StringElement | undefined) { + throw new UnsupportedOperationError('base setter in Items class is not not supported.'); + } + + get links(): ArrayElement | undefined { + throw new UnsupportedOperationError('links getter in Items class is not not supported.'); + } + + set links(links: ArrayElement | undefined) { + throw new UnsupportedOperationError('links setter in Items class is not not supported.'); + } + + get media(): MediaElement | undefined { + throw new UnsupportedOperationError('media getter in Items class is not not supported.'); + } + + set media(media: MediaElement | undefined) { + throw new UnsupportedOperationError('media setter in Items class is not not supported.'); + } + + get readOnly(): BooleanElement | undefined { + throw new UnsupportedOperationError('readOnly getter in Items class is not not supported.'); + } + + set readOnly(readOnly: BooleanElement | undefined) { + throw new UnsupportedOperationError('readOnly setter in Items class is not not supported.'); + } +} +/* eslint-enable class-methods-use-this */ + +export default Items; diff --git a/packages/apidom-ns-openapi-2/src/index.ts b/packages/apidom-ns-openapi-2/src/index.ts index 09e9c2d2c4..e3288e9abb 100644 --- a/packages/apidom-ns-openapi-2/src/index.ts +++ b/packages/apidom-ns-openapi-2/src/index.ts @@ -24,6 +24,7 @@ export { isLicenseElement, isContactElement, isExternalDocumentationElement, + isItemsElement, isExampleElement, isTagElement, isXmlElement, @@ -41,6 +42,7 @@ export { LicenseElement, ContactElement, ExternalDocumentationElement, + ItemsElement, ExampleElement, TagElement, XmlElement, diff --git a/packages/apidom-ns-openapi-2/src/namespace.ts b/packages/apidom-ns-openapi-2/src/namespace.ts index 0794e6c00f..b611613dcb 100644 --- a/packages/apidom-ns-openapi-2/src/namespace.ts +++ b/packages/apidom-ns-openapi-2/src/namespace.ts @@ -4,6 +4,7 @@ import InfoElement from './elements/Info'; import LicenseElement from './elements/License'; import ContactElement from './elements/Contact'; import ExternalDocumentation from './elements/ExternalDocumentation'; +import ItemsElement from './elements/Items'; import ExampleElement from './elements/Example'; import TagElement from './elements/Tag'; import XmlElement from './elements/Xml'; @@ -20,6 +21,7 @@ const openApi2 = { base.register('license', LicenseElement); base.register('contact', ContactElement); base.register('externalDocumentation', ExternalDocumentation); + base.register('items', ItemsElement); base.register('example', ExampleElement); base.register('tag', TagElement); base.register('xml', XmlElement); diff --git a/packages/apidom-ns-openapi-2/src/predicates.ts b/packages/apidom-ns-openapi-2/src/predicates.ts index 4bc81058ab..e8b7781337 100644 --- a/packages/apidom-ns-openapi-2/src/predicates.ts +++ b/packages/apidom-ns-openapi-2/src/predicates.ts @@ -4,6 +4,7 @@ import InfoElement from './elements/Info'; import LicenseElement from './elements/License'; import ContactElement from './elements/Contact'; import ExternalDocumentation from './elements/ExternalDocumentation'; +import ItemsElement from './elements/Items'; import ExampleElement from './elements/Example'; import TagElement from './elements/Tag'; import XmlElement from './elements/Xml'; @@ -14,7 +15,7 @@ import ScopesElement from './elements/Scopes'; export const isInfoElement = createPredicate( ({ hasBasicElementProps, isElementType, primitiveEq }) => { - return (element: any) => + return (element: unknown) => element instanceof InfoElement || (hasBasicElementProps(element) && isElementType('info', element) && @@ -24,7 +25,7 @@ export const isInfoElement = createPredicate( export const isLicenseElement = createPredicate( ({ hasBasicElementProps, isElementType, primitiveEq }) => { - return (element: any) => + return (element: unknown) => element instanceof LicenseElement || (hasBasicElementProps(element) && isElementType('license', element) && @@ -34,7 +35,7 @@ export const isLicenseElement = createPredicate( export const isContactElement = createPredicate( ({ hasBasicElementProps, isElementType, primitiveEq }) => { - return (element: any) => + return (element: unknown) => element instanceof ContactElement || (hasBasicElementProps(element) && isElementType('contact', element) && @@ -43,7 +44,7 @@ export const isContactElement = createPredicate( ); export const isExternalDocumentationElement = createPredicate( ({ hasBasicElementProps, isElementType, primitiveEq }) => { - return (element: any) => + return (element: unknown) => element instanceof ExternalDocumentation || (hasBasicElementProps(element) && isElementType('externalDocumentation', element) && @@ -51,9 +52,19 @@ export const isExternalDocumentationElement = createPredicate( }, ); +export const isItemsElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq }) => { + return (element: unknown) => + element instanceof ItemsElement || + (hasBasicElementProps(element) && + isElementType('items', element) && + primitiveEq('object', element)); + }, +); + export const isExampleElement = createPredicate( ({ hasBasicElementProps, isElementType, primitiveEq }) => { - return (element: any) => + return (element: unknown) => element instanceof ExampleElement || (hasBasicElementProps(element) && isElementType('example', element) && @@ -63,7 +74,7 @@ export const isExampleElement = createPredicate( export const isTagElement = createPredicate( ({ hasBasicElementProps, isElementType, primitiveEq }) => { - return (element: any) => + return (element: unknown) => element instanceof TagElement || (hasBasicElementProps(element) && isElementType('tag', element) && @@ -73,7 +84,7 @@ export const isTagElement = createPredicate( export const isXmlElement = createPredicate( ({ hasBasicElementProps, isElementType, primitiveEq }) => { - return (element: any) => + return (element: unknown) => element instanceof XmlElement || (hasBasicElementProps(element) && isElementType('xml', element) && @@ -83,7 +94,7 @@ export const isXmlElement = createPredicate( export const isSecurityDefinitionsElement = createPredicate( ({ hasBasicElementProps, isElementType, primitiveEq }) => { - return (element: any) => + return (element: unknown) => element instanceof SecurityDefinitionsElement || (hasBasicElementProps(element) && isElementType('securityDefinitions', element) && @@ -93,7 +104,7 @@ export const isSecurityDefinitionsElement = createPredicate( export const isSecuritySchemeElement = createPredicate( ({ hasBasicElementProps, isElementType, primitiveEq }) => { - return (element: any) => + return (element: unknown) => element instanceof SecuritySchemeElement || (hasBasicElementProps(element) && isElementType('securityScheme', element) && @@ -103,7 +114,7 @@ export const isSecuritySchemeElement = createPredicate( export const isScopesElement = createPredicate( ({ hasBasicElementProps, isElementType, primitiveEq }) => { - return (element: any) => + return (element: unknown) => element instanceof ScopesElement || (hasBasicElementProps(element) && isElementType('scopes', element) && @@ -113,7 +124,7 @@ export const isScopesElement = createPredicate( export const isSecurityRequirementElement = createPredicate( ({ hasBasicElementProps, isElementType, primitiveEq }) => { - return (element: any) => + return (element: unknown) => element instanceof SecurityRequirementElement || (hasBasicElementProps(element) && isElementType('securityRequirement', element) && diff --git a/packages/apidom-ns-openapi-2/src/refractor/index.ts b/packages/apidom-ns-openapi-2/src/refractor/index.ts index c3f9f5115b..ec33385eaf 100644 --- a/packages/apidom-ns-openapi-2/src/refractor/index.ts +++ b/packages/apidom-ns-openapi-2/src/refractor/index.ts @@ -12,7 +12,7 @@ import { keyMap, getNodeType } from '../traversal/visitor'; import createToolbox from './toolbox'; const refract = ( - value: any, + value: unknown, { specPath = ['visitors', 'document', 'objects', 'Swagger', '$visitor'], plugins = [] } = {}, ): T => { const element = baseRefract(value); @@ -38,7 +38,7 @@ const refract = ( export const createRefractor = (specPath: string[]) => - (value: any, options = {}) => + (value: unknown, options = {}) => refract(value, { specPath, ...options }); export default refract; diff --git a/packages/apidom-ns-openapi-2/src/refractor/registration.ts b/packages/apidom-ns-openapi-2/src/refractor/registration.ts index df2a4c96cb..967128d0c0 100644 --- a/packages/apidom-ns-openapi-2/src/refractor/registration.ts +++ b/packages/apidom-ns-openapi-2/src/refractor/registration.ts @@ -2,6 +2,7 @@ import InfoElement from '../elements/Info'; import LicenseElement from '../elements/License'; import ContactElement from '../elements/Contact'; import ExternalDocumentationElement from '../elements/ExternalDocumentation'; +import ItemsElement from '../elements/Items'; import ExampleElement from '../elements/Example'; import TagElement from '../elements/Tag'; import XmlElement from '../elements/Xml'; @@ -34,6 +35,7 @@ ExternalDocumentationElement.refract = createRefractor([ 'ExternalDocumentation', '$visitor', ]); +ItemsElement.refract = createRefractor(['visitors', 'document', 'objects', 'Items', '$visitor']); ExampleElement.refract = createRefractor([ 'visitors', 'document', @@ -71,6 +73,7 @@ export { LicenseElement, ContactElement, ExternalDocumentationElement, + ItemsElement, ExampleElement, TagElement, XmlElement, diff --git a/packages/apidom-ns-openapi-2/src/refractor/specification.ts b/packages/apidom-ns-openapi-2/src/refractor/specification.ts index 6bede9c0d1..5e66a54a50 100644 --- a/packages/apidom-ns-openapi-2/src/refractor/specification.ts +++ b/packages/apidom-ns-openapi-2/src/refractor/specification.ts @@ -1,9 +1,12 @@ +import { specificationObj as JSONSchemaDraft4Specification } from '@swagger-api/apidom-ns-json-schema-draft-4'; + import FallbackVisitor from './visitors/FallbackVisitor'; import InfoVisitor from './visitors/open-api-2/info'; import InfoVersionVisitor from './visitors/open-api-2/info/VersionVisitor'; import LicenseVisitor from './visitors/open-api-2/license'; import ContactVisitor from './visitors/open-api-2/contact'; import ExternalDocumentationElement from './visitors/open-api-2/external-documentation'; +import ItemsVisitor from './visitors/open-api-2/items'; import ExampleVisitor from './visitors/open-api-2/example'; import TagVisitor from './visitors/open-api-2/tag'; import XmlVisitor from './visitors/open-api-2/xml'; @@ -22,6 +25,9 @@ import SpecificationExtensionVisitor from './visitors/SpecificationExtensionVisi * Note: Specification object allows to use absolute internal JSON pointers. */ +const { fixedFields: jsonSchemaFixedFields } = + JSONSchemaDraft4Specification.visitors.document.objects.JSONSchema; + const specification = { visitors: { value: FallbackVisitor, @@ -64,6 +70,30 @@ const specification = { url: FallbackVisitor, }, }, + Items: { + $visitor: ItemsVisitor, + fixedFields: { + type: jsonSchemaFixedFields.type, + format: jsonSchemaFixedFields.format, + items: { + $ref: '#/visitors/document/objects/Items', + }, + collectionFormat: FallbackVisitor, + default: jsonSchemaFixedFields.default, + maximum: jsonSchemaFixedFields.maximum, + exclusiveMaximum: jsonSchemaFixedFields.exclusiveMaximum, + minimum: jsonSchemaFixedFields.minimum, + exclusiveMinimum: jsonSchemaFixedFields.exclusiveMinimum, + maxLength: jsonSchemaFixedFields.maxLength, + minLength: jsonSchemaFixedFields.minLength, + pattern: jsonSchemaFixedFields.pattern, + maxItems: jsonSchemaFixedFields.maxItems, + minItems: jsonSchemaFixedFields.minItems, + uniqueItems: jsonSchemaFixedFields.uniqueItems, + enum: jsonSchemaFixedFields.enum, + multipleOf: jsonSchemaFixedFields.multipleOf, + }, + }, Example: { $visitor: ExampleVisitor, }, diff --git a/packages/apidom-ns-openapi-2/src/refractor/visitors/open-api-2/items/index.ts b/packages/apidom-ns-openapi-2/src/refractor/visitors/open-api-2/items/index.ts new file mode 100644 index 0000000000..25015cacc2 --- /dev/null +++ b/packages/apidom-ns-openapi-2/src/refractor/visitors/open-api-2/items/index.ts @@ -0,0 +1,18 @@ +import stampit from 'stampit'; +import { always } from 'ramda'; + +import ItemsElement from '../../../../elements/Items'; +import FallbackVisitor from '../../FallbackVisitor'; +import FixedFieldsVisitor from '../../generics/FixedFieldsVisitor'; + +const ItemsVisitor = stampit(FixedFieldsVisitor, FallbackVisitor, { + props: { + specPath: always(['document', 'objects', 'Items']), + canSupportSpecificationExtensions: true, + }, + init() { + this.element = new ItemsElement(); + }, +}); + +export default ItemsVisitor; diff --git a/packages/apidom-ns-openapi-2/src/traversal/visitor.ts b/packages/apidom-ns-openapi-2/src/traversal/visitor.ts index 888274d798..9d1c25c4d2 100644 --- a/packages/apidom-ns-openapi-2/src/traversal/visitor.ts +++ b/packages/apidom-ns-openapi-2/src/traversal/visitor.ts @@ -23,6 +23,7 @@ export const keyMap = { LicenseElement: ['content'], ContactElement: ['content'], ExternalDocumentationElement: ['content'], + ItemsElement: ['content'], ExampleElement: ['content'], TagElement: ['content'], XmlElement: ['content'], diff --git a/packages/apidom-ns-openapi-2/test/predicates.ts b/packages/apidom-ns-openapi-2/test/predicates.ts index c985f6fa16..e171c6d5dd 100644 --- a/packages/apidom-ns-openapi-2/test/predicates.ts +++ b/packages/apidom-ns-openapi-2/test/predicates.ts @@ -5,6 +5,7 @@ import { LicenseElement, ContactElement, ExternalDocumentationElement, + ItemsElement, ExampleElement, TagElement, XmlElement, @@ -16,6 +17,7 @@ import { isLicenseElement, isContactElement, isExternalDocumentationElement, + isItemsElement, isExampleElement, isTagElement, isXmlElement, @@ -254,6 +256,63 @@ describe('predicates', function () { }); }); + context('isItemsElement', function () { + context('given ItemsElement instance value', function () { + specify('should return true', function () { + const element = new ItemsElement(); + + assert.isTrue(isItemsElement(element)); + }); + }); + + context('given subtype instance value', function () { + specify('should return true', function () { + // eslint-disable-next-line @typescript-eslint/naming-convention + class ItemsSubElement extends ItemsElement {} + + assert.isTrue(isItemsElement(new ItemsSubElement())); + }); + }); + + context('given non ItemsSubElement instance value', function () { + specify('should return false', function () { + assert.isFalse(isItemsElement(1)); + assert.isFalse(isItemsElement(null)); + assert.isFalse(isItemsElement(undefined)); + assert.isFalse(isItemsElement({})); + assert.isFalse(isItemsElement([])); + assert.isFalse(isItemsElement('string')); + }); + }); + + specify('should support duck-typing', function () { + const itemsElementDuck = { + _storedElement: 'items', + _content: [], + primitive() { + return 'object'; + }, + get element() { + return this._storedElement; + }, + }; + + const itemsElementSwan = { + _storedElement: undefined, + _content: undefined, + primitive() { + return 'swan'; + }, + get length() { + return 0; + }, + }; + + assert.isTrue(isItemsElement(itemsElementDuck)); + assert.isFalse(isItemsElement(itemsElementSwan)); + }); + }); + context('isExampleElement', function () { context('given ExampleElement instance value', function () { specify('should return true', function () { diff --git a/packages/apidom-ns-openapi-2/test/refractor/elements/Items/__snapshots__/index.ts.snap b/packages/apidom-ns-openapi-2/test/refractor/elements/Items/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..cc8073a9c5 --- /dev/null +++ b/packages/apidom-ns-openapi-2/test/refractor/elements/Items/__snapshots__/index.ts.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements ItemsElement should refract to semantic ApiDOM tree 1`] = ` +(ItemsElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ItemsElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (NumberElement))))) +`; diff --git a/packages/apidom-ns-openapi-2/test/refractor/elements/Items/index.ts b/packages/apidom-ns-openapi-2/test/refractor/elements/Items/index.ts new file mode 100644 index 0000000000..37d488ac1b --- /dev/null +++ b/packages/apidom-ns-openapi-2/test/refractor/elements/Items/index.ts @@ -0,0 +1,37 @@ +import { assert, expect } from 'chai'; +import { includesClasses, sexprs } from '@swagger-api/apidom-core'; + +import { ItemsElement } from '../../../../src'; + +describe('refractor', function () { + context('elements', function () { + context('ItemsElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const itemsElement = ItemsElement.refract({ + type: 'array', + items: { + type: 'integer', + minimum: 0, + maximum: 63, + }, + }); + + expect(sexprs(itemsElement)).toMatchSnapshot(); + }); + + specify('should support specification extensions', function () { + const itemsElement = ItemsElement.refract({ + type: 'array', + 'x-extension': 'extension', + }) as ItemsElement; + + assert.isFalse( + includesClasses(['specification-extension'], itemsElement.getMember('type')), + ); + assert.isTrue( + includesClasses(['specification-extension'], itemsElement.getMember('x-extension')), + ); + }); + }); + }); +});