diff --git a/src/core/components/operation-tag.jsx b/src/core/components/operation-tag.jsx index ce45a17102a..dddf6b028b9 100644 --- a/src/core/components/operation-tag.jsx +++ b/src/core/components/operation-tag.jsx @@ -2,7 +2,7 @@ import React from "react" import PropTypes from "prop-types" import ImPropTypes from "react-immutable-proptypes" import Im from "immutable" -import { createDeepLinkPath, sanitizeUrl } from "core/utils" +import { createDeepLinkPath, escapeDeepLinkPath, sanitizeUrl } from "core/utils" export default class OperationTag extends React.Component { @@ -52,7 +52,7 @@ export default class OperationTag extends React.Component { let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"]) let tagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"]) - let isShownKey = ["operations-tag", createDeepLinkPath(tag)] + let isShownKey = ["operations-tag", tag] let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list") return ( @@ -61,11 +61,14 @@ export default class OperationTag extends React.Component {

layoutActions.show(isShownKey, !showTag)} className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" } - id={isShownKey.join("-")}> + id={isShownKey.map(v => escapeDeepLinkPath(v)).join("-")} + data-tag={tag} + data-is-open={showTag} + > { !tagDescription ? : diff --git a/test/e2e-cypress/tests/features/deep-linking.js b/test/e2e-cypress/tests/features/deep-linking.js index 7f2751f3927..257c00fa111 100644 --- a/test/e2e-cypress/tests/features/deep-linking.js +++ b/test/e2e-cypress/tests/features/deep-linking.js @@ -3,7 +3,7 @@ describe("Deep linking feature", () => { const swagger2BaseUrl = "/?deepLinking=true&url=/documents/features/deep-linking.swagger.yaml" describe("regular Operation", () => { - BaseDeeplinkTestFactory({ + OperationDeeplinkTestFactory({ baseUrl: swagger2BaseUrl, elementToGet: ".opblock-get", correctElementId: "operations-myTag-myOperation", @@ -16,7 +16,7 @@ describe("Deep linking feature", () => { const elementToGet = ".opblock-post" const correctFragment = "#/my%20Tag/my%20Operation" - BaseDeeplinkTestFactory({ + OperationDeeplinkTestFactory({ baseUrl: swagger2BaseUrl, elementToGet, correctElementId: "operations-my_Tag-my_Operation", @@ -42,7 +42,7 @@ describe("Deep linking feature", () => { }) describe("Operation with underscores in tag+id", () => { - BaseDeeplinkTestFactory({ + OperationDeeplinkTestFactory({ baseUrl: swagger2BaseUrl, elementToGet: ".opblock-patch", correctElementId: "operations-underscore_Tag-underscore_Operation", @@ -52,7 +52,7 @@ describe("Deep linking feature", () => { }) describe("Operation with UTF-16 characters", () => { - BaseDeeplinkTestFactory({ + OperationDeeplinkTestFactory({ baseUrl: swagger2BaseUrl, elementToGet: ".opblock-head", correctElementId: "operations-шеллы-пошел", @@ -62,7 +62,7 @@ describe("Deep linking feature", () => { }) describe("Operation with no operationId", () => { - BaseDeeplinkTestFactory({ + OperationDeeplinkTestFactory({ baseUrl: swagger2BaseUrl, elementToGet: ".opblock-put", correctElementId: "operations-tagTwo-put_noOperationId", @@ -71,16 +71,25 @@ describe("Deep linking feature", () => { }) }) - describe("regular Operation with `docExpansion: none` enabled", function() { - it("should expand a tag", () => { - cy.visit(`${swagger2BaseUrl}&docExpansion=none#/myTag`) - .get(`.opblock-tag-section.is-open`) - .should("have.length", 1) + describe("regular Tag", () => { + TagDeeplinkTestFactory({ + isTagCase: true, + baseUrl: swagger2BaseUrl, + elementToGet: `.opblock-tag[data-tag="myTag"][data-is-open="true"]`, + correctElementId: "operations-tag-myTag", + correctFragment: "#/myTag", + correctHref: "#/myTag" }) - it("should expand an operation", () => { - cy.visit(`${swagger2BaseUrl}&docExpansion=none#/myTag/myOperation`) - .get(`.opblock.is-open`) - .should("have.length", 1) + }) + + describe("Tag with whitespace", () => { + TagDeeplinkTestFactory({ + isTagCase: true, + baseUrl: swagger2BaseUrl, + elementToGet: `.opblock-tag[data-tag="my Tag"][data-is-open="true"]`, + correctElementId: "operations-tag-my_Tag", + correctFragment: "#/my%20Tag", + correctHref: "#/my%20Tag" }) }) }) @@ -88,7 +97,7 @@ describe("Deep linking feature", () => { const openAPI3BaseUrl = "/?deepLinking=true&url=/documents/features/deep-linking.openapi.yaml" describe("regular Operation", () => { - BaseDeeplinkTestFactory({ + OperationDeeplinkTestFactory({ baseUrl: openAPI3BaseUrl, elementToGet: ".opblock-get", correctElementId: "operations-myTag-myOperation", @@ -101,7 +110,7 @@ describe("Deep linking feature", () => { const elementToGet = ".opblock-post" const correctFragment = "#/my%20Tag/my%20Operation" - BaseDeeplinkTestFactory({ + OperationDeeplinkTestFactory({ baseUrl: openAPI3BaseUrl, elementToGet: ".opblock-post", correctElementId: "operations-my_Tag-my_Operation", @@ -128,7 +137,7 @@ describe("Deep linking feature", () => { }) describe("Operation with underscores in tag+id", () => { - BaseDeeplinkTestFactory({ + OperationDeeplinkTestFactory({ baseUrl: openAPI3BaseUrl, elementToGet: ".opblock-patch", correctElementId: "operations-underscore_Tag-underscore_Operation", @@ -138,7 +147,7 @@ describe("Deep linking feature", () => { }) describe("Operation with UTF-16 characters", () => { - BaseDeeplinkTestFactory({ + OperationDeeplinkTestFactory({ baseUrl: openAPI3BaseUrl, elementToGet: ".opblock-head", correctElementId: "operations-шеллы-пошел", @@ -148,7 +157,7 @@ describe("Deep linking feature", () => { }) describe("Operation with no operationId", () => { - BaseDeeplinkTestFactory({ + OperationDeeplinkTestFactory({ baseUrl: openAPI3BaseUrl, elementToGet: ".opblock-put", correctElementId: "operations-tagTwo-put_noOperationId", @@ -157,22 +166,31 @@ describe("Deep linking feature", () => { }) }) - describe("regular Operation with `docExpansion: none` enabled", function () { - it("should expand a tag", () => { - cy.visit(`${openAPI3BaseUrl}&docExpansion=none#/myTag`) - .get(`.opblock-tag-section.is-open`) - .should("have.length", 1) + describe("regular Tag", () => { + TagDeeplinkTestFactory({ + isTagCase: true, + baseUrl: openAPI3BaseUrl, + elementToGet: `.opblock-tag[data-tag="myTag"][data-is-open="true"]`, + correctElementId: "operations-tag-myTag", + correctFragment: "#/myTag", + correctHref: "#/myTag" }) - it("should expand an operation", () => { - cy.visit(`${openAPI3BaseUrl}&docExpansion=none#/myTag/myOperation`) - .get(`.opblock.is-open`) - .should("have.length", 1) + }) + + describe("Tag with whitespace", () => { + TagDeeplinkTestFactory({ + isTagCase: true, + baseUrl: openAPI3BaseUrl, + elementToGet: `.opblock-tag[data-tag="my Tag"][data-is-open="true"]`, + correctElementId: "operations-tag-my_Tag", + correctFragment: "#/my%20Tag", + correctHref: "#/my%20Tag" }) }) }) }) -function BaseDeeplinkTestFactory({ baseUrl, elementToGet, correctElementId, correctFragment, correctHref }) { +function OperationDeeplinkTestFactory({ baseUrl, elementToGet, correctElementId, correctFragment, correctHref }) { it("should generate a correct element ID", () => { cy.visit(baseUrl) .get(elementToGet) @@ -210,4 +228,60 @@ function BaseDeeplinkTestFactory({ baseUrl, elementToGet, correctElementId, corr .window() .should("have.deep.property", "location.hash", correctFragment) }) + + it("should expand a tag with docExpansion disabled", () => { + cy.visit(`${baseUrl}&docExpansion=none${correctFragment}`) + .get(`.opblock-tag-section.is-open`) + .should("have.length", 1) + }) + + it("should expand an operation with docExpansion disabled", () => { + cy.visit(`${baseUrl}&docExpansion=none${correctFragment}`) + .get(`.opblock.is-open`) + .should("have.length", 1) + }) +} + +function TagDeeplinkTestFactory({ baseUrl, elementToGet, correctElementId, correctFragment, correctHref, isTagCase = false }) { + it("should generate a correct element ID", () => { + cy.visit(baseUrl) + .get(elementToGet) + .should("have.id", correctElementId) + }) + + it("should add the correct element fragment to the URL when expanded", () => { + cy.visit(baseUrl) + .get(elementToGet) + .click() + .click() // tags need two clicks because they're expanded by default + .window() + .should("have.deep.property", "location.hash", correctFragment) + }) + + it("should provide an anchor link that has the correct fragment as href", () => { + cy.visit(baseUrl) + .get(elementToGet) + .find("a") + .should("have.attr", "href", correctHref) + }) + + it("should expand the tag when reloaded", () => { + cy.visit(`${baseUrl}${correctFragment}`) + .get(`${elementToGet}[data-is-open="true"]`) + .should("exist") + }) + + it("should retain the correct fragment when reloaded", () => { + cy.visit(`${baseUrl}${correctFragment}`) + .reload() + .should("exist") + .window() + .should("have.deep.property", "location.hash", correctFragment) + }) + + it("should expand a tag with docExpansion disabled", () => { + cy.visit(`${baseUrl}&docExpansion=none${correctFragment}`) + .get(`.opblock-tag-section.is-open`) + .should("have.length", 1) + }) } \ No newline at end of file