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:
kyle
2019-01-10 15:58:37 -06:00
committed by GitHub
parent 2db63e2c08
commit 1e8e0dba30
2 changed files with 110 additions and 33 deletions

View File

@@ -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>

View File

@@ -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)
})
} }