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")
+ })
+})