From 8d1d1ed501b593c11cf22a939916d902d1b8d061 Mon Sep 17 00:00:00 2001 From: yogeshwaran-c Date: Thu, 16 Apr 2026 07:42:20 +0530 Subject: [PATCH 1/4] fix(oas3): show request body description for file upload content types When a request body uses a content type like application/octet-stream that triggers file upload mode, the description from the requestBody object was not being displayed. Instead, only the "Example values are not available" message was shown (in non-execute mode) or just the file input (in execute mode). This renders the requestBody description above the file input or the example-not-available message, consistent with how descriptions are shown for other content types. Fixes #5637 --- .../plugins/oas3/components/request-body.jsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) 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 && + + } + +
} From 527322c9158440c300a0888655e2e852bc50510a Mon Sep 17 00:00:00 2001 From: yogeshwaran-c Date: Tue, 21 Apr 2026 10:58:36 +0530 Subject: [PATCH 2/4] test(oas3): cover request body description for file upload content types --- .../plugins/oas3/components/request-body.jsx | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 test/unit/core/plugins/oas3/components/request-body.jsx 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") + }) +}) From f0ad27cd4f1012f7050e0f2e3028aa70a2d082a2 Mon Sep 17 00:00:00 2001 From: yogeshwaran-c Date: Tue, 21 Apr 2026 12:16:36 +0530 Subject: [PATCH 3/4] test(oas3): add e2e coverage for request body description on file upload content types --- ...request-body-file-upload-description.cy.js | 155 ++++++++++++++++++ ...-request-body-file-upload-description.yaml | 54 ++++++ 2 files changed, 209 insertions(+) create mode 100644 test/e2e-cypress/e2e/features/plugins/oas3/request-body-file-upload-description.cy.js create mode 100644 test/e2e-cypress/static/documents/features/oas3-request-body-file-upload-description.yaml 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..ff3ffc746bd --- /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 .markdown" + ) + .should("exist") + .and("contain.text", "Upload a binary blob") + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .markdown 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 .markdown" + ) + .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 .markdown" + ) + .should("exist") + .and("contain.text", "Upload a PNG image") + cy.get( + ".opblock-section-request-body .opblock-description-wrapper .markdown 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 .markdown" + ) + .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 .markdown" + ) + .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 .markdown" + ) + .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 .markdown" + ) + .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 .markdown" + ) + .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 .markdown" + ).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 .markdown" + ).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 From 247ea140074991add2a159a61b9874555ee5e3d2 Mon Sep 17 00:00:00 2001 From: yogeshwaran-c Date: Wed, 22 Apr 2026 13:19:55 +0530 Subject: [PATCH 4/4] test(oas3): use OAS3 renderedMarkdown class in e2e selectors OAS3 wrap-components/markdown.jsx renders with class renderedMarkdown, not the core markdown class; the prior selectors never matched and all eight new e2e cases timed out in CI. --- ...request-body-file-upload-description.cy.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) 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 index ff3ffc746bd..98bb61d1cef 100644 --- 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 @@ -16,12 +16,12 @@ describe("OpenAPI 3.0 Request Body description on file upload content types", () it("renders the Markdown description before Try it out is enabled", () => { cy.get( - ".opblock-section-request-body .opblock-description-wrapper .markdown" + ".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 .markdown strong" + ".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" @@ -34,7 +34,7 @@ describe("OpenAPI 3.0 Request Body description on file upload content 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 .markdown" + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" ) .should("exist") .and("contain.text", "Upload a binary blob") @@ -51,19 +51,19 @@ describe("OpenAPI 3.0 Request Body description on file upload content types", () it("renders the Markdown description before Try it out is enabled", () => { cy.get( - ".opblock-section-request-body .opblock-description-wrapper .markdown" + ".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 .markdown em" + ".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 .markdown" + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" ) .should("exist") .and("contain.text", "Upload a PNG image") @@ -80,7 +80,7 @@ describe("OpenAPI 3.0 Request Body description on file upload content types", () it("renders the Markdown description before Try it out is enabled", () => { cy.get( - ".opblock-section-request-body .opblock-description-wrapper .markdown" + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" ) .should("exist") .and("contain.text", "Empty media type object") @@ -89,7 +89,7 @@ describe("OpenAPI 3.0 Request Body description on file upload content 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 .markdown" + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" ) .should("exist") .and("contain.text", "Empty media type object") @@ -106,7 +106,7 @@ describe("OpenAPI 3.0 Request Body description on file upload content types", () it("renders the Markdown description before Try it out is enabled", () => { cy.get( - ".opblock-section-request-body .opblock-description-wrapper .markdown" + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" ) .should("exist") .and("contain.text", "application/x-custom") @@ -115,7 +115,7 @@ describe("OpenAPI 3.0 Request Body description on file upload content 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 .markdown" + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" ) .should("exist") .and("contain.text", "application/x-custom") @@ -132,7 +132,7 @@ describe("OpenAPI 3.0 Request Body description on file upload content types", () it("does not render a Markdown block when description is absent (before Try it out)", () => { cy.get( - ".opblock-section-request-body .opblock-description-wrapper .markdown" + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" ).should("not.exist") cy.get( ".opblock-section-request-body .opblock-description-wrapper i" @@ -145,7 +145,7 @@ describe("OpenAPI 3.0 Request Body description on file upload content 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 .markdown" + ".opblock-section-request-body .opblock-description-wrapper .renderedMarkdown" ).should("not.exist") cy.get( ".opblock-section-request-body .opblock-description-wrapper input"