fix: tag-level deep link escaping inconsistencies (via #5117)
* add test cases * update tag deeplinking implementation * MOAR test updates * update operation-tag.jsx
This commit is contained in:
@@ -2,7 +2,7 @@ import React from "react"
|
|||||||
import PropTypes from "prop-types"
|
import PropTypes from "prop-types"
|
||||||
import ImPropTypes from "react-immutable-proptypes"
|
import ImPropTypes from "react-immutable-proptypes"
|
||||||
import Im from "immutable"
|
import Im from "immutable"
|
||||||
import { createDeepLinkPath, sanitizeUrl } from "core/utils"
|
import { createDeepLinkPath, escapeDeepLinkPath, sanitizeUrl } from "core/utils"
|
||||||
|
|
||||||
export default class OperationTag extends React.Component {
|
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 tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"])
|
||||||
let tagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"])
|
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")
|
let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -61,11 +61,14 @@ export default class OperationTag extends React.Component {
|
|||||||
<h4
|
<h4
|
||||||
onClick={() => layoutActions.show(isShownKey, !showTag)}
|
onClick={() => layoutActions.show(isShownKey, !showTag)}
|
||||||
className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" }
|
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}
|
||||||
|
>
|
||||||
<DeepLink
|
<DeepLink
|
||||||
enabled={isDeepLinkingEnabled}
|
enabled={isDeepLinkingEnabled}
|
||||||
isShown={showTag}
|
isShown={showTag}
|
||||||
path={tag}
|
path={createDeepLinkPath(tag)}
|
||||||
text={tag} />
|
text={tag} />
|
||||||
{ !tagDescription ? <small></small> :
|
{ !tagDescription ? <small></small> :
|
||||||
<small>
|
<small>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ describe("Deep linking feature", () => {
|
|||||||
const swagger2BaseUrl = "/?deepLinking=true&url=/documents/features/deep-linking.swagger.yaml"
|
const swagger2BaseUrl = "/?deepLinking=true&url=/documents/features/deep-linking.swagger.yaml"
|
||||||
|
|
||||||
describe("regular Operation", () => {
|
describe("regular Operation", () => {
|
||||||
BaseDeeplinkTestFactory({
|
OperationDeeplinkTestFactory({
|
||||||
baseUrl: swagger2BaseUrl,
|
baseUrl: swagger2BaseUrl,
|
||||||
elementToGet: ".opblock-get",
|
elementToGet: ".opblock-get",
|
||||||
correctElementId: "operations-myTag-myOperation",
|
correctElementId: "operations-myTag-myOperation",
|
||||||
@@ -16,7 +16,7 @@ describe("Deep linking feature", () => {
|
|||||||
const elementToGet = ".opblock-post"
|
const elementToGet = ".opblock-post"
|
||||||
const correctFragment = "#/my%20Tag/my%20Operation"
|
const correctFragment = "#/my%20Tag/my%20Operation"
|
||||||
|
|
||||||
BaseDeeplinkTestFactory({
|
OperationDeeplinkTestFactory({
|
||||||
baseUrl: swagger2BaseUrl,
|
baseUrl: swagger2BaseUrl,
|
||||||
elementToGet,
|
elementToGet,
|
||||||
correctElementId: "operations-my_Tag-my_Operation",
|
correctElementId: "operations-my_Tag-my_Operation",
|
||||||
@@ -42,7 +42,7 @@ describe("Deep linking feature", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("Operation with underscores in tag+id", () => {
|
describe("Operation with underscores in tag+id", () => {
|
||||||
BaseDeeplinkTestFactory({
|
OperationDeeplinkTestFactory({
|
||||||
baseUrl: swagger2BaseUrl,
|
baseUrl: swagger2BaseUrl,
|
||||||
elementToGet: ".opblock-patch",
|
elementToGet: ".opblock-patch",
|
||||||
correctElementId: "operations-underscore_Tag-underscore_Operation",
|
correctElementId: "operations-underscore_Tag-underscore_Operation",
|
||||||
@@ -52,7 +52,7 @@ describe("Deep linking feature", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("Operation with UTF-16 characters", () => {
|
describe("Operation with UTF-16 characters", () => {
|
||||||
BaseDeeplinkTestFactory({
|
OperationDeeplinkTestFactory({
|
||||||
baseUrl: swagger2BaseUrl,
|
baseUrl: swagger2BaseUrl,
|
||||||
elementToGet: ".opblock-head",
|
elementToGet: ".opblock-head",
|
||||||
correctElementId: "operations-шеллы-пошел",
|
correctElementId: "operations-шеллы-пошел",
|
||||||
@@ -62,7 +62,7 @@ describe("Deep linking feature", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("Operation with no operationId", () => {
|
describe("Operation with no operationId", () => {
|
||||||
BaseDeeplinkTestFactory({
|
OperationDeeplinkTestFactory({
|
||||||
baseUrl: swagger2BaseUrl,
|
baseUrl: swagger2BaseUrl,
|
||||||
elementToGet: ".opblock-put",
|
elementToGet: ".opblock-put",
|
||||||
correctElementId: "operations-tagTwo-put_noOperationId",
|
correctElementId: "operations-tagTwo-put_noOperationId",
|
||||||
@@ -71,16 +71,25 @@ describe("Deep linking feature", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("regular Operation with `docExpansion: none` enabled", function() {
|
describe("regular Tag", () => {
|
||||||
it("should expand a tag", () => {
|
TagDeeplinkTestFactory({
|
||||||
cy.visit(`${swagger2BaseUrl}&docExpansion=none#/myTag`)
|
isTagCase: true,
|
||||||
.get(`.opblock-tag-section.is-open`)
|
baseUrl: swagger2BaseUrl,
|
||||||
.should("have.length", 1)
|
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`)
|
describe("Tag with whitespace", () => {
|
||||||
.should("have.length", 1)
|
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"
|
const openAPI3BaseUrl = "/?deepLinking=true&url=/documents/features/deep-linking.openapi.yaml"
|
||||||
|
|
||||||
describe("regular Operation", () => {
|
describe("regular Operation", () => {
|
||||||
BaseDeeplinkTestFactory({
|
OperationDeeplinkTestFactory({
|
||||||
baseUrl: openAPI3BaseUrl,
|
baseUrl: openAPI3BaseUrl,
|
||||||
elementToGet: ".opblock-get",
|
elementToGet: ".opblock-get",
|
||||||
correctElementId: "operations-myTag-myOperation",
|
correctElementId: "operations-myTag-myOperation",
|
||||||
@@ -101,7 +110,7 @@ describe("Deep linking feature", () => {
|
|||||||
const elementToGet = ".opblock-post"
|
const elementToGet = ".opblock-post"
|
||||||
const correctFragment = "#/my%20Tag/my%20Operation"
|
const correctFragment = "#/my%20Tag/my%20Operation"
|
||||||
|
|
||||||
BaseDeeplinkTestFactory({
|
OperationDeeplinkTestFactory({
|
||||||
baseUrl: openAPI3BaseUrl,
|
baseUrl: openAPI3BaseUrl,
|
||||||
elementToGet: ".opblock-post",
|
elementToGet: ".opblock-post",
|
||||||
correctElementId: "operations-my_Tag-my_Operation",
|
correctElementId: "operations-my_Tag-my_Operation",
|
||||||
@@ -128,7 +137,7 @@ describe("Deep linking feature", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("Operation with underscores in tag+id", () => {
|
describe("Operation with underscores in tag+id", () => {
|
||||||
BaseDeeplinkTestFactory({
|
OperationDeeplinkTestFactory({
|
||||||
baseUrl: openAPI3BaseUrl,
|
baseUrl: openAPI3BaseUrl,
|
||||||
elementToGet: ".opblock-patch",
|
elementToGet: ".opblock-patch",
|
||||||
correctElementId: "operations-underscore_Tag-underscore_Operation",
|
correctElementId: "operations-underscore_Tag-underscore_Operation",
|
||||||
@@ -138,7 +147,7 @@ describe("Deep linking feature", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("Operation with UTF-16 characters", () => {
|
describe("Operation with UTF-16 characters", () => {
|
||||||
BaseDeeplinkTestFactory({
|
OperationDeeplinkTestFactory({
|
||||||
baseUrl: openAPI3BaseUrl,
|
baseUrl: openAPI3BaseUrl,
|
||||||
elementToGet: ".opblock-head",
|
elementToGet: ".opblock-head",
|
||||||
correctElementId: "operations-шеллы-пошел",
|
correctElementId: "operations-шеллы-пошел",
|
||||||
@@ -148,7 +157,7 @@ describe("Deep linking feature", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("Operation with no operationId", () => {
|
describe("Operation with no operationId", () => {
|
||||||
BaseDeeplinkTestFactory({
|
OperationDeeplinkTestFactory({
|
||||||
baseUrl: openAPI3BaseUrl,
|
baseUrl: openAPI3BaseUrl,
|
||||||
elementToGet: ".opblock-put",
|
elementToGet: ".opblock-put",
|
||||||
correctElementId: "operations-tagTwo-put_noOperationId",
|
correctElementId: "operations-tagTwo-put_noOperationId",
|
||||||
@@ -157,22 +166,31 @@ describe("Deep linking feature", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("regular Operation with `docExpansion: none` enabled", function () {
|
describe("regular Tag", () => {
|
||||||
it("should expand a tag", () => {
|
TagDeeplinkTestFactory({
|
||||||
cy.visit(`${openAPI3BaseUrl}&docExpansion=none#/myTag`)
|
isTagCase: true,
|
||||||
.get(`.opblock-tag-section.is-open`)
|
baseUrl: openAPI3BaseUrl,
|
||||||
.should("have.length", 1)
|
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`)
|
describe("Tag with whitespace", () => {
|
||||||
.should("have.length", 1)
|
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", () => {
|
it("should generate a correct element ID", () => {
|
||||||
cy.visit(baseUrl)
|
cy.visit(baseUrl)
|
||||||
.get(elementToGet)
|
.get(elementToGet)
|
||||||
@@ -210,4 +228,60 @@ function BaseDeeplinkTestFactory({ baseUrl, elementToGet, correctElementId, corr
|
|||||||
.window()
|
.window()
|
||||||
.should("have.deep.property", "location.hash", correctFragment)
|
.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)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user