diff --git a/src/core/plugins/oas3/components/request-body.jsx b/src/core/plugins/oas3/components/request-body.jsx index 388f5cc6662..1e554a3c37c 100644 --- a/src/core/plugins/oas3/components/request-body.jsx +++ b/src/core/plugins/oas3/components/request-body.jsx @@ -114,12 +114,22 @@ const RequestBody = ({ const Input = getComponent("Input") if(!isExecute) { - return - Example values are not available for {contentType} media types. - + return
+ { requestBodyDescription && + + } + + Example values are not available for {contentType} media types. + +
} - return + return
+ { requestBodyDescription && + + } + +
} diff --git a/test/e2e-cypress/e2e/features/plugins/oas3/request-body-file-upload-description.cy.js b/test/e2e-cypress/e2e/features/plugins/oas3/request-body-file-upload-description.cy.js new file mode 100644 index 00000000000..98bb61d1cef --- /dev/null +++ b/test/e2e-cypress/e2e/features/plugins/oas3/request-body-file-upload-description.cy.js @@ -0,0 +1,155 @@ +/** + * @prettier + */ + +describe("OpenAPI 3.0 Request Body description on file upload content types", () => { + beforeEach(() => { + cy.visit( + "/?url=/documents/features/oas3-request-body-file-upload-description.yaml" + ) + }) + + describe("application/octet-stream (with schema)", () => { + beforeEach(() => { + cy.get("#operations-default-uploadApplicationOctetStream").click() + }) + + it("renders the Markdown description before Try it out is enabled", () => { + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" + ) + .should("exist") + .and("contain.text", "Upload a binary blob") + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown strong" + ).should("have.text", "Upload a binary blob") + cy.get( + ".opblock-section-request-body .opblock-description-wrapper i" + ).should( + "have.text", + "Example values are not available for application/octet-stream media types." + ) + }) + + it("renders the Markdown description after Try it out is enabled", () => { + cy.get(".try-out__btn").click() + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" + ) + .should("exist") + .and("contain.text", "Upload a binary blob") + cy.get( + ".opblock-section-request-body .opblock-description-wrapper input" + ).should("have.prop", "type", "file") + }) + }) + + describe("image/png", () => { + beforeEach(() => { + cy.get("#operations-default-uploadImagePng").click() + }) + + it("renders the Markdown description before Try it out is enabled", () => { + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" + ) + .should("exist") + .and("contain.text", "Upload a PNG image") + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown em" + ).should("have.text", "Upload a PNG image") + }) + + it("renders the Markdown description after Try it out is enabled", () => { + cy.get(".try-out__btn").click() + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" + ) + .should("exist") + .and("contain.text", "Upload a PNG image") + cy.get( + ".opblock-section-request-body .opblock-description-wrapper input" + ).should("have.prop", "type", "file") + }) + }) + + describe("application/octet-stream with empty Media Type Object", () => { + beforeEach(() => { + cy.get("#operations-default-uploadApplicationOctetStreamEmpty").click() + }) + + it("renders the Markdown description before Try it out is enabled", () => { + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" + ) + .should("exist") + .and("contain.text", "Empty media type object") + }) + + it("renders the Markdown description after Try it out is enabled", () => { + cy.get(".try-out__btn").click() + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" + ) + .should("exist") + .and("contain.text", "Empty media type object") + cy.get( + ".opblock-section-request-body .opblock-description-wrapper input" + ).should("have.prop", "type", "file") + }) + }) + + describe("schema type string and format binary", () => { + beforeEach(() => { + cy.get("#operations-default-uploadSchemaFormatBinary").click() + }) + + it("renders the Markdown description before Try it out is enabled", () => { + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" + ) + .should("exist") + .and("contain.text", "application/x-custom") + }) + + it("renders the Markdown description after Try it out is enabled", () => { + cy.get(".try-out__btn").click() + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" + ) + .should("exist") + .and("contain.text", "application/x-custom") + cy.get( + ".opblock-section-request-body .opblock-description-wrapper input" + ).should("have.prop", "type", "file") + }) + }) + + describe("file upload without a requestBody description", () => { + beforeEach(() => { + cy.get("#operations-default-uploadWithoutDescription").click() + }) + + it("does not render a Markdown block when description is absent (before Try it out)", () => { + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" + ).should("not.exist") + cy.get( + ".opblock-section-request-body .opblock-description-wrapper i" + ).should( + "have.text", + "Example values are not available for application/octet-stream media types." + ) + }) + + it("does not render a Markdown block when description is absent (after Try it out)", () => { + cy.get(".try-out__btn").click() + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" + ).should("not.exist") + cy.get( + ".opblock-section-request-body .opblock-description-wrapper input" + ).should("have.prop", "type", "file") + }) + }) +}) diff --git a/test/e2e-cypress/static/documents/features/oas3-request-body-file-upload-description.yaml b/test/e2e-cypress/static/documents/features/oas3-request-body-file-upload-description.yaml new file mode 100644 index 00000000000..b75a46d13b3 --- /dev/null +++ b/test/e2e-cypress/static/documents/features/oas3-request-body-file-upload-description.yaml @@ -0,0 +1,54 @@ +openapi: 3.0.4 +info: + title: "Request body description on file upload content types" + description: |- + This document exercises rendering of `requestBody.description` (Markdown) + on file-upload-intended content types. The description should render both + before and after "Try it out" is enabled. + version: "1.0.0" +paths: + /upload-application-octet-stream: + post: + operationId: uploadApplicationOctetStream + requestBody: + description: |- + **Upload a binary blob** as `application/octet-stream`. + See [the docs](https://example.com) for details. + content: + application/octet-stream: + schema: + type: string + /upload-image-png: + post: + operationId: uploadImagePng + requestBody: + description: "*Upload a PNG image* of the user avatar." + content: + image/png: + schema: + type: string + /upload-application-octet-stream-empty: + post: + operationId: uploadApplicationOctetStreamEmpty + requestBody: + description: "Empty media type object with **octet-stream** description." + content: + application/octet-stream: {} + /upload-schema-format-binary: + post: + operationId: uploadSchemaFormatBinary + requestBody: + description: "`application/x-custom` with **binary** format." + content: + application/x-custom: + schema: + type: string + format: binary + /upload-without-description: + post: + operationId: uploadWithoutDescription + requestBody: + content: + application/octet-stream: + schema: + type: string diff --git a/test/unit/core/plugins/oas3/components/request-body.jsx b/test/unit/core/plugins/oas3/components/request-body.jsx new file mode 100644 index 00000000000..d97190d3d3d --- /dev/null +++ b/test/unit/core/plugins/oas3/components/request-body.jsx @@ -0,0 +1,145 @@ +/** + * @prettier + */ +import React from "react" +import PropTypes from "prop-types" +import { shallow } from "enzyme" +import { fromJS, List } from "immutable" + +import RequestBody from "core/plugins/oas3/components/request-body" + +describe("", function () { + const Markdown = ({ source }) =>
{source}
+ Markdown.propTypes = { source: PropTypes.string } + const Input = () => null + const RequestBodyEditor = () => null + const HighlightCode = () => null + const ModelExample = () => null + const ExamplesSelectValueRetainer = () => null + const Example = () => null + const ParameterIncludeEmpty = () => null + + const components = { + Markdown, + Input, + RequestBodyEditor, + HighlightCode, + modelExample: ModelExample, + ExamplesSelectValueRetainer, + Example, + ParameterIncludeEmpty, + } + + const getComponentStub = (name) => components[name] || null + + const baseProps = { + userHasEditedBody: false, + requestBodyValue: fromJS({}), + requestBodyInclusionSetting: fromJS({}), + requestBodyErrors: List(), + getComponent: getComponentStub, + getConfigs: () => ({}), + specSelectors: {}, + fn: { + isFileUploadIntended: () => true, + hasSchemaType: () => false, + getSampleSchema: () => "", + }, + specPath: List(), + onChange: () => {}, + onChangeIncludeEmpty: () => {}, + updateActiveExamplesKey: () => {}, + setRetainRequestBodyValueFlag: () => {}, + oas3Actions: {}, + } + + it("renders the request body description for file upload content types when isExecute is true", function () { + const props = { + ...baseProps, + isExecute: true, + contentType: "application/octet-stream", + requestBody: fromJS({ + description: "A zip file containing files that will be unzipped", + content: { + "application/octet-stream": { + schema: { type: "string", format: "binary" }, + }, + }, + }), + } + + const wrapper = shallow() + + const markdown = wrapper.find(Markdown) + expect(markdown.length).toEqual(1) + expect(markdown.prop("source")).toEqual( + "A zip file containing files that will be unzipped" + ) + expect(wrapper.find(Input).length).toEqual(1) + }) + + it("renders the request body description for file upload content types when isExecute is false", function () { + const props = { + ...baseProps, + isExecute: false, + contentType: "application/octet-stream", + requestBody: fromJS({ + description: "A zip file containing files that will be unzipped", + content: { + "application/octet-stream": { + schema: { type: "string", format: "binary" }, + }, + }, + }), + } + + const wrapper = shallow() + + const markdown = wrapper.find(Markdown) + expect(markdown.length).toEqual(1) + expect(markdown.prop("source")).toEqual( + "A zip file containing files that will be unzipped" + ) + expect(wrapper.text()).toContain("Example values are not available") + }) + + it("does not render a Markdown description when the request body has none (isExecute true)", function () { + const props = { + ...baseProps, + isExecute: true, + contentType: "application/octet-stream", + requestBody: fromJS({ + content: { + "application/octet-stream": { + schema: { type: "string", format: "binary" }, + }, + }, + }), + } + + const wrapper = shallow() + + expect(wrapper.find(Markdown).length).toEqual(0) + expect(wrapper.find(Input).length).toEqual(1) + }) + + it("does not render a Markdown description when the request body has none (isExecute false)", function () { + const props = { + ...baseProps, + isExecute: false, + contentType: "application/octet-stream", + requestBody: fromJS({ + content: { + "application/octet-stream": { + schema: { type: "string", format: "binary" }, + }, + }, + }), + } + + const wrapper = shallow() + + expect(wrapper.find(Markdown).length).toEqual(0) + expect(wrapper.text()).toContain("Example values are not available") + }) +})