diff --git a/.eslintrc b/.eslintrc index 502479e223b..3927dd7789c 100644 --- a/.eslintrc +++ b/.eslintrc @@ -23,7 +23,7 @@ settings: rules: semi: [2, never] strict: 0 - quotes: [2, double, { allowTemplateLiterals: true }] + quotes: [2, double, { avoidEscape: true, allowTemplateLiterals: true }] no-unused-vars: 2 no-multi-spaces: 1 camelcase: 1 @@ -37,4 +37,4 @@ rules: react/display-name: 0 mocha/no-exclusive-tests: 2 import/no-extraneous-dependencies: 2 - react/jsx-filename-extension: 2 \ No newline at end of file + react/jsx-filename-extension: 2 diff --git a/src/core/plugins/json-schema-2020-12/samples-extensions/fn.js b/src/core/plugins/json-schema-2020-12/samples-extensions/fn.js index 4a6f031958d..617996d168e 100644 --- a/src/core/plugins/json-schema-2020-12/samples-extensions/fn.js +++ b/src/core/plugins/json-schema-2020-12/samples-extensions/fn.js @@ -18,22 +18,39 @@ const generateStringFromRegex = (pattern) => { } } +/* eslint-disable camelcase */ const primitives = { string: (schema) => schema.pattern ? generateStringFromRegex(schema.pattern) : "string", string_email: () => "user@example.com", - "string_date-time": () => new Date().toISOString(), - string_date: () => new Date().toISOString().substring(0, 10), - string_uuid: () => "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "string_idn-email": () => "실례@example.com", string_hostname: () => "example.com", + "string_idn-hostname": () => "실례.com", string_ipv4: () => "198.51.100.42", string_ipv6: () => "2001:0db8:5b96:0000:0000:426f:8e17:642a", + string_uri: () => "https://example.com/", + "string_uri-reference": () => "path/index.html", + string_iri: () => "https://실례.com/", + "string_iri-reference": () => "path/실례.html", + string_uuid: () => "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "string_uri-template": () => "https://example.com/dictionary/{term:1}/{term}", + "string_json-pointer": () => "/a/b/c", + "string_relative-json-pointer": () => "1/0", + "string_date-time": () => new Date().toISOString(), + string_date: () => new Date().toISOString().substring(0, 10), + string_time: () => new Date().toISOString().substring(11), + string_duration: () => "P3D", // expresses a duration of 3 days + string_password: () => "********", number: () => 0, - number_float: () => 0.0, + number_float: () => 0.1, + number_double: () => 0.1, integer: () => 0, + integer_int32: () => (2 ** 30) >>> 0, + integer_int64: () => 2 ** 53 - 1, boolean: (schema) => typeof schema.default === "boolean" ? schema.default : true, } +/* eslint-enable camelcase */ const primitive = (schema) => { schema = objectify(schema) diff --git a/test/unit/core/plugins/json-schema-2020-12/samples-extensions/fn.js b/test/unit/core/plugins/json-schema-2020-12/samples-extensions/fn.js new file mode 100644 index 00000000000..0cf370a4f75 --- /dev/null +++ b/test/unit/core/plugins/json-schema-2020-12/samples-extensions/fn.js @@ -0,0 +1,2497 @@ +/** + * @prettier + * + */ +import { fromJS } from "immutable" +import { + createXMLExample, + sampleFromSchema, + memoizedCreateXMLExample, + memoizedSampleFromSchema, +} from "core/plugins/json-schema-2020-12/samples-extensions/fn" + +describe("sampleFromSchema", () => { + it("should return appropriate example for primitive types + format", function () { + const sample = (schema) => sampleFromSchema(fromJS(schema)) + + expect(sample({ type: "string" })).toStrictEqual("string") + expect(sample({ type: "string", pattern: "^abc$" })).toStrictEqual("abc") + expect(sample({ type: "string", format: "email" })).toStrictEqual( + "user@example.com" + ) + expect(sample({ type: "string", format: "idn-email" })).toStrictEqual( + "실례@example.com" + ) + expect(sample({ type: "string", format: "hostname" })).toStrictEqual( + "example.com" + ) + expect(sample({ type: "string", format: "idn-hostname" })).toStrictEqual( + "실례.com" + ) + expect(sample({ type: "string", format: "ipv4" })).toStrictEqual( + "198.51.100.42" + ) + expect(sample({ type: "string", format: "ipv6" })).toStrictEqual( + "2001:0db8:5b96:0000:0000:426f:8e17:642a" + ) + expect(sample({ type: "string", format: "uri" })).toStrictEqual( + "https://example.com/" + ) + expect(sample({ type: "string", format: "uri-reference" })).toStrictEqual( + "path/index.html" + ) + expect(sample({ type: "string", format: "iri" })).toStrictEqual( + "https://실례.com/" + ) + expect(sample({ type: "string", format: "iri-reference" })).toStrictEqual( + "path/실례.html" + ) + expect(sample({ type: "string", format: "uuid" })).toStrictEqual( + "3fa85f64-5717-4562-b3fc-2c963f66afa6" + ) + expect(sample({ type: "string", format: "uri-template" })).toStrictEqual( + "https://example.com/dictionary/{term:1}/{term}" + ) + expect(sample({ type: "string", format: "json-pointer" })).toStrictEqual( + "/a/b/c" + ) + expect( + sample({ type: "string", format: "relative-json-pointer" }) + ).toStrictEqual("1/0") + expect(sample({ type: "string", format: "date-time" })).toMatch( + /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z$/ + ) + expect(sample({ type: "string", format: "date" })).toMatch( + /^\d{4}-\d{2}-\d{2}$/ + ) + expect(sample({ type: "string", format: "time" })).toMatch( + /^\d{2}:\d{2}:\d{2}\.\d+Z$/ + ) + expect(sample({ type: "string", format: "duration" })).toStrictEqual("P3D") + expect(sample({ type: "string", format: "password" })).toStrictEqual( + "********" + ) + expect(sample({ type: "number" })).toStrictEqual(0) + expect(sample({ type: "number", format: "float" })).toStrictEqual(0.1) + expect(sample({ type: "number", format: "double" })).toStrictEqual(0.1) + expect(sample({ type: "integer" })).toStrictEqual(0) + expect(sample({ type: "integer", format: "int32" })).toStrictEqual( + (2 ** 30) >>> 0 + ) + expect(sample({ type: "integer", format: "int64" })).toStrictEqual( + 2 ** 53 - 1 + ) + expect(sample({ type: "boolean" })).toStrictEqual(true) + }) + + it("handles Immutable.js objects for nested schemas", function () { + const definition = fromJS({ + type: "object", + properties: { + json: { + type: "object", + example: { + a: "string", + }, + properties: { + a: { + type: "string", + }, + }, + }, + }, + }) + + const expected = { + json: { + a: "string", + }, + } + + expect(sampleFromSchema(definition, { includeReadOnly: false })).toEqual( + expected + ) + }) + + it("should return first enum value if only enum is provided", function () { + const definition = fromJS({ + enum: ["probe"], + }) + + const expected = "probe" + expect(sampleFromSchema(definition, { includeReadOnly: false })).toEqual( + expected + ) + }) + + it("combine first oneOf or anyOf with schema's definitions", function () { + let definition = { + type: "object", + anyOf: [ + { + type: "object", + properties: { + test2: { + type: "string", + example: "anyOf", + }, + test: { + type: "string", + example: "anyOf", + }, + }, + }, + ], + properties: { + test: { + type: "string", + example: "schema", + }, + }, + } + + let expected = { + test: "schema", + test2: "anyOf", + } + + expect(sampleFromSchema(definition, { includeReadOnly: false })).toEqual( + expected + ) + + definition = { + type: "object", + oneOf: [ + { + type: "object", + properties: { + test2: { + type: "string", + example: "oneOf", + }, + test: { + type: "string", + example: "oneOf", + }, + }, + }, + ], + properties: { + test: { + type: "string", + example: "schema", + }, + }, + } + + expected = { + test: "schema", + test2: "oneOf", + } + + expect(sampleFromSchema(definition, { includeReadOnly: false })).toEqual( + expected + ) + }) + + it("returns object with no readonly fields for parameter", function () { + const definition = { + type: "object", + properties: { + id: { + type: "integer", + }, + readOnlyDog: { + readOnly: true, + type: "string", + }, + }, + xml: { + name: "animals", + }, + } + + const expected = { + id: 0, + } + + expect(sampleFromSchema(definition, { includeReadOnly: false })).toEqual( + expected + ) + }) + + it("returns object with readonly fields for parameter, with includeReadOnly", function () { + const definition = { + type: "object", + properties: { + id: { + type: "integer", + }, + readOnlyDog: { + readOnly: true, + type: "string", + }, + }, + xml: { + name: "animals", + }, + } + + const expected = { + id: 0, + readOnlyDog: "string", + } + + expect(sampleFromSchema(definition, { includeReadOnly: true })).toEqual( + expected + ) + }) + + it("regex pattern test", function () { + const definition = { + type: "object", + properties: { + macAddress: { + type: "string", + pattern: "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$", + }, + }, + } + const resp = sampleFromSchema(definition) + + expect( + new RegExp("^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$", "g").test( + resp.macAddress + ) + ).toBe(true) + }) + + it("returns object without deprecated fields for parameter", function () { + const definition = { + type: "object", + properties: { + id: { + type: "integer", + }, + deprecatedProperty: { + deprecated: true, + type: "string", + }, + }, + xml: { + name: "animals", + }, + } + + const expected = { + id: 0, + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns object without writeonly fields for parameter", function () { + const definition = { + type: "object", + properties: { + id: { + type: "integer", + }, + writeOnlyDog: { + writeOnly: true, + type: "string", + }, + }, + xml: { + name: "animals", + }, + } + + const expected = { + id: 0, + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns object with writeonly fields for parameter, with includeWriteOnly", function () { + const definition = { + type: "object", + properties: { + id: { + type: "integer", + }, + writeOnlyDog: { + writeOnly: true, + type: "string", + }, + }, + xml: { + name: "animals", + }, + } + + const expected = { + id: 0, + writeOnlyDog: "string", + } + + expect(sampleFromSchema(definition, { includeWriteOnly: true })).toEqual( + expected + ) + }) + + it("returns object without any $$ref fields at the root schema level", function () { + const definition = { + type: "object", + properties: { + message: { + type: "string", + }, + }, + example: { + value: { + message: "Hello, World!", + }, + $$ref: "#/components/examples/WelcomeExample", + }, + $$ref: "#/components/schemas/Welcome", + } + + const expected = { + value: { + message: "Hello, World!", + }, + } + + expect(sampleFromSchema(definition, { includeWriteOnly: true })).toEqual( + expected + ) + }) + + it("returns object without any $$ref fields at nested schema levels", function () { + const definition = { + type: "object", + properties: { + message: { + type: "string", + }, + }, + example: { + a: { + value: { + message: "Hello, World!", + }, + $$ref: "#/components/examples/WelcomeExample", + }, + }, + $$ref: "#/components/schemas/Welcome", + } + + const expected = { + a: { + value: { + message: "Hello, World!", + }, + }, + } + + expect(sampleFromSchema(definition, { includeWriteOnly: true })).toEqual( + expected + ) + }) + + it("returns object with any $$ref fields that appear to be user-created", function () { + const definition = { + type: "object", + properties: { + message: { + type: "string", + }, + }, + example: { + $$ref: { + value: { + message: "Hello, World!", + }, + $$ref: "#/components/examples/WelcomeExample", + }, + }, + $$ref: "#/components/schemas/Welcome", + } + + const expected = { + $$ref: { + value: { + message: "Hello, World!", + }, + }, + } + + expect(sampleFromSchema(definition, { includeWriteOnly: true })).toEqual( + expected + ) + }) + + it("returns example value for date-time property", () => { + const definition = { + type: "string", + format: "date-time", + } + + // 0-20 chops off milliseconds + // necessary because test latency can cause failures + // it would be better to mock Date globally and expect a string - KS 11/18 + const expected = new Date().toISOString().substring(0, 20) + + expect(sampleFromSchema(definition)).toContain(expected) + }) + + it("returns example value for date property", () => { + const definition = { + type: "string", + format: "date", + } + + const expected = new Date().toISOString().substring(0, 10) + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns a UUID for a string with format=uuid", () => { + const definition = { + type: "string", + format: "uuid", + } + + const expected = "3fa85f64-5717-4562-b3fc-2c963f66afa6" + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns a hostname for a string with format=hostname", () => { + const definition = { + type: "string", + format: "hostname", + } + + const expected = "example.com" + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns an IPv4 address for a string with format=ipv4", () => { + const definition = { + type: "string", + format: "ipv4", + } + + const expected = "198.51.100.42" + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns an IPv6 address for a string with format=ipv6", () => { + const definition = { + type: "string", + format: "ipv6", + } + + const expected = "2001:0db8:5b96:0000:0000:426f:8e17:642a" + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + describe("for array type", () => { + it("returns array with sample of array type", () => { + const definition = { + type: "array", + items: { + type: "integer", + }, + } + + const expected = [0] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns string for example for array that has example of type string", () => { + const definition = { + type: "array", + items: { + type: "string", + }, + example: "dog", + } + + const expected = "dog" + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns array of examples for array that has examples", () => { + const definition = { + type: "array", + items: { + type: "string", + }, + example: ["dog", "cat"], + } + + const expected = ["dog", "cat"] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns array of samples for oneOf type", () => { + const definition = { + type: "array", + items: { + type: "string", + oneOf: [ + { + type: "integer", + }, + ], + }, + } + + const expected = [0] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns array of samples for oneOf types", () => { + const definition = { + type: "array", + items: { + type: "string", + oneOf: [ + { + type: "string", + }, + { + type: "integer", + }, + ], + }, + } + + const expected = ["string", 0] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns array of samples for oneOf examples", () => { + const definition = { + type: "array", + items: { + type: "string", + oneOf: [ + { + type: "string", + example: "dog", + }, + { + type: "integer", + example: 1, + }, + ], + }, + } + + const expected = ["dog", 1] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns array of samples for anyOf type", () => { + const definition = { + type: "array", + items: { + type: "string", + anyOf: [ + { + type: "integer", + }, + ], + }, + } + + const expected = [0] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns array of samples for anyOf types", () => { + const definition = { + type: "array", + items: { + type: "string", + anyOf: [ + { + type: "string", + }, + { + type: "integer", + }, + ], + }, + } + + const expected = ["string", 0] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns array of samples for anyOf examples", () => { + const definition = { + type: "array", + items: { + type: "string", + anyOf: [ + { + type: "string", + example: "dog", + }, + { + type: "integer", + example: 1, + }, + ], + }, + } + + const expected = ["dog", 1] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns null for a null example", () => { + const definition = { + type: "object", + properties: { + foo: { + type: "string", + nullable: true, + example: null, + }, + }, + } + + const expected = { + foo: null, + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("returns null for a null object-level example", () => { + const definition = { + type: "object", + properties: { + foo: { + type: "string", + nullable: true, + }, + }, + example: { + foo: null, + }, + } + + const expected = { + foo: null, + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + }) + + describe("discriminator mapping example", () => { + it("returns an example where discriminated field is equal to mapping value", () => { + const definition = { + type: "array", + items: { + oneOf: [ + { + required: ["type"], + type: "object", + properties: { + type: { + type: "string", + enum: ["TYPE1", "TYPE2"], + }, + }, + discriminator: { + propertyName: "type", + mapping: { + TYPE1: "#/components/schemas/FirstDto", + TYPE2: "#/components/schemas/SecondDto", + }, + }, + $$ref: + "examples/swagger-config.yaml#/components/schemas/FirstDto", + }, + { + required: ["type"], + type: "object", + properties: { + type: { + type: "string", + enum: ["TYPE1", "TYPE2"], + }, + }, + discriminator: { + propertyName: "type", + mapping: { + TYPE1: "#/components/schemas/FirstDto", + TYPE2: "#/components/schemas/SecondDto", + }, + }, + $$ref: + "examples/swagger-config.yaml#/components/schemas/SecondDto", + }, + ], + }, + } + + const expected = [ + { + type: "TYPE1", + }, + { + type: "TYPE2", + }, + ] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should not throw if expected $$ref is missing, and should fallback to default behavior", () => { + const definition = { + type: "array", + items: { + oneOf: [ + { + required: ["type"], + type: "object", + properties: { + type: { + type: "string", + enum: ["TYPE1", "TYPE2"], + }, + }, + discriminator: { + propertyName: "type", + mapping: { + TYPE1: "#/components/schemas/FirstDto", + TYPE2: "#/components/schemas/SecondDto", + }, + }, + }, + { + required: ["type"], + type: "object", + properties: { + type: { + type: "string", + enum: ["TYPE1", "TYPE2"], + }, + }, + discriminator: { + propertyName: "type", + mapping: { + TYPE1: "#/components/schemas/FirstDto", + TYPE2: "#/components/schemas/SecondDto", + }, + }, + }, + ], + }, + } + + expect(() => { + sampleFromSchema(definition) + }).not.toThrow() + }) + }) + + it("should use overrideExample when defined", () => { + const definition = { + type: "object", + properties: { + foo: { + type: "string", + }, + }, + example: { + foo: null, + }, + } + + const expected = { + foo: "override", + } + + expect(sampleFromSchema(definition, {}, expected)).toEqual(expected) + }) + + it("should merge properties with anyOf", () => { + const definition = { + type: "object", + properties: { + foo: { + type: "string", + }, + }, + anyOf: [ + { + type: "object", + properties: { + bar: { + type: "boolean", + }, + }, + }, + ], + } + + const expected = { + foo: "string", + bar: true, + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should merge array item properties with anyOf", () => { + const definition = { + type: "array", + items: { + type: "object", + properties: { + foo: { + type: "string", + }, + }, + anyOf: [ + { + type: "object", + properties: { + bar: { + type: "boolean", + }, + }, + }, + ], + }, + } + + const expected = [ + { + foo: "string", + bar: true, + }, + ] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should merge properties with oneOf", () => { + const definition = { + type: "object", + properties: { + foo: { + type: "string", + }, + }, + oneOf: [ + { + type: "object", + properties: { + bar: { + type: "boolean", + }, + }, + }, + ], + } + + const expected = { + foo: "string", + bar: true, + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should merge array item properties with oneOf", () => { + const definition = { + type: "array", + items: { + type: "object", + properties: { + foo: { + type: "string", + }, + }, + oneOf: [ + { + type: "object", + properties: { + bar: { + type: "boolean", + }, + }, + }, + ], + }, + } + + const expected = [ + { + foo: "string", + bar: true, + }, + ] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should lift items with anyOf", () => { + const definition = { + type: "array", + anyOf: [ + { + type: "array", + items: { + type: "boolean", + }, + }, + ], + } + + const expected = [true] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should lift items with oneOf", () => { + const definition = { + type: "array", + oneOf: [ + { + type: "array", + items: { + type: "boolean", + }, + }, + ], + } + + const expected = [true] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + it("should ignore minProperties if cannot extend object", () => { + const definition = { + type: "object", + minProperties: 2, + properties: { + foo: { + type: "string", + }, + }, + } + + const expected = { + foo: "string", + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle minProperties in conjunction with additionalProperties", () => { + const definition = { + type: "object", + minProperties: 2, + additionalProperties: { + type: "string", + }, + } + + const expected = { + additionalProp1: "string", + additionalProp2: "string", + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle minProperties in conjunction with properties and additionalProperties", () => { + const definition = { + type: "object", + minProperties: 2, + additionalProperties: true, + properties: { + foo: { + type: "string", + }, + }, + } + + const expected = { + foo: "string", + additionalProp1: {}, + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle minProperties in conjunction with additionalProperties and anyOf", () => { + const definition = { + type: "object", + minProperties: 2, + additionalProperties: true, + anyOf: [ + { + type: "object", + properties: { + foo: { + type: "string", + }, + }, + }, + ], + } + + const expected = { + foo: "string", + additionalProp1: {}, + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle maxProperties", () => { + const definition = { + type: "object", + maxProperties: 1, + properties: { + foo: { + type: "string", + }, + swaggerUi: { + type: "string", + }, + }, + } + + const expected = { + foo: "string", + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle maxProperties in conjunction with additionalProperties", () => { + const definition = { + type: "object", + maxProperties: 1, + additionalProperties: true, + } + + const expected = { + additionalProp1: {}, + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle maxProperties in conjunction with anyOf", () => { + const definition = { + type: "object", + maxProperties: 1, + anyOf: [ + { + type: "object", + properties: { + foo: { + type: "string", + }, + swaggerUi: { + type: "string", + }, + }, + }, + ], + } + + const expected = { + foo: "string", + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle handle maxProperties in conjunction with required", () => { + const definition = { + type: "object", + maxProperties: 1, + required: ["swaggerUi"], + properties: { + foo: { + type: "string", + }, + swaggerUi: { + type: "string", + example: "<3", + }, + }, + } + + const expected = { + swaggerUi: "<3", + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle handle maxProperties in conjunction with anyOf required", () => { + const definition = { + type: "object", + maxProperties: 1, + required: ["swaggerUi"], + anyOf: [ + { + type: "object", + properties: { + foo: { + type: "string", + }, + swaggerUi: { + type: "string", + example: "<3", + }, + }, + }, + ], + } + + const expected = { + swaggerUi: "<3", + } + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle minItems", () => { + const definition = { + type: "array", + minItems: 2, + items: { + type: "string", + }, + } + + const expected = ["string", "string"] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle minItems with example", () => { + const definition = { + type: "array", + minItems: 2, + items: { + type: "string", + example: "some", + }, + } + + const expected = ["some", "some"] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle minItems in conjunction with oneOf", () => { + const definition = { + type: "array", + minItems: 4, + items: { + oneOf: [ + { + type: "string", + }, + { + type: "number", + }, + ], + }, + } + + const expected = ["string", 0, "string", 0] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle maxItems in conjunction with multiple oneOf", () => { + const definition = { + type: "array", + maxItems: 1, + items: { + oneOf: [ + { + type: "string", + }, + { + type: "number", + }, + ], + }, + } + + const expected = ["string"] + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle minimum", () => { + const definition = { + type: "number", + minimum: 5, + } + + const expected = 5 + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle minimum with exclusive", () => { + const definition = { + type: "number", + minimum: 5, + exclusiveMinimum: true, + } + + const expected = 6 + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle maximum", () => { + const definition = { + type: "number", + maximum: -1, + } + + const expected = -1 + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle maximum with exclusive", () => { + const definition = { + type: "number", + maximum: -1, + exclusiveMaximum: true, + } + + const expected = -2 + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle minLength", () => { + const definition = { + type: "string", + minLength: 7, + } + + const expected = "strings" + + expect(sampleFromSchema(definition)).toEqual(expected) + }) + + it("should handle maxLength", () => { + const definition = { + type: "string", + maxLength: 3, + } + + const expected = "str" + + expect(sampleFromSchema(definition)).toEqual(expected) + }) +}) + +describe("createXMLExample", function () { + let sut = createXMLExample + describe("simple types with xml property", function () { + it('returns tag string when passing type string and xml:{name: "newtagname"}', function () { + const definition = { + type: "string", + xml: { + name: "newtagname", + }, + } + + expect(sut(definition)).toEqual( + '\nstring' + ) + }) + + it('returns tag string when passing type string and xml:{name: "newtagname", prefix:"test"}', function () { + const definition = { + type: "string", + xml: { + name: "newtagname", + prefix: "test", + }, + } + + expect(sut(definition)).toEqual( + '\nstring' + ) + }) + + it('returns tag string when passing type string and xml:{"namespace": "http://swagger.io/schema/sample", "prefix": "sample"}', function () { + const definition = { + type: "string", + xml: { + namespace: "http://swagger.io/schema/sample", + prefix: "sample", + name: "name", + }, + } + + expect(sut(definition)).toEqual( + '\nstring' + ) + }) + + it('returns tag string when passing type string and xml:{"namespace": "http://swagger.io/schema/sample"}', function () { + const definition = { + type: "string", + xml: { + namespace: "http://swagger.io/schema/sample", + name: "name", + }, + } + + expect(sut(definition)).toEqual( + '\nstring' + ) + }) + + it("returns tag test when passing default value", function () { + const expected = + '\ntest' + const definition = { + type: "string", + default: "test", + xml: { + name: "newtagname", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns default value when enum provided", function () { + const expected = + '\none' + const definition = { + type: "string", + default: "one", + enum: ["two", "one"], + xml: { + name: "newtagname", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns example value when provided", function () { + const expected = + '\ntwo' + const definition = { + type: "string", + default: "one", + example: "two", + enum: ["two", "one"], + xml: { + name: "newtagname", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("sets first enum if provided", function () { + const expected = + '\none' + const definition = { + type: "string", + enum: ["one", "two"], + xml: { + name: "newtagname", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + }) + + describe("array", function () { + it("returns tag string when passing string items", function () { + const expected = + '\nstring' + const definition = { + type: "array", + items: { + type: "string", + }, + xml: { + name: "tagname", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns tag string when passing string items with name", function () { + const expected = + '\nstring' + const definition = { + type: "array", + items: { + type: "string", + xml: { + name: "animal", + }, + }, + xml: { + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns tag string when passing string items with name", function () { + const expected = + '\n\n\tstring\n' + const definition = { + type: "array", + items: { + type: "string", + xml: { + name: "animal", + }, + }, + xml: { + wrapped: true, + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("return correct nested wrapped array", function () { + const expected = + '\n\n\tstring\n' + const definition = { + type: "array", + items: { + type: "array", + items: { + type: "string", + }, + xml: { + name: "dog", + }, + }, + xml: { + wrapped: true, + name: "aliens", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("return correct nested wrapped array with xml", function () { + const expected = + '\n\n\t\n\t\tstring\n\t\n' + const definition = { + type: "array", + items: { + type: "array", + items: { + type: "string", + xml: { + name: "dog", + }, + }, + xml: { + name: "dogs", + wrapped: true, + }, + }, + xml: { + wrapped: true, + name: "aliens", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("adds namespace to array", function () { + const expected = + '\nstring' + const definition = { + type: "array", + items: { + type: "string", + xml: { + name: "dog", + namespace: "test", + }, + }, + xml: { + name: "aliens", + namespace: "test_new", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("adds prefix to array", function () { + const expected = + '\nstring' + const definition = { + type: "array", + items: { + type: "string", + xml: { + name: "dog", + prefix: "test", + }, + }, + xml: { + name: "aliens", + prefix: "test_new", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("adds prefix to array with no xml in items", function () { + const expected = + '\nstring' + const definition = { + type: "array", + items: { + type: "string", + }, + xml: { + name: "dog", + prefix: "test", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("adds namespace to array with no xml in items", function () { + const expected = + '\nstring' + const definition = { + type: "array", + items: { + type: "string", + }, + xml: { + name: "dog", + namespace: "test", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("adds namespace to array with wrapped", function () { + const expected = + '\n\n\tstring\n' + const definition = { + type: "array", + items: { + type: "string", + xml: { + name: "dog", + }, + }, + xml: { + wrapped: true, + name: "aliens", + namespace: "test", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("adds prefix to array with wrapped", function () { + const expected = + '\n\n\tstring\n' + const definition = { + type: "array", + items: { + type: "string", + xml: { + name: "dog", + }, + }, + xml: { + wrapped: true, + name: "aliens", + prefix: "test", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns wrapped array when type is not passed", function () { + const expected = + '\n\n\tstring\n' + const definition = { + items: { + type: "string", + xml: { + name: "animal", + }, + }, + xml: { + wrapped: true, + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns array with default values", function () { + const expected = + '\none\ntwo' + const definition = { + items: { + type: "string", + xml: { + name: "animal", + }, + }, + default: ["one", "two"], + xml: { + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns array with default values with wrapped=true", function () { + const expected = + '\n\n\tone\n\ttwo\n' + const definition = { + items: { + type: "string", + xml: { + name: "animal", + }, + }, + default: ["one", "two"], + xml: { + wrapped: true, + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns array with default values", function () { + const expected = + '\none' + const definition = { + items: { + type: "string", + enum: ["one", "two"], + xml: { + name: "animal", + }, + }, + xml: { + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns array with default values with wrapped=true", function () { + const expected = + '\n\n\t1\n\t2\n' + const definition = { + items: { + enum: ["one", "two"], + type: "string", + xml: { + name: "animal", + }, + }, + default: ["1", "2"], + xml: { + wrapped: true, + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns array with example values with ", function () { + const expected = + '\n\n\t1\n\t2\n' + const definition = { + type: "object", + properties: { + animal: { + type: "array", + items: { + type: "string", + }, + example: ["1", "2"], + }, + }, + xml: { + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns array with example values with wrapped=true", function () { + const expected = + '\n\n\t1\n\t2\n' + const definition = { + type: "array", + items: { + type: "string", + xml: { + name: "animal", + }, + }, + example: ["1", "2"], + xml: { + wrapped: true, + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns array of objects with example values with wrapped=true", function () { + const expected = + '\n\n\t\n\t\t1\n\t\tArthur Dent\n\t\n\t\n\t\t2\n\t\tFord Prefect\n\t\n' + const definition = { + type: "array", + items: { + type: "object", + properties: { + id: { + type: "integer", + }, + name: { + type: "string", + }, + }, + xml: { + name: "user", + }, + }, + xml: { + name: "users", + wrapped: true, + }, + example: [ + { + id: 1, + name: "Arthur Dent", + }, + { + id: 2, + name: "Ford Prefect", + }, + ], + } + + expect(sut(definition)).toEqual(expected) + }) + it("should return additionalProperty example", () => { + const expected = + '\n\n\ttest\n' + const definition = { + type: "array", + items: { + type: "object", + properties: { + alien: { + type: "string", + }, + dog: { + type: "integer", + }, + }, + }, + xml: { + name: "aliens", + }, + } + + expect(sut(definition, {}, [{ notalien: "test" }])).toEqual(expected) + }) + it("should return literal example", () => { + const expected = "\n\t\n\t0\n" + const definition = { + type: "array", + items: { + properties: { + alien: { + type: "string", + }, + dog: { + type: "integer", + }, + }, + }, + xml: { + name: "aliens", + }, + } + + expect(sut(definition, {}, expected)).toEqual(expected) + }) + }) + + describe("object", function () { + it("returns object with 2 properties", function () { + const expected = + '\n\n\tstring\n\t0\n' + const definition = { + type: "object", + properties: { + alien: { + type: "string", + }, + dog: { + type: "integer", + }, + }, + xml: { + name: "aliens", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with integer property and array property", function () { + const expected = + '\n\n\tstring\n\t0\n' + const definition = { + type: "object", + properties: { + aliens: { + type: "array", + items: { + type: "string", + }, + }, + dog: { + type: "integer", + }, + }, + xml: { + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns nested objects", function () { + const expected = + '\n\n\t\n\t\tstring\n\t\n\tstring\n' + const definition = { + type: "object", + properties: { + aliens: { + type: "object", + properties: { + alien: { + type: "string", + xml: { + name: "alien", + }, + }, + }, + }, + dog: { + type: "string", + }, + }, + xml: { + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with no readonly fields for parameter", function () { + const expected = + '\n\n\t0\n' + const definition = { + type: "object", + properties: { + id: { + type: "integer", + }, + dog: { + readOnly: true, + type: "string", + }, + }, + xml: { + name: "animals", + }, + } + + expect(sut(definition, { includeReadOnly: false })).toEqual(expected) + }) + + it("returns object with readonly fields for parameter, with includeReadOnly", function () { + const expected = + '\n\n\t0\n\tstring\n' + const definition = { + type: "object", + properties: { + id: { + type: "integer", + }, + dog: { + readOnly: true, + type: "string", + }, + }, + xml: { + name: "animals", + }, + } + + expect(sut(definition, { includeReadOnly: true })).toEqual(expected) + }) + + it("returns object without writeonly fields for parameter", function () { + const expected = + '\n\n\t0\n' + const definition = { + type: "object", + properties: { + id: { + type: "integer", + }, + dog: { + writeOnly: true, + type: "string", + }, + }, + xml: { + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with writeonly fields for parameter, with includeWriteOnly", function () { + const expected = + '\n\n\t0\n\tstring\n' + const definition = { + type: "object", + properties: { + id: { + type: "integer", + }, + dog: { + writeOnly: true, + type: "string", + }, + }, + xml: { + name: "animals", + }, + } + + expect(sut(definition, { includeWriteOnly: true })).toEqual(expected) + }) + + it("returns object with passed property as attribute", function () { + const expected = + '\n\n\tstring\n' + const definition = { + type: "object", + properties: { + id: { + type: "integer", + xml: { + attribute: true, + }, + }, + dog: { + type: "string", + }, + }, + xml: { + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with passed property as attribute with custom name", function () { + const expected = + '\n\n\tstring\n' + const definition = { + type: "object", + properties: { + id: { + type: "integer", + xml: { + attribute: true, + name: "test", + }, + }, + dog: { + type: "string", + }, + }, + xml: { + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with example values in attribute", function () { + const expected = + '\n\n\tadmin\n' + const definition = { + type: "object", + properties: { + id: { + type: "integer", + xml: { + attribute: true, + }, + }, + role: { + type: "string", + }, + }, + xml: { + name: "user", + }, + example: { + id: 42, + role: "admin", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with enum values in attribute", function () { + const expected = + '\n\n\tstring\n' + const definition = { + type: "object", + properties: { + id: { + type: "string", + enum: ["one", "two"], + xml: { + attribute: true, + }, + }, + role: { + type: "string", + }, + }, + xml: { + name: "user", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with default values in attribute", function () { + const expected = + '\n\n\tstring\n' + const definition = { + type: "object", + properties: { + id: { + type: "string", + default: "one", + xml: { + attribute: true, + }, + }, + role: { + type: "string", + }, + }, + xml: { + name: "user", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with default values in attribute", function () { + const expected = + '\n\n\tstring\n' + const definition = { + type: "object", + properties: { + id: { + type: "string", + example: "one", + xml: { + attribute: true, + }, + }, + role: { + type: "string", + }, + }, + xml: { + name: "user", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with example value", function () { + const expected = + '\n\n\t42\n\tadmin\n' + const definition = { + type: "object", + properties: { + id: { + type: "integer", + }, + role: { + type: "string", + }, + }, + xml: { + name: "user", + }, + example: { + id: 42, + role: "admin", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with additional props", function () { + const expected = + '\n\n\tstring\n\tstring\n\tstring\n\tstring\n' + const definition = { + type: "object", + properties: { + dog: { + type: "string", + }, + }, + additionalProperties: { + type: "string", + }, + xml: { + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with additional props =true", function () { + const expected = + '\n\n\tstring\n\tAnything can be here\n' + const definition = { + type: "object", + properties: { + dog: { + type: "string", + }, + }, + additionalProperties: true, + xml: { + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with 2 properties with no type passed but properties", function () { + const expected = + '\n\n\tstring\n\t0\n' + const definition = { + properties: { + alien: { + type: "string", + }, + dog: { + type: "integer", + }, + }, + xml: { + name: "aliens", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("returns object with additional props with no type passed", function () { + const expected = + '\n\n\tstring\n\tstring\n\tstring\n' + const definition = { + additionalProperties: { + type: "string", + }, + xml: { + name: "animals", + }, + } + + expect(sut(definition)).toEqual(expected) + }) + + it("should use overrideExample when defined", () => { + const expected = + '\n\n\toverride\n' + + const definition = { + type: "object", + properties: { + foo: { + type: "string", + xml: { + name: "foo", + }, + }, + }, + example: { + foo: null, + }, + xml: { + name: "bar", + }, + } + + const overrideExample = { + foo: "override", + } + + expect(sut(definition, {}, overrideExample)).toEqual(expected) + }) + + it("should return additionalProperty example", () => { + const expected = + '\n\n\ttest\n\t1\n' + const definition = { + type: "object", + properties: { + alien: { + type: "string", + }, + dog: { + type: "integer", + }, + }, + xml: { + name: "aliens", + }, + } + + expect(sut(definition, {}, { alien: "test", dog: 1 })).toEqual(expected) + }) + it("should return literal example", () => { + const expected = "\n\t\n\t0\n" + const definition = { + type: "object", + properties: { + alien: { + type: "string", + }, + dog: { + type: "integer", + }, + }, + xml: { + name: "aliens", + }, + } + + expect(sut(definition, {}, expected)).toEqual(expected) + }) + it("should use exampleOverride for attr too", () => { + const expected = + '\n\n' + const definition = { + type: "object", + properties: { + test: { + type: "string", + xml: { + attribute: true, + }, + }, + }, + xml: { + name: "aliens", + }, + } + + expect(sut(definition, {}, { test: "probe" })).toEqual(expected) + }) + }) + + it("should handle handle maxProperties in conjunction with required", function () { + const definition = { + type: "object", + maxProperties: 1, + required: ["swaggerUi"], + xml: { + name: "probe", + }, + properties: { + foo: { + type: "string", + }, + swaggerUi: { + type: "string", + example: "cool", + }, + }, + } + + const expected = ` + +\tcool +` + + expect(sut(definition)).toEqual(expected) + }) +}) + +describe("memoizedSampleFromSchema", () => { + it("should sequentially update memoized overrideExample", () => { + const definition = { + type: "object", + properties: { + foo: { + type: "string", + }, + }, + example: { + foo: null, + }, + } + + const expected = { + foo: "override", + } + expect(memoizedSampleFromSchema(definition, {}, expected)).toEqual(expected) + + const updatedExpected = { + foo: "cat", + } + expect(memoizedSampleFromSchema(definition, {}, updatedExpected)).toEqual( + updatedExpected + ) + }) +}) + +describe("memoizedCreateXMLExample", () => { + it("should sequentially update memoized overrideExample", () => { + const expected = + '\n\n\toverride\n' + + const definition = { + type: "object", + properties: { + foo: { + type: "string", + xml: { + name: "foo", + }, + }, + }, + example: { + foo: null, + }, + xml: { + name: "bar", + }, + } + + const overrideExample = { + foo: "override", + } + expect(memoizedCreateXMLExample(definition, {}, overrideExample)).toEqual( + expected + ) + + const updatedOverrideExample = { + foo: "cat", + } + const updatedExpected = + '\n\n\tcat\n' + expect( + memoizedCreateXMLExample(definition, {}, updatedOverrideExample) + ).toEqual(updatedExpected) + }) +}) diff --git a/test/unit/core/plugins/json-schema-2020-12/samples-extensions/get-sample-schema.js b/test/unit/core/plugins/json-schema-2020-12/samples-extensions/get-sample-schema.js new file mode 100644 index 00000000000..dd4332d7dcb --- /dev/null +++ b/test/unit/core/plugins/json-schema-2020-12/samples-extensions/get-sample-schema.js @@ -0,0 +1,195 @@ +/** + * @prettier + */ +import { + memoizedSampleFromSchema, + memoizedCreateXMLExample, +} from "core/plugins/json-schema-2020-12/samples-extensions/fn" +import makeGetSampleSchema from "core/plugins/samples/fn/get-sample-schema" +import makeGetJsonSampleSchema from "core/plugins/samples/fn/get-json-sample-schema" +import makeGetYamlSampleSchema from "core/plugins/samples/fn/get-yaml-sample-schema" +import makeGetXmlSampleSchema from "core/plugins/samples/fn/get-xml-sample-schema" + +describe("getSampleSchema", () => { + const oriDate = Date + const getSystem = () => ({ + fn: { + memoizedSampleFromSchema, + memoizedCreateXMLExample, + getJsonSampleSchema: makeGetJsonSampleSchema(getSystem), + getYamlSampleSchema: makeGetYamlSampleSchema(getSystem), + getXmlSampleSchema: makeGetXmlSampleSchema(getSystem), + }, + }) + const getSampleSchema = makeGetSampleSchema(getSystem) + + beforeEach(() => { + Date = function () { + this.toISOString = function () { + return "2018-07-07T07:07:05.189Z" + } + } + }) + + afterEach(() => { + Date = oriDate + }) + + it("should stringify string values if json content-type", () => { + // Given + const res = getSampleSchema( + { + type: "string", + format: "date-time", + }, + "text/json" + ) + + // Then + expect(res).toEqual(JSON.stringify(new Date().toISOString())) + }) + + it("should not unnecessarily stringify string values for other content-types", () => { + // Given + const res = getSampleSchema({ + type: "string", + format: "date-time", + }) + + // Then + expect(res).toEqual(new Date().toISOString()) + }) + + it("should not unnecessarily stringify non-object values", () => { + // Given + const res = getSampleSchema({ + type: "number", + }) + + // Then + expect(res).toEqual(0) + }) + + it("should not unnecessarily stringify non-object values if content-type is json", () => { + // Given + const res = getSampleSchema( + { + type: "number", + }, + "application/json" + ) + + // Then + expect(res).toEqual(0) + }) + + it("should stringify object when literal string example is provided if json content-type", () => { + // Given + const expected = "" + const res = getSampleSchema( + { + type: "object", + }, + "text/json", + {}, + expected + ) + + // Then + expect(res).toEqual(JSON.stringify(expected)) + }) + + it("should parse valid json literal example if json content-type", () => { + // Given + const expected = { test: 123 } + const res = getSampleSchema( + { + type: "object", + }, + "text/json", + {}, + JSON.stringify(expected) + ) + + // Then + const actual = JSON.parse(res) + expect(actual.test).toEqual(123) + }) + + it("should handle number example with string schema as string", () => { + // Given + const expected = 123 + const res = getSampleSchema( + { + type: "string", + }, + "text/json", + {}, + expected + ) + + // Then + const actual = JSON.parse(res) + expect(actual).toEqual("123") + }) + + it("should handle number literal example with string schema as string", () => { + // Given + const expected = "123" + const res = getSampleSchema( + { + type: "string", + }, + "text/json", + {}, + expected + ) + + // Then + const actual = JSON.parse(res) + expect(actual).toEqual("123") + }) + + it("should handle number literal example with number schema as number", () => { + // Given + const expected = "123" + const res = getSampleSchema( + { + type: "number", + }, + "text/json", + {}, + expected + ) + + // Then + const actual = JSON.parse(res) + expect(actual).toEqual(123) + }) + + it("should return yaml example if yaml is contained in the content-type", () => { + const res = getSampleSchema( + { + type: "object", + }, + "text/yaml", + {}, + { test: 123 } + ) + + expect(res).toEqual("test: 123") + }) + + it("should return yaml example if yml is contained in the content-type", () => { + const res = getSampleSchema( + { + type: "object", + }, + "text/yml", + {}, + { test: 123 } + ) + + expect(res).toEqual("test: 123") + }) +})