From 7d390363ec389ef77ec600bdcad1bd9bf197d938 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Thu, 2 May 2019 17:23:30 +0100 Subject: [PATCH 01/29] Added tooling for appending OAS3 relative URLs to selected Server --- src/core/components/info.jsx | 36 +++++++++++++++------------ src/core/components/operation-tag.jsx | 6 +++-- src/core/components/operation.jsx | 7 +++--- src/core/components/operations.jsx | 3 +++ src/core/containers/info.jsx | 6 +++-- src/core/utils.js | 19 +++++++++++--- 6 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/core/components/info.jsx b/src/core/components/info.jsx index a746ef6fb41..62ca4bb4a39 100644 --- a/src/core/components/info.jsx +++ b/src/core/components/info.jsx @@ -2,7 +2,8 @@ import React from "react" import PropTypes from "prop-types" import { fromJS } from "immutable" import ImPropTypes from "react-immutable-proptypes" -import { sanitizeUrl } from "core/utils" +import { sanitizeUrl, buildUrl } from "core/utils" + export class InfoBasePath extends React.Component { @@ -26,13 +27,14 @@ export class InfoBasePath extends React.Component { class Contact extends React.Component { static propTypes = { data: PropTypes.object, - getComponent: PropTypes.func.isRequired + getComponent: PropTypes.func.isRequired, + selectedServer: PropTypes.string, } render(){ - let { data, getComponent } = this.props + let { data, getComponent, selectedServer } = this.props let name = data.get("name") || "the developer" - let url = data.get("url") + let url = buildUrl(data.get("url"), selectedServer) let email = data.get("email") const Link = getComponent("Link") @@ -53,17 +55,16 @@ class Contact extends React.Component { class License extends React.Component { static propTypes = { license: PropTypes.object, - getComponent: PropTypes.func.isRequired - + getComponent: PropTypes.func.isRequired, + selectedServer: PropTypes.string, } render(){ - let { license, getComponent } = this.props + let { license, getComponent, selectedServer } = this.props const Link = getComponent("Link") - let name = license.get("name") || "License" - let url = license.get("url") + let url = buildUrl(license.get("url"), selectedServer) return (
@@ -82,13 +83,13 @@ export class InfoUrl extends React.PureComponent { getComponent: PropTypes.func.isRequired } - + render() { const { url, getComponent } = this.props const Link = getComponent("Link") - return { url } + return { url } !!! } } @@ -100,17 +101,20 @@ export default class Info extends React.Component { basePath: PropTypes.string, externalDocs: ImPropTypes.map, getComponent: PropTypes.func.isRequired, + oas3selectors: PropTypes.func, + selectedServer: PropTypes.string, } render() { - let { info, url, host, basePath, getComponent, externalDocs } = this.props + let { info, url, host, basePath, getComponent, externalDocs, selectedServer } = this.props let version = info.get("version") let description = info.get("description") let title = info.get("title") - let termsOfService = info.get("termsOfService") + let termsOfService = buildUrl(info.get("termsOfService"), selectedServer) let contact = info.get("contact") let license = info.get("license") - const { url:externalDocsUrl, description:externalDocsDescription } = (externalDocs || fromJS({})).toJS() + let { url:externalDocsUrl, description:externalDocsDescription } = (externalDocs || fromJS({})).toJS() + externalDocsUrl = buildUrl( externalDocs.url, selectedServer ) const Markdown = getComponent("Markdown") const Link = getComponent("Link") @@ -138,8 +142,8 @@ export default class Info extends React.Component {
} - {contact && contact.size ? : null } - {license && license.size ? : null } + {contact && contact.size ? : null } + {license && license.size ? : null } { externalDocsUrl ? {externalDocsDescription || externalDocsUrl} : null } diff --git a/src/core/components/operation-tag.jsx b/src/core/components/operation-tag.jsx index dddf6b028b9..37159a2982d 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, escapeDeepLinkPath, sanitizeUrl } from "core/utils" +import { createDeepLinkPath, escapeDeepLinkPath, sanitizeUrl, buildUrl } from "core/utils" export default class OperationTag extends React.Component { @@ -15,6 +15,7 @@ export default class OperationTag extends React.Component { tagObj: ImPropTypes.map.isRequired, tag: PropTypes.string.isRequired, + oas3Selectors: PropTypes.func.isRequired, layoutSelectors: PropTypes.object.isRequired, layoutActions: PropTypes.object.isRequired, @@ -29,7 +30,7 @@ export default class OperationTag extends React.Component { tagObj, tag, children, - + oas3Selectors, layoutSelectors, layoutActions, getConfigs, @@ -51,6 +52,7 @@ export default class OperationTag extends React.Component { let tagDescription = tagObj.getIn(["tagDetails", "description"], null) let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"]) let tagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"]) + tagExternalDocsUrl = buildUrl( tagExternalDocsUrl, oas3Selectors.selectedServer() ) let isShownKey = ["operations-tag", tag] let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list") diff --git a/src/core/components/operation.jsx b/src/core/components/operation.jsx index f441a5d0fbe..68b26d208dc 100644 --- a/src/core/components/operation.jsx +++ b/src/core/components/operation.jsx @@ -1,7 +1,7 @@ import React, { PureComponent } from "react" import PropTypes from "prop-types" import { getList } from "core/utils" -import { getExtensions, sanitizeUrl, escapeDeepLinkPath } from "core/utils" +import { getExtensions, sanitizeUrl, escapeDeepLinkPath, buildUrl } from "core/utils" import { Iterable, List } from "immutable" import ImPropTypes from "react-immutable-proptypes" @@ -81,6 +81,7 @@ export default class Operation extends PureComponent { schemes } = op + const externalDocsUrl = externalDocs ? buildUrl(externalDocs.url, oas3Selectors.selectedServer()) : "" let operation = operationProps.getIn(["op"]) let responses = operation.get("responses") let parameters = getList(operation, ["parameters"]) @@ -127,14 +128,14 @@ export default class Operation extends PureComponent { } { - externalDocs && externalDocs.url ? + externalDocsUrl ?

Find more details

- {externalDocs.url} + {externalDocsUrl}
: null } diff --git a/src/core/components/operations.jsx b/src/core/components/operations.jsx index 68244177452..8987f9fa283 100644 --- a/src/core/components/operations.jsx +++ b/src/core/components/operations.jsx @@ -16,6 +16,7 @@ export default class Operations extends React.Component { specActions: PropTypes.object.isRequired, oas3Actions: PropTypes.object.isRequired, getComponent: PropTypes.func.isRequired, + oas3Selectors: PropTypes.func.isRequired, layoutSelectors: PropTypes.object.isRequired, layoutActions: PropTypes.object.isRequired, authActions: PropTypes.object.isRequired, @@ -28,6 +29,7 @@ export default class Operations extends React.Component { let { specSelectors, getComponent, + oas3Selectors, layoutSelectors, layoutActions, getConfigs, @@ -65,6 +67,7 @@ export default class Operations extends React.Component { key={"operation-" + tag} tagObj={tagObj} tag={tag} + oas3Selectors={oas3Selectors} layoutSelectors={layoutSelectors} layoutActions={layoutActions} getConfigs={getConfigs} diff --git a/src/core/containers/info.jsx b/src/core/containers/info.jsx index 4ae558c7d50..ebf01a1f71d 100644 --- a/src/core/containers/info.jsx +++ b/src/core/containers/info.jsx @@ -7,16 +7,18 @@ export default class InfoContainer extends React.Component { specActions: PropTypes.object.isRequired, specSelectors: PropTypes.object.isRequired, getComponent: PropTypes.func.isRequired, + oas3Selectors: PropTypes.func.isRequired, } render () { - const {specSelectors, getComponent} = this.props + const {specSelectors, getComponent, oas3Selectors} = this.props const info = specSelectors.info() const url = specSelectors.url() const basePath = specSelectors.basePath() const host = specSelectors.host() const externalDocs = specSelectors.externalDocs() + const selectedServer = oas3Selectors.selectedServer() const Info = getComponent("info") @@ -24,7 +26,7 @@ export default class InfoContainer extends React.Component {
{info && info.count() ? ( + getComponent={getComponent} selectedServer={selectedServer} /> ) : null}
) diff --git a/src/core/utils.js b/src/core/utils.js index ccc27c366e6..6254e7de8b7 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -349,13 +349,13 @@ export function extractFileNameFromContentDispositionHeader(value){ /filename="([^;]*);?"/i, /filename=([^;]*);?/i ] - + let responseFilename patterns.some(regex => { responseFilename = regex.exec(value) return responseFilename !== null }) - + if (responseFilename !== null && responseFilename.length > 1) { try { return decodeURIComponent(responseFilename[1]) @@ -722,6 +722,17 @@ export function sanitizeUrl(url) { return braintreeSanitizeUrl(url) } +export function isAbsoluteUrl(url) { + return url.match(/^(?:[a-z]+:)?\/\//i) // Matches http://, HTTP://, https://, ftp://, //example.com, +} + +export function buildUrl(url="", selectedServer="") { + if(!url) return "" + if(isAbsoluteUrl(url)) return url + if(!url.startsWith("/")) url = "/" + url // Add '/' to start of relative URL, if missing + return selectedServer ? selectedServer.replace(/\/$/, "") + url : url // Remove '/' from server, if present +} + export function getAcceptControllingResponse(responses) { if(!Im.OrderedMap.isOrderedMap(responses)) { // wrong type! @@ -810,7 +821,7 @@ export function paramToIdentifier(param, { returnAll = false, allowHashes = true } const paramName = param.get("name") const paramIn = param.get("in") - + let generatedIdentifiers = [] // Generate identifiers in order of most to least specificity @@ -818,7 +829,7 @@ export function paramToIdentifier(param, { returnAll = false, allowHashes = true if (param && param.hashCode && paramIn && paramName && allowHashes) { generatedIdentifiers.push(`${paramIn}.${paramName}.hash-${param.hashCode()}`) } - + if(paramIn && paramName) { generatedIdentifiers.push(`${paramIn}.${paramName}`) } From 619cbfec281aef4cb20880cc0b2fb9daf2c437f8 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Fri, 3 May 2019 11:31:30 +0100 Subject: [PATCH 02/29] Modified buildUrl util to append realtive URLs beginning with '/' to Server host --- src/core/utils.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/utils.js b/src/core/utils.js index 6254e7de8b7..f8b6c845f36 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -728,9 +728,11 @@ export function isAbsoluteUrl(url) { export function buildUrl(url="", selectedServer="") { if(!url) return "" - if(isAbsoluteUrl(url)) return url - if(!url.startsWith("/")) url = "/" + url // Add '/' to start of relative URL, if missing - return selectedServer ? selectedServer.replace(/\/$/, "") + url : url // Remove '/' from server, if present + if(isAbsoluteUrl(url) || !selectedServer) return url + + const selectedServerObj = new URL(selectedServer) + if(url.startsWith("/")) return `${selectedServerObj.protocol}${selectedServerObj.host}${url}` + return selectedServer + url } export function getAcceptControllingResponse(responses) { From 9d431427959283c9e9dd360f67179a23a836e9e9 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Fri, 3 Jul 2020 10:10:24 +0100 Subject: [PATCH 03/29] Removed erroroneous exclamations marks! --- src/core/components/info.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/components/info.jsx b/src/core/components/info.jsx index 65d211c3738..f37165fb1a4 100644 --- a/src/core/components/info.jsx +++ b/src/core/components/info.jsx @@ -89,7 +89,7 @@ export class InfoUrl extends React.PureComponent { const Link = getComponent("Link") - return { url } !!! + return { url } } } From 79d61e4d33eec302aade426f9769a1e358413f56 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Fri, 3 Jul 2020 10:12:33 +0100 Subject: [PATCH 04/29] Updated varibale to `termsOfServiceUrl` --- src/core/components/info.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/components/info.jsx b/src/core/components/info.jsx index f37165fb1a4..9129655e080 100644 --- a/src/core/components/info.jsx +++ b/src/core/components/info.jsx @@ -110,7 +110,7 @@ export default class Info extends React.Component { let version = info.get("version") let description = info.get("description") let title = info.get("title") - let termsOfService = buildUrl(info.get("termsOfService"), selectedServer) + let termsOfServiceUrl = buildUrl(info.get("termsOfService"), selectedServer) let contact = info.get("contact") let license = info.get("license") let { url:externalDocsUrl, description:externalDocsDescription } = (externalDocs || fromJS({})).toJS() @@ -138,7 +138,7 @@ export default class Info extends React.Component { { termsOfService &&
- Terms of service + Terms of service
} From e21857468f07a057cc3d1b78d23201630725c3f4 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Fri, 3 Jul 2020 10:20:48 +0100 Subject: [PATCH 05/29] Changed how externalDocs url & description are retrieved and named --- src/core/components/info.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/components/info.jsx b/src/core/components/info.jsx index 9129655e080..210d9e7ef50 100644 --- a/src/core/components/info.jsx +++ b/src/core/components/info.jsx @@ -113,8 +113,9 @@ export default class Info extends React.Component { let termsOfServiceUrl = buildUrl(info.get("termsOfService"), selectedServer) let contact = info.get("contact") let license = info.get("license") - let { url:externalDocsUrl, description:externalDocsDescription } = (externalDocs || fromJS({})).toJS() - externalDocsUrl = buildUrl( externalDocs.url, selectedServer ) + let rawExternalDocsUrl = externalDocs.get("url") + let externalDocsUrl = buildUrl( rawExternalDocsUrl, selectedServer ) + let externalDocsDescription = externalDocs.get("description") const Markdown = getComponent("Markdown", true) const Link = getComponent("Link") From 1c5b39b9b27cd284545c271cbffe66e9501f68d8 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Fri, 3 Jul 2020 10:23:28 +0100 Subject: [PATCH 06/29] Changed naming of `tagExternalDocsUrl` --- src/core/components/operation-tag.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/components/operation-tag.jsx b/src/core/components/operation-tag.jsx index 67d40e98bc1..970ba009cea 100644 --- a/src/core/components/operation-tag.jsx +++ b/src/core/components/operation-tag.jsx @@ -51,8 +51,8 @@ export default class OperationTag extends React.Component { let tagDescription = tagObj.getIn(["tagDetails", "description"], null) let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"]) - let tagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"]) - tagExternalDocsUrl = buildUrl( tagExternalDocsUrl, oas3Selectors.selectedServer() ) + let rawTagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"]) + let tagExternalDocsUrl = buildUrl( rawTagExternalDocsUrl, oas3Selectors.selectedServer() ) let isShownKey = ["operations-tag", tag] let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list") From b44ac986a071b3d9c78f0cbd6076b644594f1fce Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Mon, 20 Jul 2020 16:32:46 +0100 Subject: [PATCH 07/29] Added unit test for util `isAbsoluteUrl()` --- test/mocha/core/utils.js | 425 ++++++++++++++++++++------------------- 1 file changed, 222 insertions(+), 203 deletions(-) diff --git a/test/mocha/core/utils.js b/test/mocha/core/utils.js index df7b9a9d4b3..597df1b8ba2 100644 --- a/test/mocha/core/utils.js +++ b/test/mocha/core/utils.js @@ -23,6 +23,7 @@ import { getExtensions, getCommonExtensions, sanitizeUrl, + isAbsoluteUrl, requiresValidationURL, extractFileNameFromContentDispositionHeader, deeplyStripKey, @@ -35,9 +36,9 @@ import { import win from "core/window" describe("utils", function() { - + describe("mapToList", function(){ - + it("should convert a map to a list, setting `key`", function(){ // With const aMap = fromJS({ @@ -48,17 +49,17 @@ describe("utils", function() { two: 2, } }) - + // When const aList = mapToList(aMap, "someKey") - + // Then expect(aList.toJS()).toEqual([ { someKey: "a", one: 1 }, { someKey: "b", two: 2 }, ]) }) - + it("should flatten an arbitrarily deep map", function(){ // With const aMap = fromJS({ @@ -76,97 +77,97 @@ describe("utils", function() { } } }) - + // When const aList = mapToList(aMap, ["levelA", "levelB"]) - + // Then expect(aList.toJS()).toEqual([ { levelA: "a", levelB: "one", alpha: true }, { levelA: "b", levelB: "two", bravo: true }, { levelA: "b", levelB: "three", charlie: true }, ]) - + }) - + it("should handle an empty map", function(){ // With const aMap = fromJS({}) - + // When const aList = mapToList(aMap, ["levelA", "levelB"]) - + // Then expect(aList.toJS()).toEqual([]) }) - + }) - + describe("extractFileNameFromContentDispositionHeader", function(){ it("should extract quoted filename", function(){ let cdHeader = "attachment; filename=\"file name.jpg\"" let expectedResult = "file name.jpg" expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult) }) - + it("should extract filename", function(){ let cdHeader = "attachment; filename=filename.jpg" let expectedResult = "filename.jpg" expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult) }) - + it("should extract quoted filename in utf-8", function(){ let cdHeader = "attachment; filename*=UTF-8''\"%D1%84%D0%B0%D0%B9%D0%BB.txt\"" let expectedResult = "файл.txt" expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult) }) - + it("should extract filename in utf-8", function(){ let cdHeader = "attachment; filename*=utf-8'ru'%D1%84%D0%B0%D0%B9%D0%BB.txt" let expectedResult = "файл.txt" expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult) }) - + it("should not extract filename and return null", function(){ let cdHeader = "attachment; no file name provided" let expectedResult = null expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult) }) }) - + describe("validateMaximum", function() { let errorMessage = "Value must be less than Maximum" - + it("doesn't return for valid input", function() { expect(validateMaximum(9, 10)).toBeFalsy() expect(validateMaximum(19, 20)).toBeFalsy() }) - + it("returns a message for invalid input", function() { expect(validateMaximum(1, 0)).toEqual(errorMessage) expect(validateMaximum(10, 9)).toEqual(errorMessage) expect(validateMaximum(20, 19)).toEqual(errorMessage) }) }) - + describe("validateMinimum", function() { let errorMessage = "Value must be greater than Minimum" - + it("doesn't return for valid input", function() { expect(validateMinimum(2, 1)).toBeFalsy() expect(validateMinimum(20, 10)).toBeFalsy() }) - + it("returns a message for invalid input", function() { expect(validateMinimum(-1, 0)).toEqual(errorMessage) expect(validateMinimum(1, 2)).toEqual(errorMessage) expect(validateMinimum(10, 20)).toEqual(errorMessage) }) }) - + describe("validateNumber", function() { let errorMessage = "Value must be a number" - + it("doesn't return for whole numbers", function() { expect(validateNumber(0)).toBeFalsy() expect(validateNumber(1)).toBeFalsy() @@ -178,25 +179,25 @@ describe("utils", function() { expect(validateNumber(-20)).toBeFalsy() expect(validateNumber(-5000000)).toBeFalsy() }) - + it("doesn't return for negative numbers", function() { expect(validateNumber(-1)).toBeFalsy() expect(validateNumber(-20)).toBeFalsy() expect(validateNumber(-5000000)).toBeFalsy() }) - + it("doesn't return for decimal numbers", function() { expect(validateNumber(1.1)).toBeFalsy() expect(validateNumber(2.5)).toBeFalsy() expect(validateNumber(-30.99)).toBeFalsy() }) - + it("returns a message for strings", function() { expect(validateNumber("")).toEqual(errorMessage) expect(validateNumber(" ")).toEqual(errorMessage) expect(validateNumber("test")).toEqual(errorMessage) }) - + it("returns a message for invalid input", function() { expect(validateNumber(undefined)).toEqual(errorMessage) expect(validateNumber(null)).toEqual(errorMessage) @@ -206,10 +207,10 @@ describe("utils", function() { expect(validateNumber(false)).toEqual(errorMessage) }) }) - + describe("validateInteger", function() { let errorMessage = "Value must be an integer" - + it("doesn't return for positive integers", function() { expect(validateInteger(0)).toBeFalsy() expect(validateInteger(1)).toBeFalsy() @@ -221,25 +222,25 @@ describe("utils", function() { expect(validateInteger(-20)).toBeFalsy() expect(validateInteger(-5000000)).toBeFalsy() }) - + it("doesn't return for negative integers", function() { expect(validateInteger(-1)).toBeFalsy() expect(validateInteger(-20)).toBeFalsy() expect(validateInteger(-5000000)).toBeFalsy() }) - + it("returns a message for decimal values", function() { expect(validateInteger(1.1)).toEqual(errorMessage) expect(validateInteger(2.5)).toEqual(errorMessage) expect(validateInteger(-30.99)).toEqual(errorMessage) }) - + it("returns a message for strings", function() { expect(validateInteger("")).toEqual(errorMessage) expect(validateInteger(" ")).toEqual(errorMessage) expect(validateInteger("test")).toEqual(errorMessage) }) - + it("returns a message for invalid input", function() { expect(validateInteger(undefined)).toEqual(errorMessage) expect(validateInteger(null)).toEqual(errorMessage) @@ -249,10 +250,10 @@ describe("utils", function() { expect(validateInteger(false)).toEqual(errorMessage) }) }) - + describe("validateFile", function() { let errorMessage = "Value must be a file" - + it("validates against objects which are instances of 'File'", function() { let fileObj = new win.File([], "Test File") expect(validateFile(fileObj)).toBeFalsy() @@ -262,23 +263,23 @@ describe("utils", function() { expect(validateFile("string")).toEqual(errorMessage) }) }) - + describe("validateDateTime", function() { let errorMessage = "Value must be a DateTime" - + it("doesn't return for valid dates", function() { expect(validateDateTime("Mon, 25 Dec 1995 13:30:00 +0430")).toBeFalsy() }) - + it("returns a message for invalid input'", function() { expect(validateDateTime(null)).toEqual(errorMessage) expect(validateDateTime("string")).toEqual(errorMessage) }) }) - + describe("validateGuid", function() { let errorMessage = "Value must be a Guid" - + it("doesn't return for valid guid", function() { expect(validateGuid("8ce4811e-cec5-4a29-891a-15d1917153c1")).toBeFalsy() expect(validateGuid("{8ce4811e-cec5-4a29-891a-15d1917153c1}")).toBeFalsy() @@ -287,56 +288,56 @@ describe("utils", function() { expect(validateGuid("6FFEFD8E-A018-E811-BBF9-60F67727D806")).toBeFalsy() expect(validateGuid("00000000-0000-0000-0000-000000000000")).toBeFalsy() }) - + it("returns a message for invalid input'", function() { expect(validateGuid(1)).toEqual(errorMessage) expect(validateGuid("string")).toEqual(errorMessage) }) }) - + describe("validateMaxLength", function() { let errorMessage = "Value must be less than MaxLength" - + it("doesn't return for valid guid", function() { expect(validateMaxLength("a", 1)).toBeFalsy() expect(validateMaxLength("abc", 5)).toBeFalsy() }) - + it("returns a message for invalid input'", function() { expect(validateMaxLength("abc", 0)).toEqual(errorMessage) expect(validateMaxLength("abc", 1)).toEqual(errorMessage) expect(validateMaxLength("abc", 2)).toEqual(errorMessage) }) }) - + describe("validateMinLength", function() { let errorMessage = "Value must be greater than MinLength" - + it("doesn't return for valid guid", function() { expect(validateMinLength("a", 1)).toBeFalsy() expect(validateMinLength("abc", 2)).toBeFalsy() }) - + it("returns a message for invalid input'", function() { expect(validateMinLength("abc", 5)).toEqual(errorMessage) expect(validateMinLength("abc", 8)).toEqual(errorMessage) }) }) - + describe("validatePattern", function() { let rxPattern = "^(red|blue)" let errorMessage = "Value must follow pattern " + rxPattern - + it("doesn't return for a match", function() { expect(validatePattern("red", rxPattern)).toBeFalsy() expect(validatePattern("blue", rxPattern)).toBeFalsy() }) - + it("returns a message for invalid pattern", function() { expect(validatePattern("pink", rxPattern)).toEqual(errorMessage) expect(validatePattern("123", rxPattern)).toEqual(errorMessage) }) - + it("fails gracefully when an invalid regex value is passed", function() { expect(() => validatePattern("aValue", "---")).toNotThrow() expect(() => validatePattern("aValue", 1234)).toNotThrow() @@ -344,17 +345,17 @@ describe("utils", function() { expect(() => validatePattern("aValue", [])).toNotThrow() }) }) - + describe("validateParam", function() { let param = null let value = null let result = null - + const assertValidateParam = (param, value, expectedError) => { // Swagger 2.0 version result = validateParam( fromJS(param), fromJS(value)) expect( result ).toEqual( expectedError ) - + // OAS3 version, using `schema` sub-object let oas3Param = { required: param.required, @@ -368,7 +369,7 @@ describe("utils", function() { }) expect( result ).toEqual( expectedError ) } - + const assertValidateOas3Param = (param, value, expectedError) => { // for cases where you _only_ want to try OAS3 result = validateParam(fromJS(param), value, { @@ -376,7 +377,7 @@ describe("utils", function() { }) expect( result ).toEqual( expectedError ) } - + it("should check the isOAS3 flag when validating parameters", function() { // This should "skip" validation because there is no `schema` property // and we are telling `validateParam` this is an OAS3 spec @@ -389,7 +390,7 @@ describe("utils", function() { } ) expect( result ).toEqual( [] ) }) - + it("validates required OAS3 objects", function() { // valid object param = { @@ -402,7 +403,7 @@ describe("utils", function() { abc: 123 } assertValidateOas3Param(param, value, []) - + // valid object-as-string param = { required: true, @@ -414,7 +415,7 @@ describe("utils", function() { abc: 123 }) assertValidateOas3Param(param, value, []) - + // invalid object-as-string param = { required: true, @@ -424,7 +425,7 @@ describe("utils", function() { } value = "{{}" assertValidateOas3Param(param, value, ["Parameter string value must be valid JSON"]) - + // missing when required param = { required: true, @@ -435,7 +436,7 @@ describe("utils", function() { value = undefined assertValidateOas3Param(param, value, ["Required field is not provided"]) }) - + it("validates optional OAS3 objects", function() { // valid object param = { @@ -447,7 +448,7 @@ describe("utils", function() { abc: 123 } assertValidateOas3Param(param, value, []) - + // valid object-as-string param = { schema: { @@ -458,7 +459,7 @@ describe("utils", function() { abc: 123 }) assertValidateOas3Param(param, value, []) - + // invalid object-as-string param = { schema: { @@ -467,7 +468,7 @@ describe("utils", function() { } value = "{{}" assertValidateOas3Param(param, value, ["Parameter string value must be valid JSON"]) - + // missing when not required param = { schema: { @@ -477,7 +478,7 @@ describe("utils", function() { value = undefined assertValidateOas3Param(param, value, []) }) - + it("validates required strings", function() { // invalid string param = { @@ -486,7 +487,7 @@ describe("utils", function() { } value = "" assertValidateParam(param, value, ["Required field is not provided"]) - + // valid string param = { required: true, @@ -494,7 +495,7 @@ describe("utils", function() { } value = "test string" assertValidateParam(param, value, []) - + // valid string with min and max length param = { required: true, @@ -505,7 +506,7 @@ describe("utils", function() { value = "test string" assertValidateParam(param, value, []) }) - + it("handles OAS3 `Parameter.content`", function() { // invalid string param = { @@ -520,7 +521,7 @@ describe("utils", function() { } value = "" assertValidateOas3Param(param, value, ["Required field is not provided"]) - + // valid string param = { content: { @@ -535,7 +536,7 @@ describe("utils", function() { value = "test string" assertValidateOas3Param(param, value, []) - + // invalid (empty) JSON string param = { content: { @@ -564,7 +565,7 @@ describe("utils", function() { value = "{{}" assertValidateOas3Param(param, value, ["Parameter string value must be valid JSON"]) - + // valid (empty object) JSON string param = { content: { @@ -578,7 +579,7 @@ describe("utils", function() { } value = "{}" assertValidateOas3Param(param, value, []) - + // valid (empty object) JSON object param = { content: { @@ -592,7 +593,7 @@ describe("utils", function() { } value = {} assertValidateOas3Param(param, value, []) - + // should skip JSON validation for non-JSON media types param = { content: { @@ -607,7 +608,7 @@ describe("utils", function() { value = "{{}" assertValidateOas3Param(param, value, []) }) - + it("validates required strings with min and max length", function() { // invalid string with max length param = { @@ -617,7 +618,7 @@ describe("utils", function() { } value = "test string" assertValidateParam(param, value, ["Value must be less than MaxLength"]) - + // invalid string with max length 0 param = { required: true, @@ -626,7 +627,7 @@ describe("utils", function() { } value = "test string" assertValidateParam(param, value, ["Value must be less than MaxLength"]) - + // invalid string with min length param = { required: true, @@ -636,7 +637,7 @@ describe("utils", function() { value = "test string" assertValidateParam(param, value, ["Value must be greater than MinLength"]) }) - + it("validates optional strings", function() { // valid (empty) string param = { @@ -645,7 +646,7 @@ describe("utils", function() { } value = "" assertValidateParam(param, value, []) - + // valid string param = { required: false, @@ -654,7 +655,7 @@ describe("utils", function() { value = "test" assertValidateParam(param, value, []) }) - + it("validates required files", function() { // invalid file param = { @@ -663,7 +664,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, ["Required field is not provided"]) - + // valid file param = { required: true, @@ -672,7 +673,7 @@ describe("utils", function() { value = new win.File() assertValidateParam(param, value, []) }) - + it("validates optional files", function() { // invalid file param = { @@ -681,7 +682,7 @@ describe("utils", function() { } value = "not a file" assertValidateParam(param, value, ["Value must be a file"]) - + // valid (empty) file param = { required: false, @@ -689,7 +690,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, []) - + // valid file param = { required: false, @@ -698,7 +699,7 @@ describe("utils", function() { value = new win.File() assertValidateParam(param, value, []) }) - + it("validates required arrays", function() { // invalid (empty) array param = { @@ -715,7 +716,7 @@ describe("utils", function() { } value = "" assertValidateParam(param, value, ["Required field is not provided"]) - + // invalid (not an array) param = { required: true, @@ -723,7 +724,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, ["Required field is not provided"]) - + // invalid array, items do not match correct type param = { required: true, @@ -734,7 +735,7 @@ describe("utils", function() { } value = [1] assertValidateParam(param, value, [{index: 0, error: "Value must be a string"}]) - + // valid array, with no 'type' for items param = { required: true, @@ -750,7 +751,7 @@ describe("utils", function() { } value = "[1]" assertValidateParam(param, value, []) - + // valid array, items match type param = { required: true, @@ -762,7 +763,7 @@ describe("utils", function() { value = ["1"] assertValidateParam(param, value, []) }) - + it("validates optional arrays", function() { // valid, empty array param = { @@ -771,7 +772,7 @@ describe("utils", function() { } value = [] assertValidateParam(param, value, []) - + // invalid, items do not match correct type param = { required: false, @@ -782,7 +783,7 @@ describe("utils", function() { } value = ["number"] assertValidateParam(param, value, [{index: 0, error: "Value must be a number"}]) - + // valid param = { required: false, @@ -794,7 +795,7 @@ describe("utils", function() { value = ["test"] assertValidateParam(param, value, []) }) - + it("validates required booleans", function() { // invalid boolean value param = { @@ -803,7 +804,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, ["Required field is not provided"]) - + // invalid boolean value (not a boolean) param = { required: true, @@ -811,7 +812,7 @@ describe("utils", function() { } value = "test string" assertValidateParam(param, value, ["Value must be a boolean"]) - + // valid boolean value param = { required: true, @@ -819,7 +820,7 @@ describe("utils", function() { } value = "true" assertValidateParam(param, value, []) - + // valid boolean value param = { required: true, @@ -828,7 +829,7 @@ describe("utils", function() { value = false assertValidateParam(param, value, []) }) - + it("validates optional booleans", function() { // valid (empty) boolean value param = { @@ -837,7 +838,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, []) - + // invalid boolean value (not a boolean) param = { required: false, @@ -845,7 +846,7 @@ describe("utils", function() { } value = "test string" assertValidateParam(param, value, ["Value must be a boolean"]) - + // valid boolean value param = { required: false, @@ -853,7 +854,7 @@ describe("utils", function() { } value = "true" assertValidateParam(param, value, []) - + // valid boolean value param = { required: false, @@ -862,7 +863,7 @@ describe("utils", function() { value = false assertValidateParam(param, value, []) }) - + it("validates required numbers", function() { // invalid number, string instead of a number param = { @@ -871,7 +872,7 @@ describe("utils", function() { } value = "test" assertValidateParam(param, value, ["Value must be a number"]) - + // invalid number, undefined value param = { required: true, @@ -879,7 +880,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, ["Required field is not provided"]) - + // valid number with min and max param = { required: true, @@ -889,7 +890,7 @@ describe("utils", function() { } value = 10 assertValidateParam(param, value, []) - + // valid negative number with min and max param = { required: true, @@ -899,17 +900,17 @@ describe("utils", function() { } value = -10 assertValidateParam(param, value, []) - + // invalid number with maximum:0 param = { required: true, type: "number", - + maximum: 0 } value = 1 assertValidateParam(param, value, ["Value must be less than Maximum"]) - + // invalid number with minimum:0 param = { required: true, @@ -919,7 +920,7 @@ describe("utils", function() { value = -10 assertValidateParam(param, value, ["Value must be greater than Minimum"]) }) - + it("validates optional numbers", function() { // invalid number, string instead of a number param = { @@ -928,7 +929,7 @@ describe("utils", function() { } value = "test" assertValidateParam(param, value, ["Value must be a number"]) - + // valid (empty) number param = { required: false, @@ -936,7 +937,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, []) - + // valid number param = { required: false, @@ -945,7 +946,7 @@ describe("utils", function() { value = 10 assertValidateParam(param, value, []) }) - + it("validates required integers", function() { // invalid integer, string instead of an integer param = { @@ -954,7 +955,7 @@ describe("utils", function() { } value = "test" assertValidateParam(param, value, ["Value must be an integer"]) - + // invalid integer, undefined value param = { required: true, @@ -962,7 +963,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, ["Required field is not provided"]) - + // valid integer, but 0 is falsy in JS param = { required: true, @@ -970,7 +971,7 @@ describe("utils", function() { } value = 0 assertValidateParam(param, value, []) - + // valid integer param = { required: true, @@ -979,7 +980,7 @@ describe("utils", function() { value = 10 assertValidateParam(param, value, []) }) - + it("validates optional integers", function() { // invalid integer, string instead of an integer param = { @@ -988,7 +989,7 @@ describe("utils", function() { } value = "test" assertValidateParam(param, value, ["Value must be an integer"]) - + // valid (empty) integer param = { required: false, @@ -996,7 +997,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, []) - + // integers param = { required: false, @@ -1006,34 +1007,34 @@ describe("utils", function() { assertValidateParam(param, value, []) }) }) - + describe("fromJSOrdered", () => { it("should create an OrderedMap from an object", () => { const param = { value: "test" } - + const result = fromJSOrdered(param).toJS() expect( result ).toEqual( { value: "test" } ) }) - + it("should not use an object's length property for Map size", () => { const param = { length: 5 } - + const result = fromJSOrdered(param).toJS() expect( result ).toEqual( { length: 5 } ) }) - + it("should create an OrderedMap from an array", () => { const param = [1, 1, 2, 3, 5, 8] - + const result = fromJSOrdered(param).toJS() expect( result ).toEqual( [1, 1, 2, 3, 5, 8] ) }) }) - + describe("getAcceptControllingResponse", () => { it("should return the first 2xx response with a media type", () => { const responses = fromJSOrdered({ @@ -1056,7 +1057,7 @@ describe("utils", function() { } } }) - + expect(getAcceptControllingResponse(responses)).toEqual(responses.get("200")) }) it("should skip 2xx responses without defined media types", () => { @@ -1080,7 +1081,7 @@ describe("utils", function() { } } }) - + expect(getAcceptControllingResponse(responses)).toEqual(responses.get("201")) }) it("should default to the `default` response if it has defined media types", () => { @@ -1101,7 +1102,7 @@ describe("utils", function() { } } }) - + expect(getAcceptControllingResponse(responses)).toEqual(responses.get("default")) }) it("should return null if there are no suitable controlling responses", () => { @@ -1116,40 +1117,40 @@ describe("utils", function() { description: "also empty.." } }) - + expect(getAcceptControllingResponse(responses)).toBe(null) }) it("should return null if an empty OrderedMap is passed", () => { const responses = fromJSOrdered() - + expect(getAcceptControllingResponse(responses)).toBe(null) }) it("should return null if anything except an OrderedMap is passed", () => { const responses = {} - + expect(getAcceptControllingResponse(responses)).toBe(null) }) }) - + describe("createDeepLinkPath", function() { it("creates a deep link path replacing spaces with underscores", function() { const result = createDeepLinkPath("tag id with spaces") expect(result).toEqual("tag%20id%20with%20spaces") }) - + it("trims input when creating a deep link path", function() { let result = createDeepLinkPath(" spaces before and after ") expect(result).toEqual("spaces%20before%20and%20after") - + result = createDeepLinkPath(" ") expect(result).toEqual("") }) - + it("creates a deep link path with special characters", function() { const result = createDeepLinkPath("!@#$%^&*(){}[]") expect(result).toEqual("!@#$%^&*(){}[]") }) - + it("returns an empty string for invalid input", function() { expect( createDeepLinkPath(null) ).toEqual("") expect( createDeepLinkPath(undefined) ).toEqual("") @@ -1158,39 +1159,39 @@ describe("utils", function() { expect( createDeepLinkPath({}) ).toEqual("") }) }) - + describe("escapeDeepLinkPath", function() { it("creates and escapes a deep link path", function() { const result = escapeDeepLinkPath("tag id with spaces?") expect(result).toEqual("tag_id_with_spaces\\?") }) - + it("escapes a deep link path that starts with a number", function() { const result = escapeDeepLinkPath("123") expect(result).toEqual("\\31 23") }) - + it("escapes a deep link path with a class selector", function() { const result = escapeDeepLinkPath("hello.world") expect(result).toEqual("hello\\.world") }) - + it("escapes a deep link path with an id selector", function() { const result = escapeDeepLinkPath("hello#world") expect(result).toEqual("hello\\#world") }) - + it("escapes a deep link path with a space", function() { const result = escapeDeepLinkPath("hello world") expect(result).toEqual("hello_world") }) - + it("escapes a deep link path with a percent-encoded space", function() { const result = escapeDeepLinkPath("hello%20world") expect(result).toEqual("hello_world") }) }) - + describe("getExtensions", function() { const objTest = Map([[ "x-test", "a"], ["minimum", "b"]]) it("does not error on empty array", function() { @@ -1208,7 +1209,7 @@ describe("utils", function() { expect(result).toEqual(Map([[ "minimum", "b"]])) }) }) - + describe("deeplyStripKey", function() { it("should filter out a specified key", function() { const input = { @@ -1225,7 +1226,7 @@ describe("utils", function() { } }) }) - + it("should filter out a specified key by predicate", function() { const input = { $$ref: "#/this/is/my/ref", @@ -1242,7 +1243,7 @@ describe("utils", function() { } }) }) - + it("should only call the predicate when the key matches", function() { const input = { $$ref: "#/this/is/my/ref", @@ -1252,7 +1253,7 @@ describe("utils", function() { } } let count = 0 - + const result = deeplyStripKey(input, "$$ref", () => { count++ return true @@ -1260,78 +1261,78 @@ describe("utils", function() { expect(count).toEqual(2) }) }) - + describe("parse and serialize search", function() { afterEach(function() { win.location.search = "" }) - + describe("parsing", function() { it("works with empty search", function() { win.location.search = "" expect(parseSearch()).toEqual({}) }) - + it("works with only one key", function() { win.location.search = "?foo" expect(parseSearch()).toEqual({foo: ""}) }) - + it("works with keys and values", function() { win.location.search = "?foo=fooval&bar&baz=bazval" expect(parseSearch()).toEqual({foo: "fooval", bar: "", baz: "bazval"}) }) - + it("decode url encoded components", function() { win.location.search = "?foo=foo%20bar" expect(parseSearch()).toEqual({foo: "foo bar"}) }) }) - + describe("serializing", function() { it("works with empty map", function() { expect(serializeSearch({})).toEqual("") }) - + it("works with multiple keys with and without values", function() { expect(serializeSearch({foo: "", bar: "barval"})).toEqual("foo=&bar=barval") }) - + it("encode url components", function() { expect(serializeSearch({foo: "foo bar"})).toEqual("foo=foo%20bar") }) }) }) - + describe("sanitizeUrl", function() { it("should sanitize a `javascript:` url", function() { const res = sanitizeUrl("javascript:alert('bam!')") - + expect(res).toEqual("about:blank") }) - + it("should sanitize a `data:` url", function() { const res = sanitizeUrl(`data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=`) - + expect(res).toEqual("about:blank") }) - + it("should not modify a `http:` url", function() { const res = sanitizeUrl(`http://swagger.io/`) - + expect(res).toEqual("http://swagger.io/") }) - + it("should not modify a `https:` url", function() { const res = sanitizeUrl(`https://swagger.io/`) - + expect(res).toEqual("https://swagger.io/") }) - + it("should gracefully handle empty strings", function() { expect(sanitizeUrl("")).toEqual("") }) - + it("should gracefully handle non-string values", function() { expect(sanitizeUrl(123)).toEqual("") expect(sanitizeUrl(null)).toEqual("") @@ -1341,6 +1342,24 @@ describe("utils", function() { }) }) + describe("isAbsoluteUrl", function() { + + it("check if url is absolute", function() { + expect(!!isAbsoluteUrl("http://example.com")).toEqual(true) + expect(!!isAbsoluteUrl("https://secure-example.com")).toEqual(true) + expect(!!isAbsoluteUrl("HTTP://uppercase-example.com")).toEqual(true) + expect(!!isAbsoluteUrl("HTTP://uppercase-secure-example.com")).toEqual(true) + expect(!!isAbsoluteUrl("http://trailing-slash.com/")).toEqual(true) + expect(!!isAbsoluteUrl("ftp://file-transfer-protocol.com")).toEqual(true) + expect(!!isAbsoluteUrl("//no-protocol.com")).toEqual(true) + }) + + it("check if url is not absolute", function() { + expect(!!isAbsoluteUrl("/relative-path")).toEqual(false) + expect(!!isAbsoluteUrl("/trailing-slash-relative-path/")).toEqual(false) + }) + }) + describe("requiresValidationURL", function() { it("Should tell us if we require a ValidationURL", function() { const res = requiresValidationURL("https://example.com") @@ -1388,7 +1407,7 @@ describe("utils", function() { describe("getSampleSchema", function() { const oriDate = Date - + before(function() { Date = function () { this.toISOString = function () { @@ -1396,23 +1415,23 @@ describe("utils", function() { } } }) - + after(function() { Date = oriDate }) - + it("should not unnecessarily stringify non-object values", function() { // Given const res = getSampleSchema({ type: "string", format: "date-time" }) - + // Then expect(res).toEqual(new Date().toISOString()) }) }) - + describe("paramToIdentifier", function() { it("should convert an Immutable parameter map to an identifier", () => { const param = fromJS({ @@ -1420,7 +1439,7 @@ describe("utils", function() { in: "query" }) const res = paramToIdentifier(param) - + expect(res).toEqual("query.id.hash-606199662") }) it("should convert an Immutable parameter map to a set of identifiers", () => { @@ -1429,126 +1448,126 @@ describe("utils", function() { in: "query" }) const res = paramToIdentifier(param, { returnAll: true }) - + expect(res).toEqual([ "query.id.hash-606199662", "query.id", "id" ]) }) - + it("should convert an unhashable Immutable parameter map to an identifier", () => { const param = fromJS({ name: "id", in: "query" }) - + param.hashCode = null - + const res = paramToIdentifier(param) - + expect(res).toEqual("query.id") }) - + it("should convert an unhashable Immutable parameter map to a set of identifiers", () => { const param = fromJS({ name: "id", in: "query" }) - + param.hashCode = null - + const res = paramToIdentifier(param, { returnAll: true }) - + expect(res).toEqual([ "query.id", "id" ]) }) - + it("should convert an Immutable parameter map lacking an `in` value to an identifier", () => { const param = fromJS({ name: "id" }) - + const res = paramToIdentifier(param) - + expect(res).toEqual("id") }) - + it("should convert an Immutable parameter map lacking an `in` value to an identifier", () => { const param = fromJS({ name: "id" }) - + const res = paramToIdentifier(param, { returnAll: true }) - + expect(res).toEqual(["id"]) }) - + it("should throw gracefully when given a non-Immutable parameter input", () => { const param = { name: "id" } - + let error = null let res = null - + try { const res = paramToIdentifier(param) } catch(e) { error = e - } - + } + expect(error).toBeA(Error) expect(error.message).toInclude("received a non-Im.Map parameter as input") expect(res).toEqual(null) }) }) - + describe("paramToValue", function() { it("should identify a hash-keyed value", () => { const param = fromJS({ name: "id", in: "query" }) - + const paramValues = { "query.id.hash-606199662": "asdf" } - + const res = paramToValue(param, paramValues) - + expect(res).toEqual("asdf") }) - + it("should identify a in+name value", () => { const param = fromJS({ name: "id", in: "query" }) - + const paramValues = { "query.id": "asdf" } - + const res = paramToValue(param, paramValues) - + expect(res).toEqual("asdf") }) - + it("should identify a name value", () => { const param = fromJS({ name: "id", in: "query" }) - + const paramValues = { "id": "asdf" } - + const res = paramToValue(param, paramValues) - + expect(res).toEqual("asdf") }) }) From 2852fb174328e471cb9f8dd05861a1d5364d4a30 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Mon, 20 Jul 2020 16:54:44 +0100 Subject: [PATCH 08/29] Added unit tests for util `buildUrl()` --- test/mocha/core/utils.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/mocha/core/utils.js b/test/mocha/core/utils.js index 597df1b8ba2..0c51633a14b 100644 --- a/test/mocha/core/utils.js +++ b/test/mocha/core/utils.js @@ -24,6 +24,7 @@ import { getCommonExtensions, sanitizeUrl, isAbsoluteUrl, + buildUrl, requiresValidationURL, extractFileNameFromContentDispositionHeader, deeplyStripKey, @@ -1360,6 +1361,15 @@ describe("utils", function() { }) }) + describe("buildUrl", function() { + it("check if url is absolute", function() { + expect(buildUrl("/relative-path-with-leading-slash", "http://example.com")).toBe("http://example.com/relative-path-with-leading-slash") + expect(buildUrl("relative-path-with-no-leading-slash", "https://example.com")).toBe("https://example.com/relative-path-with-no-leading-slash") + expect(buildUrl("", "https://example.com")).toBe("") + expect(buildUrl("https://absolute-url-example.com/path", "")).toBe("https://absolute-url-example.com/path") + }) + }) + describe("requiresValidationURL", function() { it("Should tell us if we require a ValidationURL", function() { const res = requiresValidationURL("https://example.com") From 74c252e98a9f926e2d9400966615b081b66cb698 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Tue, 21 Jul 2020 11:37:31 +0100 Subject: [PATCH 09/29] Cleaned up utils `buildUrl()` to use URL class for constructing URL --- src/core/utils.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/utils.js b/src/core/utils.js index f768b37dd18..a969e209ba3 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -646,11 +646,9 @@ export function isAbsoluteUrl(url) { export function buildUrl(url="", selectedServer="") { if(!url) return "" - if(isAbsoluteUrl(url) || !selectedServer) return url + if(isAbsoluteUrl(url) || !isAbsoluteUrl(selectedServer)) return url - const selectedServerObj = new URL(selectedServer) - if(url.startsWith("/")) return `${selectedServerObj.protocol}${selectedServerObj.host}${url}` - return selectedServer + url + return new URL(url, selectedServer).href } export function requiresValidationURL(uri) { From d0887a925067ff76932feb5b634c43d9f9631360 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Tue, 21 Jul 2020 11:40:25 +0100 Subject: [PATCH 10/29] Improved unit test for utils `buildUrl()` --- test/mocha/core/utils.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/mocha/core/utils.js b/test/mocha/core/utils.js index 0c51633a14b..30ede85bb21 100644 --- a/test/mocha/core/utils.js +++ b/test/mocha/core/utils.js @@ -1362,11 +1362,16 @@ describe("utils", function() { }) describe("buildUrl", function() { - it("check if url is absolute", function() { - expect(buildUrl("/relative-path-with-leading-slash", "http://example.com")).toBe("http://example.com/relative-path-with-leading-slash") - expect(buildUrl("relative-path-with-no-leading-slash", "https://example.com")).toBe("https://example.com/relative-path-with-no-leading-slash") - expect(buildUrl("", "https://example.com")).toBe("") - expect(buildUrl("https://absolute-url-example.com/path", "")).toBe("https://absolute-url-example.com/path") + it("build url from server and url provided", function() { + + expect(buildUrl("/relative-url-1", "http://example.com/path")).toBe("http://example.com/relative-url-1") + expect(buildUrl("relative-url-2", "http://example.com/path")).toBe("http://example.com/relative-url-2") + + expect(buildUrl("/relative-url-3", "http://example.com/base-path/")).toBe("http://example.com/relative-url-3") + expect(buildUrl("relative-url-4", "http://example.com/base-path/")).toBe("http://example.com/base-path/relative-url-4") + + expect(buildUrl("/relative-url-5", "http://example.com/base-path/path")).toBe("http://example.com/relative-url-5") + expect(buildUrl("relative-url-6", "http://example.com/base-path/path")).toBe("http://example.com/base-path/relative-url-6") }) }) From 2228717d7c5146e623ef4a8905f94a4d69465b5f Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Tue, 21 Jul 2020 11:48:08 +0100 Subject: [PATCH 11/29] Removed unused `fromJS` and cleaned up whitespace --- src/core/components/info.jsx | 2 -- src/core/utils.js | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/components/info.jsx b/src/core/components/info.jsx index 210d9e7ef50..01e5bc66854 100644 --- a/src/core/components/info.jsx +++ b/src/core/components/info.jsx @@ -1,11 +1,9 @@ import React from "react" import PropTypes from "prop-types" -import { fromJS } from "immutable" import ImPropTypes from "react-immutable-proptypes" import { sanitizeUrl, buildUrl } from "core/utils" - export class InfoBasePath extends React.Component { static propTypes = { host: PropTypes.string, diff --git a/src/core/utils.js b/src/core/utils.js index a969e209ba3..d7be8acb279 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -658,6 +658,7 @@ export function requiresValidationURL(uri) { return true } + export function getAcceptControllingResponse(responses) { if(!Im.OrderedMap.isOrderedMap(responses)) { // wrong type! @@ -758,7 +759,7 @@ export function paramToIdentifier(param, { returnAll = false, allowHashes = true if (param && param.hashCode && paramIn && paramName && allowHashes) { generatedIdentifiers.push(`${paramIn}.${paramName}.hash-${param.hashCode()}`) } - + if(paramIn && paramName) { generatedIdentifiers.push(`${paramIn}.${paramName}`) } From a170d64d931fd5908ea8f6a3631bbf4ad2ea00ed Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Wed, 22 Jul 2020 11:26:32 +0100 Subject: [PATCH 12/29] Added support for server relative urls in utils `buildUrl()` --- src/core/utils.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/utils.js b/src/core/utils.js index d7be8acb279..9fce721b02a 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -645,10 +645,9 @@ export function isAbsoluteUrl(url) { } export function buildUrl(url="", selectedServer="") { - if(!url) return "" - if(isAbsoluteUrl(url) || !isAbsoluteUrl(selectedServer)) return url - - return new URL(url, selectedServer).href + const baseUrl = isAbsoluteUrl(selectedServer) ? selectedServer + : new URL(selectedServer, win.location.href).href + return new URL(url, baseUrl).href } export function requiresValidationURL(uri) { @@ -759,7 +758,7 @@ export function paramToIdentifier(param, { returnAll = false, allowHashes = true if (param && param.hashCode && paramIn && paramName && allowHashes) { generatedIdentifiers.push(`${paramIn}.${paramName}.hash-${param.hashCode()}`) } - + if(paramIn && paramName) { generatedIdentifiers.push(`${paramIn}.${paramName}`) } From e526ef43c4e65ee747a71564e4e287cf09348b99 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Wed, 22 Jul 2020 11:28:18 +0100 Subject: [PATCH 13/29] Cleaned up `buildUrl` unit tests and added `location.href` to window.js for testing --- src/core/window.js | 6 ++++-- test/mocha/core/utils.js | 26 +++++++++++++++++++------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/core/window.js b/src/core/window.js index 9a364ee294b..af53ad739e2 100644 --- a/src/core/window.js +++ b/src/core/window.js @@ -1,6 +1,8 @@ function makeWindow() { var win = { - location: {}, + location: { + href: "https://app.swaggerhub.com/apis/smartbear/petstore/1.0.0##/pet/addPet" + }, history: {}, open: () => {}, close: () => {}, @@ -13,7 +15,7 @@ function makeWindow() { try { win = window - var props = ["File", "Blob", "FormData"] + var props = ["File", "Blob", "FormData", "location"] for (var prop of props) { if (prop in window) { win[prop] = window[prop] diff --git a/test/mocha/core/utils.js b/test/mocha/core/utils.js index 30ede85bb21..084eb1bb743 100644 --- a/test/mocha/core/utils.js +++ b/test/mocha/core/utils.js @@ -1362,16 +1362,28 @@ describe("utils", function() { }) describe("buildUrl", function() { - it("build url from server and url provided", function() { + it("build url from server with a path", function() { + expect(buildUrl("relative-url", "http://example.com/path")).toBe("http://example.com/relative-url") + expect(buildUrl("/relative-url", "http://example.com/path")).toBe("http://example.com/relative-url") + }) + + it("build url from server with a base-path", function() { + expect(buildUrl("relative-url", "http://example.com/base-path/")).toBe("http://example.com/base-path/relative-url") + expect(buildUrl("/relative-url", "http://example.com/base-path/")).toBe("http://example.com/relative-url") - expect(buildUrl("/relative-url-1", "http://example.com/path")).toBe("http://example.com/relative-url-1") - expect(buildUrl("relative-url-2", "http://example.com/path")).toBe("http://example.com/relative-url-2") + }) - expect(buildUrl("/relative-url-3", "http://example.com/base-path/")).toBe("http://example.com/relative-url-3") - expect(buildUrl("relative-url-4", "http://example.com/base-path/")).toBe("http://example.com/base-path/relative-url-4") + it("build url from server with level of paths", function() { + expect(buildUrl("relative-url", "http://example.com/base-path/level-1/level-2/level-3")).toBe("http://example.com/base-path/level-1/level-2/relative-url") + expect(buildUrl("/relative-url", "http://example.com/base-path/level-1/level-2/level-3")).toBe("http://example.com/relative-url") + expect(buildUrl("../relative-url", "http://example.com/base-path/level-1/level-2/level-3")).toBe("http://example.com/base-path/level-1/relative-url") + }) - expect(buildUrl("/relative-url-5", "http://example.com/base-path/path")).toBe("http://example.com/relative-url-5") - expect(buildUrl("relative-url-6", "http://example.com/base-path/path")).toBe("http://example.com/base-path/relative-url-6") + it("build url from server with relative url", function() { + expect(buildUrl("relative-url", "server-path/")).toBe("https://app.swaggerhub.com/apis/smartbear/petstore/server-path/relative-url") + expect(buildUrl("/relative-url", "server-path/")).toBe("https://app.swaggerhub.com/relative-url") + expect(buildUrl("relative-url", "/server-path/")).toBe("https://app.swaggerhub.com/server-path/relative-url") + expect(buildUrl("/relative-url", "/server-path/")).toBe("https://app.swaggerhub.com/relative-url") }) }) From 9fd7f397d3d7eac5c731fee465102fa4a0b058f8 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Thu, 23 Jul 2020 09:36:36 +0100 Subject: [PATCH 14/29] Replaced `app.swaggerhub.com` with `example.com` --- src/core/window.js | 2 +- test/mocha/core/utils.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/window.js b/src/core/window.js index af53ad739e2..7e65656d2a0 100644 --- a/src/core/window.js +++ b/src/core/window.js @@ -1,7 +1,7 @@ function makeWindow() { var win = { location: { - href: "https://app.swaggerhub.com/apis/smartbear/petstore/1.0.0##/pet/addPet" + href: "https://example.com/apis/swagger-api/petstore/1.0.0#/pet/addPet" }, history: {}, open: () => {}, diff --git a/test/mocha/core/utils.js b/test/mocha/core/utils.js index 084eb1bb743..75ba53403d2 100644 --- a/test/mocha/core/utils.js +++ b/test/mocha/core/utils.js @@ -1380,10 +1380,10 @@ describe("utils", function() { }) it("build url from server with relative url", function() { - expect(buildUrl("relative-url", "server-path/")).toBe("https://app.swaggerhub.com/apis/smartbear/petstore/server-path/relative-url") - expect(buildUrl("/relative-url", "server-path/")).toBe("https://app.swaggerhub.com/relative-url") - expect(buildUrl("relative-url", "/server-path/")).toBe("https://app.swaggerhub.com/server-path/relative-url") - expect(buildUrl("/relative-url", "/server-path/")).toBe("https://app.swaggerhub.com/relative-url") + expect(buildUrl("relative-url", "server-path/")).toBe("https://example.com/apis/swagger-api/petstore/1.0.0/server-path/relative-url") + expect(buildUrl("/relative-url", "server-path/")).toBe("https://example.com/relative-url") + expect(buildUrl("relative-url", "/server-path/")).toBe("https://example.com/server-path/relative-url") + expect(buildUrl("/relative-url", "/server-path/")).toBe("https://example.com/relative-url") }) }) From e9c91f47bded542ea54ba053a895ce5bf8d1c53e Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Thu, 23 Jul 2020 09:57:36 +0100 Subject: [PATCH 15/29] Fixed whitespacing in mocha test `utils.js` --- test/mocha/core/utils.js | 408 +++++++++++++++++++-------------------- 1 file changed, 204 insertions(+), 204 deletions(-) diff --git a/test/mocha/core/utils.js b/test/mocha/core/utils.js index 75ba53403d2..a36d2fdd897 100644 --- a/test/mocha/core/utils.js +++ b/test/mocha/core/utils.js @@ -37,9 +37,9 @@ import { import win from "core/window" describe("utils", function() { - + describe("mapToList", function(){ - + it("should convert a map to a list, setting `key`", function(){ // With const aMap = fromJS({ @@ -50,17 +50,17 @@ describe("utils", function() { two: 2, } }) - + // When const aList = mapToList(aMap, "someKey") - + // Then expect(aList.toJS()).toEqual([ { someKey: "a", one: 1 }, { someKey: "b", two: 2 }, ]) }) - + it("should flatten an arbitrarily deep map", function(){ // With const aMap = fromJS({ @@ -78,97 +78,97 @@ describe("utils", function() { } } }) - + // When const aList = mapToList(aMap, ["levelA", "levelB"]) - + // Then expect(aList.toJS()).toEqual([ { levelA: "a", levelB: "one", alpha: true }, { levelA: "b", levelB: "two", bravo: true }, { levelA: "b", levelB: "three", charlie: true }, ]) - + }) - + it("should handle an empty map", function(){ // With const aMap = fromJS({}) - + // When const aList = mapToList(aMap, ["levelA", "levelB"]) - + // Then expect(aList.toJS()).toEqual([]) }) - + }) - + describe("extractFileNameFromContentDispositionHeader", function(){ it("should extract quoted filename", function(){ let cdHeader = "attachment; filename=\"file name.jpg\"" let expectedResult = "file name.jpg" expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult) }) - + it("should extract filename", function(){ let cdHeader = "attachment; filename=filename.jpg" let expectedResult = "filename.jpg" expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult) }) - + it("should extract quoted filename in utf-8", function(){ let cdHeader = "attachment; filename*=UTF-8''\"%D1%84%D0%B0%D0%B9%D0%BB.txt\"" let expectedResult = "файл.txt" expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult) }) - + it("should extract filename in utf-8", function(){ let cdHeader = "attachment; filename*=utf-8'ru'%D1%84%D0%B0%D0%B9%D0%BB.txt" let expectedResult = "файл.txt" expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult) }) - + it("should not extract filename and return null", function(){ let cdHeader = "attachment; no file name provided" let expectedResult = null expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult) }) }) - + describe("validateMaximum", function() { let errorMessage = "Value must be less than Maximum" - + it("doesn't return for valid input", function() { expect(validateMaximum(9, 10)).toBeFalsy() expect(validateMaximum(19, 20)).toBeFalsy() }) - + it("returns a message for invalid input", function() { expect(validateMaximum(1, 0)).toEqual(errorMessage) expect(validateMaximum(10, 9)).toEqual(errorMessage) expect(validateMaximum(20, 19)).toEqual(errorMessage) }) }) - + describe("validateMinimum", function() { let errorMessage = "Value must be greater than Minimum" - + it("doesn't return for valid input", function() { expect(validateMinimum(2, 1)).toBeFalsy() expect(validateMinimum(20, 10)).toBeFalsy() }) - + it("returns a message for invalid input", function() { expect(validateMinimum(-1, 0)).toEqual(errorMessage) expect(validateMinimum(1, 2)).toEqual(errorMessage) expect(validateMinimum(10, 20)).toEqual(errorMessage) }) }) - + describe("validateNumber", function() { let errorMessage = "Value must be a number" - + it("doesn't return for whole numbers", function() { expect(validateNumber(0)).toBeFalsy() expect(validateNumber(1)).toBeFalsy() @@ -180,25 +180,25 @@ describe("utils", function() { expect(validateNumber(-20)).toBeFalsy() expect(validateNumber(-5000000)).toBeFalsy() }) - + it("doesn't return for negative numbers", function() { expect(validateNumber(-1)).toBeFalsy() expect(validateNumber(-20)).toBeFalsy() expect(validateNumber(-5000000)).toBeFalsy() }) - + it("doesn't return for decimal numbers", function() { expect(validateNumber(1.1)).toBeFalsy() expect(validateNumber(2.5)).toBeFalsy() expect(validateNumber(-30.99)).toBeFalsy() }) - + it("returns a message for strings", function() { expect(validateNumber("")).toEqual(errorMessage) expect(validateNumber(" ")).toEqual(errorMessage) expect(validateNumber("test")).toEqual(errorMessage) }) - + it("returns a message for invalid input", function() { expect(validateNumber(undefined)).toEqual(errorMessage) expect(validateNumber(null)).toEqual(errorMessage) @@ -208,10 +208,10 @@ describe("utils", function() { expect(validateNumber(false)).toEqual(errorMessage) }) }) - + describe("validateInteger", function() { let errorMessage = "Value must be an integer" - + it("doesn't return for positive integers", function() { expect(validateInteger(0)).toBeFalsy() expect(validateInteger(1)).toBeFalsy() @@ -223,25 +223,25 @@ describe("utils", function() { expect(validateInteger(-20)).toBeFalsy() expect(validateInteger(-5000000)).toBeFalsy() }) - + it("doesn't return for negative integers", function() { expect(validateInteger(-1)).toBeFalsy() expect(validateInteger(-20)).toBeFalsy() expect(validateInteger(-5000000)).toBeFalsy() }) - + it("returns a message for decimal values", function() { expect(validateInteger(1.1)).toEqual(errorMessage) expect(validateInteger(2.5)).toEqual(errorMessage) expect(validateInteger(-30.99)).toEqual(errorMessage) }) - + it("returns a message for strings", function() { expect(validateInteger("")).toEqual(errorMessage) expect(validateInteger(" ")).toEqual(errorMessage) expect(validateInteger("test")).toEqual(errorMessage) }) - + it("returns a message for invalid input", function() { expect(validateInteger(undefined)).toEqual(errorMessage) expect(validateInteger(null)).toEqual(errorMessage) @@ -251,10 +251,10 @@ describe("utils", function() { expect(validateInteger(false)).toEqual(errorMessage) }) }) - + describe("validateFile", function() { let errorMessage = "Value must be a file" - + it("validates against objects which are instances of 'File'", function() { let fileObj = new win.File([], "Test File") expect(validateFile(fileObj)).toBeFalsy() @@ -264,23 +264,23 @@ describe("utils", function() { expect(validateFile("string")).toEqual(errorMessage) }) }) - + describe("validateDateTime", function() { let errorMessage = "Value must be a DateTime" - + it("doesn't return for valid dates", function() { expect(validateDateTime("Mon, 25 Dec 1995 13:30:00 +0430")).toBeFalsy() }) - + it("returns a message for invalid input'", function() { expect(validateDateTime(null)).toEqual(errorMessage) expect(validateDateTime("string")).toEqual(errorMessage) }) }) - + describe("validateGuid", function() { let errorMessage = "Value must be a Guid" - + it("doesn't return for valid guid", function() { expect(validateGuid("8ce4811e-cec5-4a29-891a-15d1917153c1")).toBeFalsy() expect(validateGuid("{8ce4811e-cec5-4a29-891a-15d1917153c1}")).toBeFalsy() @@ -289,56 +289,56 @@ describe("utils", function() { expect(validateGuid("6FFEFD8E-A018-E811-BBF9-60F67727D806")).toBeFalsy() expect(validateGuid("00000000-0000-0000-0000-000000000000")).toBeFalsy() }) - + it("returns a message for invalid input'", function() { expect(validateGuid(1)).toEqual(errorMessage) expect(validateGuid("string")).toEqual(errorMessage) }) }) - + describe("validateMaxLength", function() { let errorMessage = "Value must be less than MaxLength" - + it("doesn't return for valid guid", function() { expect(validateMaxLength("a", 1)).toBeFalsy() expect(validateMaxLength("abc", 5)).toBeFalsy() }) - + it("returns a message for invalid input'", function() { expect(validateMaxLength("abc", 0)).toEqual(errorMessage) expect(validateMaxLength("abc", 1)).toEqual(errorMessage) expect(validateMaxLength("abc", 2)).toEqual(errorMessage) }) }) - + describe("validateMinLength", function() { let errorMessage = "Value must be greater than MinLength" - + it("doesn't return for valid guid", function() { expect(validateMinLength("a", 1)).toBeFalsy() expect(validateMinLength("abc", 2)).toBeFalsy() }) - + it("returns a message for invalid input'", function() { expect(validateMinLength("abc", 5)).toEqual(errorMessage) expect(validateMinLength("abc", 8)).toEqual(errorMessage) }) }) - + describe("validatePattern", function() { let rxPattern = "^(red|blue)" let errorMessage = "Value must follow pattern " + rxPattern - + it("doesn't return for a match", function() { expect(validatePattern("red", rxPattern)).toBeFalsy() expect(validatePattern("blue", rxPattern)).toBeFalsy() }) - + it("returns a message for invalid pattern", function() { expect(validatePattern("pink", rxPattern)).toEqual(errorMessage) expect(validatePattern("123", rxPattern)).toEqual(errorMessage) }) - + it("fails gracefully when an invalid regex value is passed", function() { expect(() => validatePattern("aValue", "---")).toNotThrow() expect(() => validatePattern("aValue", 1234)).toNotThrow() @@ -346,17 +346,17 @@ describe("utils", function() { expect(() => validatePattern("aValue", [])).toNotThrow() }) }) - + describe("validateParam", function() { let param = null let value = null let result = null - + const assertValidateParam = (param, value, expectedError) => { // Swagger 2.0 version result = validateParam( fromJS(param), fromJS(value)) expect( result ).toEqual( expectedError ) - + // OAS3 version, using `schema` sub-object let oas3Param = { required: param.required, @@ -370,7 +370,7 @@ describe("utils", function() { }) expect( result ).toEqual( expectedError ) } - + const assertValidateOas3Param = (param, value, expectedError) => { // for cases where you _only_ want to try OAS3 result = validateParam(fromJS(param), value, { @@ -378,7 +378,7 @@ describe("utils", function() { }) expect( result ).toEqual( expectedError ) } - + it("should check the isOAS3 flag when validating parameters", function() { // This should "skip" validation because there is no `schema` property // and we are telling `validateParam` this is an OAS3 spec @@ -391,7 +391,7 @@ describe("utils", function() { } ) expect( result ).toEqual( [] ) }) - + it("validates required OAS3 objects", function() { // valid object param = { @@ -404,7 +404,7 @@ describe("utils", function() { abc: 123 } assertValidateOas3Param(param, value, []) - + // valid object-as-string param = { required: true, @@ -416,7 +416,7 @@ describe("utils", function() { abc: 123 }) assertValidateOas3Param(param, value, []) - + // invalid object-as-string param = { required: true, @@ -426,7 +426,7 @@ describe("utils", function() { } value = "{{}" assertValidateOas3Param(param, value, ["Parameter string value must be valid JSON"]) - + // missing when required param = { required: true, @@ -437,7 +437,7 @@ describe("utils", function() { value = undefined assertValidateOas3Param(param, value, ["Required field is not provided"]) }) - + it("validates optional OAS3 objects", function() { // valid object param = { @@ -449,7 +449,7 @@ describe("utils", function() { abc: 123 } assertValidateOas3Param(param, value, []) - + // valid object-as-string param = { schema: { @@ -460,7 +460,7 @@ describe("utils", function() { abc: 123 }) assertValidateOas3Param(param, value, []) - + // invalid object-as-string param = { schema: { @@ -469,7 +469,7 @@ describe("utils", function() { } value = "{{}" assertValidateOas3Param(param, value, ["Parameter string value must be valid JSON"]) - + // missing when not required param = { schema: { @@ -479,7 +479,7 @@ describe("utils", function() { value = undefined assertValidateOas3Param(param, value, []) }) - + it("validates required strings", function() { // invalid string param = { @@ -488,7 +488,7 @@ describe("utils", function() { } value = "" assertValidateParam(param, value, ["Required field is not provided"]) - + // valid string param = { required: true, @@ -496,7 +496,7 @@ describe("utils", function() { } value = "test string" assertValidateParam(param, value, []) - + // valid string with min and max length param = { required: true, @@ -507,7 +507,7 @@ describe("utils", function() { value = "test string" assertValidateParam(param, value, []) }) - + it("handles OAS3 `Parameter.content`", function() { // invalid string param = { @@ -522,7 +522,7 @@ describe("utils", function() { } value = "" assertValidateOas3Param(param, value, ["Required field is not provided"]) - + // valid string param = { content: { @@ -537,7 +537,7 @@ describe("utils", function() { value = "test string" assertValidateOas3Param(param, value, []) - + // invalid (empty) JSON string param = { content: { @@ -566,7 +566,7 @@ describe("utils", function() { value = "{{}" assertValidateOas3Param(param, value, ["Parameter string value must be valid JSON"]) - + // valid (empty object) JSON string param = { content: { @@ -580,7 +580,7 @@ describe("utils", function() { } value = "{}" assertValidateOas3Param(param, value, []) - + // valid (empty object) JSON object param = { content: { @@ -594,7 +594,7 @@ describe("utils", function() { } value = {} assertValidateOas3Param(param, value, []) - + // should skip JSON validation for non-JSON media types param = { content: { @@ -609,7 +609,7 @@ describe("utils", function() { value = "{{}" assertValidateOas3Param(param, value, []) }) - + it("validates required strings with min and max length", function() { // invalid string with max length param = { @@ -619,7 +619,7 @@ describe("utils", function() { } value = "test string" assertValidateParam(param, value, ["Value must be less than MaxLength"]) - + // invalid string with max length 0 param = { required: true, @@ -628,7 +628,7 @@ describe("utils", function() { } value = "test string" assertValidateParam(param, value, ["Value must be less than MaxLength"]) - + // invalid string with min length param = { required: true, @@ -638,7 +638,7 @@ describe("utils", function() { value = "test string" assertValidateParam(param, value, ["Value must be greater than MinLength"]) }) - + it("validates optional strings", function() { // valid (empty) string param = { @@ -647,7 +647,7 @@ describe("utils", function() { } value = "" assertValidateParam(param, value, []) - + // valid string param = { required: false, @@ -656,7 +656,7 @@ describe("utils", function() { value = "test" assertValidateParam(param, value, []) }) - + it("validates required files", function() { // invalid file param = { @@ -665,7 +665,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, ["Required field is not provided"]) - + // valid file param = { required: true, @@ -674,7 +674,7 @@ describe("utils", function() { value = new win.File() assertValidateParam(param, value, []) }) - + it("validates optional files", function() { // invalid file param = { @@ -683,7 +683,7 @@ describe("utils", function() { } value = "not a file" assertValidateParam(param, value, ["Value must be a file"]) - + // valid (empty) file param = { required: false, @@ -691,7 +691,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, []) - + // valid file param = { required: false, @@ -700,7 +700,7 @@ describe("utils", function() { value = new win.File() assertValidateParam(param, value, []) }) - + it("validates required arrays", function() { // invalid (empty) array param = { @@ -717,7 +717,7 @@ describe("utils", function() { } value = "" assertValidateParam(param, value, ["Required field is not provided"]) - + // invalid (not an array) param = { required: true, @@ -725,7 +725,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, ["Required field is not provided"]) - + // invalid array, items do not match correct type param = { required: true, @@ -736,7 +736,7 @@ describe("utils", function() { } value = [1] assertValidateParam(param, value, [{index: 0, error: "Value must be a string"}]) - + // valid array, with no 'type' for items param = { required: true, @@ -752,7 +752,7 @@ describe("utils", function() { } value = "[1]" assertValidateParam(param, value, []) - + // valid array, items match type param = { required: true, @@ -764,7 +764,7 @@ describe("utils", function() { value = ["1"] assertValidateParam(param, value, []) }) - + it("validates optional arrays", function() { // valid, empty array param = { @@ -773,7 +773,7 @@ describe("utils", function() { } value = [] assertValidateParam(param, value, []) - + // invalid, items do not match correct type param = { required: false, @@ -784,7 +784,7 @@ describe("utils", function() { } value = ["number"] assertValidateParam(param, value, [{index: 0, error: "Value must be a number"}]) - + // valid param = { required: false, @@ -796,7 +796,7 @@ describe("utils", function() { value = ["test"] assertValidateParam(param, value, []) }) - + it("validates required booleans", function() { // invalid boolean value param = { @@ -805,7 +805,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, ["Required field is not provided"]) - + // invalid boolean value (not a boolean) param = { required: true, @@ -813,7 +813,7 @@ describe("utils", function() { } value = "test string" assertValidateParam(param, value, ["Value must be a boolean"]) - + // valid boolean value param = { required: true, @@ -821,7 +821,7 @@ describe("utils", function() { } value = "true" assertValidateParam(param, value, []) - + // valid boolean value param = { required: true, @@ -830,7 +830,7 @@ describe("utils", function() { value = false assertValidateParam(param, value, []) }) - + it("validates optional booleans", function() { // valid (empty) boolean value param = { @@ -839,7 +839,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, []) - + // invalid boolean value (not a boolean) param = { required: false, @@ -847,7 +847,7 @@ describe("utils", function() { } value = "test string" assertValidateParam(param, value, ["Value must be a boolean"]) - + // valid boolean value param = { required: false, @@ -855,7 +855,7 @@ describe("utils", function() { } value = "true" assertValidateParam(param, value, []) - + // valid boolean value param = { required: false, @@ -864,7 +864,7 @@ describe("utils", function() { value = false assertValidateParam(param, value, []) }) - + it("validates required numbers", function() { // invalid number, string instead of a number param = { @@ -873,7 +873,7 @@ describe("utils", function() { } value = "test" assertValidateParam(param, value, ["Value must be a number"]) - + // invalid number, undefined value param = { required: true, @@ -881,7 +881,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, ["Required field is not provided"]) - + // valid number with min and max param = { required: true, @@ -891,7 +891,7 @@ describe("utils", function() { } value = 10 assertValidateParam(param, value, []) - + // valid negative number with min and max param = { required: true, @@ -901,17 +901,17 @@ describe("utils", function() { } value = -10 assertValidateParam(param, value, []) - + // invalid number with maximum:0 param = { required: true, type: "number", - + maximum: 0 } value = 1 assertValidateParam(param, value, ["Value must be less than Maximum"]) - + // invalid number with minimum:0 param = { required: true, @@ -921,7 +921,7 @@ describe("utils", function() { value = -10 assertValidateParam(param, value, ["Value must be greater than Minimum"]) }) - + it("validates optional numbers", function() { // invalid number, string instead of a number param = { @@ -930,7 +930,7 @@ describe("utils", function() { } value = "test" assertValidateParam(param, value, ["Value must be a number"]) - + // valid (empty) number param = { required: false, @@ -938,7 +938,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, []) - + // valid number param = { required: false, @@ -947,7 +947,7 @@ describe("utils", function() { value = 10 assertValidateParam(param, value, []) }) - + it("validates required integers", function() { // invalid integer, string instead of an integer param = { @@ -956,7 +956,7 @@ describe("utils", function() { } value = "test" assertValidateParam(param, value, ["Value must be an integer"]) - + // invalid integer, undefined value param = { required: true, @@ -964,7 +964,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, ["Required field is not provided"]) - + // valid integer, but 0 is falsy in JS param = { required: true, @@ -972,7 +972,7 @@ describe("utils", function() { } value = 0 assertValidateParam(param, value, []) - + // valid integer param = { required: true, @@ -981,7 +981,7 @@ describe("utils", function() { value = 10 assertValidateParam(param, value, []) }) - + it("validates optional integers", function() { // invalid integer, string instead of an integer param = { @@ -990,7 +990,7 @@ describe("utils", function() { } value = "test" assertValidateParam(param, value, ["Value must be an integer"]) - + // valid (empty) integer param = { required: false, @@ -998,7 +998,7 @@ describe("utils", function() { } value = undefined assertValidateParam(param, value, []) - + // integers param = { required: false, @@ -1008,34 +1008,34 @@ describe("utils", function() { assertValidateParam(param, value, []) }) }) - + describe("fromJSOrdered", () => { it("should create an OrderedMap from an object", () => { const param = { value: "test" } - + const result = fromJSOrdered(param).toJS() expect( result ).toEqual( { value: "test" } ) }) - + it("should not use an object's length property for Map size", () => { const param = { length: 5 } - + const result = fromJSOrdered(param).toJS() expect( result ).toEqual( { length: 5 } ) }) - + it("should create an OrderedMap from an array", () => { const param = [1, 1, 2, 3, 5, 8] - + const result = fromJSOrdered(param).toJS() expect( result ).toEqual( [1, 1, 2, 3, 5, 8] ) }) }) - + describe("getAcceptControllingResponse", () => { it("should return the first 2xx response with a media type", () => { const responses = fromJSOrdered({ @@ -1058,7 +1058,7 @@ describe("utils", function() { } } }) - + expect(getAcceptControllingResponse(responses)).toEqual(responses.get("200")) }) it("should skip 2xx responses without defined media types", () => { @@ -1082,7 +1082,7 @@ describe("utils", function() { } } }) - + expect(getAcceptControllingResponse(responses)).toEqual(responses.get("201")) }) it("should default to the `default` response if it has defined media types", () => { @@ -1103,7 +1103,7 @@ describe("utils", function() { } } }) - + expect(getAcceptControllingResponse(responses)).toEqual(responses.get("default")) }) it("should return null if there are no suitable controlling responses", () => { @@ -1118,40 +1118,40 @@ describe("utils", function() { description: "also empty.." } }) - + expect(getAcceptControllingResponse(responses)).toBe(null) }) it("should return null if an empty OrderedMap is passed", () => { const responses = fromJSOrdered() - + expect(getAcceptControllingResponse(responses)).toBe(null) }) it("should return null if anything except an OrderedMap is passed", () => { const responses = {} - + expect(getAcceptControllingResponse(responses)).toBe(null) }) }) - + describe("createDeepLinkPath", function() { it("creates a deep link path replacing spaces with underscores", function() { const result = createDeepLinkPath("tag id with spaces") expect(result).toEqual("tag%20id%20with%20spaces") }) - + it("trims input when creating a deep link path", function() { let result = createDeepLinkPath(" spaces before and after ") expect(result).toEqual("spaces%20before%20and%20after") - + result = createDeepLinkPath(" ") expect(result).toEqual("") }) - + it("creates a deep link path with special characters", function() { const result = createDeepLinkPath("!@#$%^&*(){}[]") expect(result).toEqual("!@#$%^&*(){}[]") }) - + it("returns an empty string for invalid input", function() { expect( createDeepLinkPath(null) ).toEqual("") expect( createDeepLinkPath(undefined) ).toEqual("") @@ -1160,39 +1160,39 @@ describe("utils", function() { expect( createDeepLinkPath({}) ).toEqual("") }) }) - + describe("escapeDeepLinkPath", function() { it("creates and escapes a deep link path", function() { const result = escapeDeepLinkPath("tag id with spaces?") expect(result).toEqual("tag_id_with_spaces\\?") }) - + it("escapes a deep link path that starts with a number", function() { const result = escapeDeepLinkPath("123") expect(result).toEqual("\\31 23") }) - + it("escapes a deep link path with a class selector", function() { const result = escapeDeepLinkPath("hello.world") expect(result).toEqual("hello\\.world") }) - + it("escapes a deep link path with an id selector", function() { const result = escapeDeepLinkPath("hello#world") expect(result).toEqual("hello\\#world") }) - + it("escapes a deep link path with a space", function() { const result = escapeDeepLinkPath("hello world") expect(result).toEqual("hello_world") }) - + it("escapes a deep link path with a percent-encoded space", function() { const result = escapeDeepLinkPath("hello%20world") expect(result).toEqual("hello_world") }) }) - + describe("getExtensions", function() { const objTest = Map([[ "x-test", "a"], ["minimum", "b"]]) it("does not error on empty array", function() { @@ -1210,7 +1210,7 @@ describe("utils", function() { expect(result).toEqual(Map([[ "minimum", "b"]])) }) }) - + describe("deeplyStripKey", function() { it("should filter out a specified key", function() { const input = { @@ -1227,7 +1227,7 @@ describe("utils", function() { } }) }) - + it("should filter out a specified key by predicate", function() { const input = { $$ref: "#/this/is/my/ref", @@ -1244,7 +1244,7 @@ describe("utils", function() { } }) }) - + it("should only call the predicate when the key matches", function() { const input = { $$ref: "#/this/is/my/ref", @@ -1254,7 +1254,7 @@ describe("utils", function() { } } let count = 0 - + const result = deeplyStripKey(input, "$$ref", () => { count++ return true @@ -1262,78 +1262,78 @@ describe("utils", function() { expect(count).toEqual(2) }) }) - + describe("parse and serialize search", function() { afterEach(function() { win.location.search = "" }) - + describe("parsing", function() { it("works with empty search", function() { win.location.search = "" expect(parseSearch()).toEqual({}) }) - + it("works with only one key", function() { win.location.search = "?foo" expect(parseSearch()).toEqual({foo: ""}) }) - + it("works with keys and values", function() { win.location.search = "?foo=fooval&bar&baz=bazval" expect(parseSearch()).toEqual({foo: "fooval", bar: "", baz: "bazval"}) }) - + it("decode url encoded components", function() { win.location.search = "?foo=foo%20bar" expect(parseSearch()).toEqual({foo: "foo bar"}) }) }) - + describe("serializing", function() { it("works with empty map", function() { expect(serializeSearch({})).toEqual("") }) - + it("works with multiple keys with and without values", function() { expect(serializeSearch({foo: "", bar: "barval"})).toEqual("foo=&bar=barval") }) - + it("encode url components", function() { expect(serializeSearch({foo: "foo bar"})).toEqual("foo=foo%20bar") }) }) }) - + describe("sanitizeUrl", function() { it("should sanitize a `javascript:` url", function() { const res = sanitizeUrl("javascript:alert('bam!')") - + expect(res).toEqual("about:blank") }) - + it("should sanitize a `data:` url", function() { const res = sanitizeUrl(`data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=`) - + expect(res).toEqual("about:blank") }) - + it("should not modify a `http:` url", function() { const res = sanitizeUrl(`http://swagger.io/`) - + expect(res).toEqual("http://swagger.io/") }) - + it("should not modify a `https:` url", function() { const res = sanitizeUrl(`https://swagger.io/`) - + expect(res).toEqual("https://swagger.io/") }) - + it("should gracefully handle empty strings", function() { expect(sanitizeUrl("")).toEqual("") }) - + it("should gracefully handle non-string values", function() { expect(sanitizeUrl(123)).toEqual("") expect(sanitizeUrl(null)).toEqual("") @@ -1386,7 +1386,7 @@ describe("utils", function() { expect(buildUrl("/relative-url", "/server-path/")).toBe("https://example.com/relative-url") }) }) - + describe("requiresValidationURL", function() { it("Should tell us if we require a ValidationURL", function() { const res = requiresValidationURL("https://example.com") @@ -1434,7 +1434,7 @@ describe("utils", function() { describe("getSampleSchema", function() { const oriDate = Date - + before(function() { Date = function () { this.toISOString = function () { @@ -1442,23 +1442,23 @@ describe("utils", function() { } } }) - + after(function() { Date = oriDate }) - + it("should not unnecessarily stringify non-object values", function() { // Given const res = getSampleSchema({ type: "string", format: "date-time" }) - + // Then expect(res).toEqual(new Date().toISOString()) }) }) - + describe("paramToIdentifier", function() { it("should convert an Immutable parameter map to an identifier", () => { const param = fromJS({ @@ -1466,7 +1466,7 @@ describe("utils", function() { in: "query" }) const res = paramToIdentifier(param) - + expect(res).toEqual("query.id.hash-606199662") }) it("should convert an Immutable parameter map to a set of identifiers", () => { @@ -1475,126 +1475,126 @@ describe("utils", function() { in: "query" }) const res = paramToIdentifier(param, { returnAll: true }) - + expect(res).toEqual([ "query.id.hash-606199662", "query.id", "id" ]) }) - + it("should convert an unhashable Immutable parameter map to an identifier", () => { const param = fromJS({ name: "id", in: "query" }) - + param.hashCode = null - + const res = paramToIdentifier(param) - + expect(res).toEqual("query.id") }) - + it("should convert an unhashable Immutable parameter map to a set of identifiers", () => { const param = fromJS({ name: "id", in: "query" }) - + param.hashCode = null - + const res = paramToIdentifier(param, { returnAll: true }) - + expect(res).toEqual([ "query.id", "id" ]) }) - + it("should convert an Immutable parameter map lacking an `in` value to an identifier", () => { const param = fromJS({ name: "id" }) - + const res = paramToIdentifier(param) - + expect(res).toEqual("id") }) - + it("should convert an Immutable parameter map lacking an `in` value to an identifier", () => { const param = fromJS({ name: "id" }) - + const res = paramToIdentifier(param, { returnAll: true }) - + expect(res).toEqual(["id"]) }) - + it("should throw gracefully when given a non-Immutable parameter input", () => { const param = { name: "id" } - + let error = null let res = null - + try { const res = paramToIdentifier(param) } catch(e) { error = e - } - + } + expect(error).toBeA(Error) expect(error.message).toInclude("received a non-Im.Map parameter as input") expect(res).toEqual(null) }) }) - + describe("paramToValue", function() { it("should identify a hash-keyed value", () => { const param = fromJS({ name: "id", in: "query" }) - + const paramValues = { "query.id.hash-606199662": "asdf" } - + const res = paramToValue(param, paramValues) - + expect(res).toEqual("asdf") }) - + it("should identify a in+name value", () => { const param = fromJS({ name: "id", in: "query" }) - + const paramValues = { "query.id": "asdf" } - + const res = paramToValue(param, paramValues) - + expect(res).toEqual("asdf") }) - + it("should identify a name value", () => { const param = fromJS({ name: "id", in: "query" }) - + const paramValues = { "id": "asdf" } - + const res = paramToValue(param, paramValues) - + expect(res).toEqual("asdf") }) }) From 2d427e0a8028f113bd8dc65f112c981efa1dcd12 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Thu, 23 Jul 2020 11:31:57 +0100 Subject: [PATCH 16/29] Added guard to `externalDocs` in --- src/core/components/info.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/components/info.jsx b/src/core/components/info.jsx index 01e5bc66854..f6f4b9c42d0 100644 --- a/src/core/components/info.jsx +++ b/src/core/components/info.jsx @@ -111,9 +111,9 @@ export default class Info extends React.Component { let termsOfServiceUrl = buildUrl(info.get("termsOfService"), selectedServer) let contact = info.get("contact") let license = info.get("license") - let rawExternalDocsUrl = externalDocs.get("url") + let rawExternalDocsUrl = externalDocs && externalDocs.get("url") let externalDocsUrl = buildUrl( rawExternalDocsUrl, selectedServer ) - let externalDocsDescription = externalDocs.get("description") + let externalDocsDescription = externalDocs && externalDocs.get("description") const Markdown = getComponent("Markdown", true) const Link = getComponent("Link") @@ -136,14 +136,14 @@ export default class Info extends React.Component { { - termsOfService &&
+ termsOfServiceUrl &&
Terms of service
} {contact && contact.size ? : null } {license && license.size ? : null } - { externalDocsUrl ? + { externalDocs ? {externalDocsDescription || externalDocsUrl} : null } From c7390ed6423280423b844ed2a473b9b766399580 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Thu, 23 Jul 2020 11:32:58 +0100 Subject: [PATCH 17/29] Commented out `location.href` in window.js; it was breaking another test --- src/core/window.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/window.js b/src/core/window.js index 7e65656d2a0..550927c5774 100644 --- a/src/core/window.js +++ b/src/core/window.js @@ -1,7 +1,7 @@ function makeWindow() { var win = { location: { - href: "https://example.com/apis/swagger-api/petstore/1.0.0#/pet/addPet" + // href: "https://example.com/apis/swagger-api/petstore/1.0.0#/pet/addPet" }, history: {}, open: () => {}, From b47d39777bfd5330eaa0e03853ec12c5e6f91a28 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Thu, 23 Jul 2020 11:33:34 +0100 Subject: [PATCH 18/29] Added extra check to utils `buildUrl()` --- src/core/utils.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/utils.js b/src/core/utils.js index 9fce721b02a..d51341b3291 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -645,8 +645,12 @@ export function isAbsoluteUrl(url) { } export function buildUrl(url="", selectedServer="") { + if(!url) return + if(isAbsoluteUrl(url)) return url + const baseUrl = isAbsoluteUrl(selectedServer) ? selectedServer : new URL(selectedServer, win.location.href).href + return new URL(url, baseUrl).href } From a1edf30f5552062cbdce8743e00e22c71a7e458e Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Thu, 23 Jul 2020 11:34:33 +0100 Subject: [PATCH 19/29] Added `selectedServer` to test mock props --- test/mocha/components/info-wrapper.jsx | 5 ++++- test/mocha/xss/info-sanitization.jsx | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test/mocha/components/info-wrapper.jsx b/test/mocha/components/info-wrapper.jsx index fcb98d6f4f8..ba8be69634e 100644 --- a/test/mocha/components/info-wrapper.jsx +++ b/test/mocha/components/info-wrapper.jsx @@ -17,7 +17,10 @@ describe("", function () { url () {}, basePath () {}, host () {}, - externalDocs () {} + externalDocs () {}, + }, + oas3Selectors: { + selectedServer () {}, }, getComponent: c => components[c] } diff --git a/test/mocha/xss/info-sanitization.jsx b/test/mocha/xss/info-sanitization.jsx index e868fe9ffa6..f73b770af3e 100644 --- a/test/mocha/xss/info-sanitization.jsx +++ b/test/mocha/xss/info-sanitization.jsx @@ -18,7 +18,8 @@ describe(" Sanitization", function(){ description: "Description *with* " }), host: "example.test", - basePath: "/api" + basePath: "/api", + selectedServer: "https://example.test", } it("renders sanitized .title content", function(){ From 8ac390bb19cb888f98e2aef9ed8da79944f957cb Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Thu, 23 Jul 2020 11:35:14 +0100 Subject: [PATCH 20/29] Skipped test "build url from server with relative url" --- test/mocha/core/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mocha/core/utils.js b/test/mocha/core/utils.js index a36d2fdd897..3fc01324041 100644 --- a/test/mocha/core/utils.js +++ b/test/mocha/core/utils.js @@ -1379,7 +1379,7 @@ describe("utils", function() { expect(buildUrl("../relative-url", "http://example.com/base-path/level-1/level-2/level-3")).toBe("http://example.com/base-path/level-1/relative-url") }) - it("build url from server with relative url", function() { + it.skip("build url from server with relative url", function() { expect(buildUrl("relative-url", "server-path/")).toBe("https://example.com/apis/swagger-api/petstore/1.0.0/server-path/relative-url") expect(buildUrl("/relative-url", "server-path/")).toBe("https://example.com/relative-url") expect(buildUrl("relative-url", "/server-path/")).toBe("https://example.com/server-path/relative-url") From 4e69e750db21327750e2982d6418c5057da41da1 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Fri, 24 Jul 2020 12:16:03 +0100 Subject: [PATCH 21/29] Removed "location" from window.js `props` --- src/core/window.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/window.js b/src/core/window.js index 550927c5774..9a364ee294b 100644 --- a/src/core/window.js +++ b/src/core/window.js @@ -1,8 +1,6 @@ function makeWindow() { var win = { - location: { - // href: "https://example.com/apis/swagger-api/petstore/1.0.0#/pet/addPet" - }, + location: {}, history: {}, open: () => {}, close: () => {}, @@ -15,7 +13,7 @@ function makeWindow() { try { win = window - var props = ["File", "Blob", "FormData", "location"] + var props = ["File", "Blob", "FormData"] for (var prop of props) { if (prop in window) { win[prop] = window[prop] From 40493da605d54e82afe0f96c3a493153d01738b9 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Fri, 24 Jul 2020 16:50:15 +0100 Subject: [PATCH 22/29] Added specUrl to buildUrl to support relative server url or no serverSelected --- src/core/components/info.jsx | 22 ++++++++++++---------- src/core/components/operation-tag.jsx | 7 +++++-- src/core/components/operation.jsx | 2 +- src/core/components/operations.jsx | 3 ++- src/core/utils.js | 16 +++++++++++----- 5 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/core/components/info.jsx b/src/core/components/info.jsx index f6f4b9c42d0..3618746b6b2 100644 --- a/src/core/components/info.jsx +++ b/src/core/components/info.jsx @@ -26,13 +26,14 @@ class Contact extends React.Component { static propTypes = { data: PropTypes.object, getComponent: PropTypes.func.isRequired, + specSelectors: PropTypes.object.isRequired, selectedServer: PropTypes.string, } render(){ - let { data, getComponent, selectedServer } = this.props + let { data, getComponent, selectedServer, url: specUrl} = this.props let name = data.get("name") || "the developer" - let url = buildUrl(data.get("url"), selectedServer) + let url = buildUrl(data.get("url"), specUrl, {selectedServer}) let email = data.get("email") const Link = getComponent("Link") @@ -54,15 +55,16 @@ class License extends React.Component { static propTypes = { license: PropTypes.object, getComponent: PropTypes.func.isRequired, + specSelectors: PropTypes.object.isRequired, selectedServer: PropTypes.string, } render(){ - let { license, getComponent, selectedServer } = this.props + let { license, getComponent, selectedServer, url: specUrl } = this.props const Link = getComponent("Link") - let name = license.get("name") || "License" - let url = buildUrl(license.get("url"), selectedServer) + let name = license.get("name") || "License" + let url = buildUrl(license.get("url"), specUrl, {selectedServer}) return (
@@ -104,15 +106,15 @@ export default class Info extends React.Component { } render() { - let { info, url, host, basePath, getComponent, externalDocs, selectedServer } = this.props + let { info, url, host, basePath, getComponent, externalDocs, selectedServer, url: specUrl } = this.props let version = info.get("version") let description = info.get("description") let title = info.get("title") - let termsOfServiceUrl = buildUrl(info.get("termsOfService"), selectedServer) + let termsOfServiceUrl = buildUrl(info.get("termsOfService"), specUrl, {selectedServer}) let contact = info.get("contact") let license = info.get("license") let rawExternalDocsUrl = externalDocs && externalDocs.get("url") - let externalDocsUrl = buildUrl( rawExternalDocsUrl, selectedServer ) + let externalDocsUrl = buildUrl(rawExternalDocsUrl, specUrl, {selectedServer}) let externalDocsDescription = externalDocs && externalDocs.get("description") const Markdown = getComponent("Markdown", true) @@ -141,8 +143,8 @@ export default class Info extends React.Component {
} - {contact && contact.size ? : null } - {license && license.size ? : null } + {contact && contact.size ? : null } + {license && license.size ? : null } { externalDocs ? {externalDocsDescription || externalDocsUrl} : null } diff --git a/src/core/components/operation-tag.jsx b/src/core/components/operation-tag.jsx index 970ba009cea..e0354240c1b 100644 --- a/src/core/components/operation-tag.jsx +++ b/src/core/components/operation-tag.jsx @@ -22,6 +22,8 @@ export default class OperationTag extends React.Component { getConfigs: PropTypes.func.isRequired, getComponent: PropTypes.func.isRequired, + specUrl: PropTypes.string.isRequired, + children: PropTypes.element, } @@ -35,6 +37,7 @@ export default class OperationTag extends React.Component { layoutActions, getConfigs, getComponent, + specUrl, } = this.props let { @@ -51,8 +54,8 @@ export default class OperationTag extends React.Component { let tagDescription = tagObj.getIn(["tagDetails", "description"], null) let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"]) - let rawTagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"]) - let tagExternalDocsUrl = buildUrl( rawTagExternalDocsUrl, oas3Selectors.selectedServer() ) + let rawTagExternalDocsUrl = tagObj.getIn(["tagDetails", "externalDocs", "url"]) + let tagExternalDocsUrl = buildUrl( rawTagExternalDocsUrl, specUrl, {selectedServer: oas3Selectors.selectedServer()} ) let isShownKey = ["operations-tag", tag] let showTag = layoutSelectors.isShown(isShownKey, docExpansion === "full" || docExpansion === "list") diff --git a/src/core/components/operation.jsx b/src/core/components/operation.jsx index 853cad92c6c..4216be72b12 100644 --- a/src/core/components/operation.jsx +++ b/src/core/components/operation.jsx @@ -81,7 +81,7 @@ export default class Operation extends PureComponent { schemes } = op - const externalDocsUrl = externalDocs ? buildUrl(externalDocs.url, oas3Selectors.selectedServer()) : "" + const externalDocsUrl = externalDocs ? buildUrl(externalDocs.url, specSelectors.url(), { selectedServer: oas3Selectors.selectedServer() }) : "" let operation = operationProps.getIn(["op"]) let responses = operation.get("responses") let parameters = getList(operation, ["parameters"]) diff --git a/src/core/components/operations.jsx b/src/core/components/operations.jsx index 8987f9fa283..505217ffbda 100644 --- a/src/core/components/operations.jsx +++ b/src/core/components/operations.jsx @@ -71,7 +71,8 @@ export default class Operations extends React.Component { layoutSelectors={layoutSelectors} layoutActions={layoutActions} getConfigs={getConfigs} - getComponent={getComponent}> + getComponent={getComponent} + specUrl={specSelectors.url()}> { operations.map( op => { const path = op.get("path") diff --git a/src/core/utils.js b/src/core/utils.js index d51341b3291..12607cd4746 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -644,13 +644,19 @@ export function isAbsoluteUrl(url) { return url.match(/^(?:[a-z]+:)?\/\//i) // Matches http://, HTTP://, https://, ftp://, //example.com, } -export function buildUrl(url="", selectedServer="") { - if(!url) return - if(isAbsoluteUrl(url)) return url +export function buildBaseUrl(selectedServer, specUrl) { + if(!selectedServer) return specUrl + if(isAbsoluteUrl(selectedServer)) return selectedServer + + return new URL(selectedServer, specUrl).href +} - const baseUrl = isAbsoluteUrl(selectedServer) ? selectedServer - : new URL(selectedServer, win.location.href).href +export function buildUrl(url, specUrl, { selectedServer="" } = {}) { + console.log(url, specUrl, selectedServer) + if(!url) return + if(isAbsoluteUrl(url)) return url + const baseUrl = buildBaseUrl(selectedServer, specUrl) return new URL(url, baseUrl).href } From 80b8438d28c2289fd3be13bd1ab326c5da74232e Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Fri, 24 Jul 2020 18:39:34 +0100 Subject: [PATCH 23/29] Added unit tests for `buildBaseUrl()` and improved unit tests `isAbsoluteUrl()` and `buildUrl()` --- test/mocha/core/utils.js | 77 +++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/test/mocha/core/utils.js b/test/mocha/core/utils.js index 3fc01324041..0a612988259 100644 --- a/test/mocha/core/utils.js +++ b/test/mocha/core/utils.js @@ -24,6 +24,7 @@ import { getCommonExtensions, sanitizeUrl, isAbsoluteUrl, + buildBaseUrl, buildUrl, requiresValidationURL, extractFileNameFromContentDispositionHeader, @@ -1356,34 +1357,76 @@ describe("utils", function() { }) it("check if url is not absolute", function() { - expect(!!isAbsoluteUrl("/relative-path")).toEqual(false) - expect(!!isAbsoluteUrl("/trailing-slash-relative-path/")).toEqual(false) + expect(!!isAbsoluteUrl("/url-relative-to-host/base-path/path")).toEqual(false) + expect(!!isAbsoluteUrl("url-relative-to-base/base-path/path")).toEqual(false) + }) + }) + + describe("buildBaseUrl", function() { + const specUrl = "https://petstore.swagger.io/v2/swagger.json" + + const noServerSelected = "" + const absoluteServerUrl = "https://server-example.com/base-path/path" + const serverUrlRelativeToBase = "server-example/base-path/path" + const serverUrlRelativeToHost = "/server-example/base-path/path" + + it("build base url with no server selected", function() { + expect(buildBaseUrl(noServerSelected, specUrl)).toBe("https://petstore.swagger.io/v2/swagger.json") + }) + + it("build base url from absolute server url", function() { + expect(buildBaseUrl(absoluteServerUrl, specUrl)).toBe("https://server-example.com/base-path/path") + }) + + it("build base url from relative server url", function() { + expect(buildBaseUrl(serverUrlRelativeToBase, specUrl)).toBe("https://petstore.swagger.io/v2/server-example/base-path/path") + expect(buildBaseUrl(serverUrlRelativeToHost, specUrl)).toBe("https://petstore.swagger.io/server-example/base-path/path") }) }) describe("buildUrl", function() { - it("build url from server with a path", function() { - expect(buildUrl("relative-url", "http://example.com/path")).toBe("http://example.com/relative-url") - expect(buildUrl("/relative-url", "http://example.com/path")).toBe("http://example.com/relative-url") + const specUrl = "https://petstore.swagger.io/v2/swagger.json" + + const noUrl = "" + const absoluteUrl = "https://example.com/base-path/path" + const urlRelativeToBase = "relative-url/base-path/path" + const urlRelativeToHost = "/relative-url/base-path/path" + + const noServerSelected = "" + const absoluteServerUrl = "https://server-example.com/base-path/path" + const serverUrlRelativeToBase = "server-example/base-path/path" + const serverUrlRelativeToHost = "/server-example/base-path/path" + + it("build no url", function() { + expect(buildUrl(noUrl, specUrl, { selectedServer: absoluteServerUrl })).toBe(undefined) + expect(buildUrl(noUrl, specUrl, { selectedServer: serverUrlRelativeToBase })).toBe(undefined) + expect(buildUrl(noUrl, specUrl, { selectedServer: serverUrlRelativeToHost })).toBe(undefined) }) - it("build url from server with a base-path", function() { - expect(buildUrl("relative-url", "http://example.com/base-path/")).toBe("http://example.com/base-path/relative-url") - expect(buildUrl("/relative-url", "http://example.com/base-path/")).toBe("http://example.com/relative-url") + it("build absolute url", function() { + expect(buildUrl(absoluteUrl, specUrl, { selectedServer: absoluteServerUrl })).toBe("https://example.com/base-path/path") + expect(buildUrl(absoluteUrl, specUrl, { selectedServer: serverUrlRelativeToBase })).toBe("https://example.com/base-path/path") + expect(buildUrl(absoluteUrl, specUrl, { selectedServer: serverUrlRelativeToHost })).toBe("https://example.com/base-path/path") + }) + + it("build relative url with no server selected", function() { + expect(buildUrl(urlRelativeToBase, specUrl, { selectedServer: noServerSelected })).toBe("https://petstore.swagger.io/v2/relative-url/base-path/path") + expect(buildUrl(urlRelativeToHost, specUrl, { selectedServer: noServerSelected })).toBe("https://petstore.swagger.io/relative-url/base-path/path") + }) + it("build relative url with absolute server url", function() { + expect(buildUrl(urlRelativeToBase, specUrl, { selectedServer: absoluteServerUrl })).toBe("https://server-example.com/base-path/relative-url/base-path/path") + expect(buildUrl(urlRelativeToHost, specUrl, { selectedServer: absoluteServerUrl })).toBe("https://server-example.com/relative-url/base-path/path") }) - it("build url from server with level of paths", function() { - expect(buildUrl("relative-url", "http://example.com/base-path/level-1/level-2/level-3")).toBe("http://example.com/base-path/level-1/level-2/relative-url") - expect(buildUrl("/relative-url", "http://example.com/base-path/level-1/level-2/level-3")).toBe("http://example.com/relative-url") - expect(buildUrl("../relative-url", "http://example.com/base-path/level-1/level-2/level-3")).toBe("http://example.com/base-path/level-1/relative-url") + it("build relative url with server url relative to base", function() { + expect(buildUrl(urlRelativeToBase, specUrl, { selectedServer: serverUrlRelativeToBase })).toBe("https://petstore.swagger.io/v2/server-example/base-path/relative-url/base-path/path") + expect(buildUrl(urlRelativeToHost, specUrl, { selectedServer: serverUrlRelativeToBase })).toBe("https://petstore.swagger.io/relative-url/base-path/path") }) - it.skip("build url from server with relative url", function() { - expect(buildUrl("relative-url", "server-path/")).toBe("https://example.com/apis/swagger-api/petstore/1.0.0/server-path/relative-url") - expect(buildUrl("/relative-url", "server-path/")).toBe("https://example.com/relative-url") - expect(buildUrl("relative-url", "/server-path/")).toBe("https://example.com/server-path/relative-url") - expect(buildUrl("/relative-url", "/server-path/")).toBe("https://example.com/relative-url") + it("build relative url with server url relative to host", function() { + expect(buildUrl(urlRelativeToBase, specUrl, { selectedServer: serverUrlRelativeToHost })).toBe("https://petstore.swagger.io/server-example/base-path/relative-url/base-path/path") + expect(buildUrl(urlRelativeToHost, specUrl, { selectedServer: serverUrlRelativeToHost })).toBe("https://petstore.swagger.io/relative-url/base-path/path") }) }) From 53cad460e3152796e37f2d68aeaf7ca22786428f Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Fri, 24 Jul 2020 18:40:39 +0100 Subject: [PATCH 24/29] Added utils `addProtocol()` --- src/core/utils.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/utils.js b/src/core/utils.js index 12607cd4746..479594d5280 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -644,14 +644,19 @@ export function isAbsoluteUrl(url) { return url.match(/^(?:[a-z]+:)?\/\//i) // Matches http://, HTTP://, https://, ftp://, //example.com, } +export function addProtocol(url) { + if(!url.match(/^\/\//i)) return url // Checks if protocol is missing e.g. //example.com + return `${window.location.protocol}${url}` +} + export function buildBaseUrl(selectedServer, specUrl) { if(!selectedServer) return specUrl - if(isAbsoluteUrl(selectedServer)) return selectedServer + if(isAbsoluteUrl(selectedServer)) return addProtocol(selectedServer) return new URL(selectedServer, specUrl).href } -export function buildUrl(url, specUrl, { selectedServer="" } = {}) { +export function buildUrl(url, specUrl, { selectedServer="" } = {}) { console.log(url, specUrl, selectedServer) if(!url) return if(isAbsoluteUrl(url)) return url From 795355e55b87113ad1ee7a59e4e0f6d82c48f5d1 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Mon, 27 Jul 2020 11:09:14 +0100 Subject: [PATCH 25/29] Removed `console.log()` from utils --- src/core/utils.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/core/utils.js b/src/core/utils.js index 479594d5280..a11cf60f2e0 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -1,4 +1,4 @@ -/* +/* ATTENTION! This file (but not the functions within) is deprecated. You should probably add a new file to `./helpers/` instead of adding a new @@ -133,7 +133,7 @@ export function createObjWithHashedKeys (fdObj) { trackKeys[pair[0]].length += 1 let hashedKeyCurrent = `${pair[0]}${hashIdx}${trackKeys[pair[0]].length}` newObj[hashedKeyCurrent] = pair[1] - } + } } return newObj } @@ -263,13 +263,13 @@ export function extractFileNameFromContentDispositionHeader(value){ /filename="([^;]*);?"/i, /filename=([^;]*);?/i ] - + let responseFilename patterns.some(regex => { responseFilename = regex.exec(value) return responseFilename !== null }) - + if (responseFilename !== null && responseFilename.length > 1) { try { return decodeURIComponent(responseFilename[1]) @@ -656,8 +656,7 @@ export function buildBaseUrl(selectedServer, specUrl) { return new URL(selectedServer, specUrl).href } -export function buildUrl(url, specUrl, { selectedServer="" } = {}) { - console.log(url, specUrl, selectedServer) +export function buildUrl(url, specUrl, { selectedServer="" } = {}) { if(!url) return if(isAbsoluteUrl(url)) return url @@ -765,7 +764,7 @@ export function paramToIdentifier(param, { returnAll = false, allowHashes = true } const paramName = param.get("name") const paramIn = param.get("in") - + let generatedIdentifiers = [] // Generate identifiers in order of most to least specificity @@ -773,7 +772,7 @@ export function paramToIdentifier(param, { returnAll = false, allowHashes = true if (param && param.hashCode && paramIn && paramName && allowHashes) { generatedIdentifiers.push(`${paramIn}.${paramName}.hash-${param.hashCode()}`) } - + if(paramIn && paramName) { generatedIdentifiers.push(`${paramIn}.${paramName}`) } From 063286236db462c614733efd3af3c2413bf71974 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Mon, 27 Jul 2020 11:10:23 +0100 Subject: [PATCH 26/29] Add PropTypes `url` --- src/core/components/info.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/components/info.jsx b/src/core/components/info.jsx index 3618746b6b2..1801bf32471 100644 --- a/src/core/components/info.jsx +++ b/src/core/components/info.jsx @@ -28,6 +28,7 @@ class Contact extends React.Component { getComponent: PropTypes.func.isRequired, specSelectors: PropTypes.object.isRequired, selectedServer: PropTypes.string, + url: PropTypes.string.isRequired, } render(){ @@ -57,6 +58,7 @@ class License extends React.Component { getComponent: PropTypes.func.isRequired, specSelectors: PropTypes.object.isRequired, selectedServer: PropTypes.string, + url: PropTypes.string.isRequired, } render(){ From 867b0aa240e4def381d296749eccf23740fde030 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Mon, 27 Jul 2020 11:10:58 +0100 Subject: [PATCH 27/29] Added `specSelector.url()` to mock props for unit test --- test/mocha/components/operations.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/mocha/components/operations.jsx b/test/mocha/components/operations.jsx index 844a4931be1..4da89427696 100644 --- a/test/mocha/components/operations.jsx +++ b/test/mocha/components/operations.jsx @@ -29,6 +29,7 @@ describe("", function(){ }, specSelectors: { isOAS3() { return false }, + url() { return "https://petstore.swagger.io/v2/swagger.json" }, taggedOperations() { return fromJS({ "default": { @@ -83,6 +84,7 @@ describe("", function(){ }, specSelectors: { isOAS3() { return true }, + url() { return "https://petstore.swagger.io/v2/swagger.json" }, taggedOperations() { return fromJS({ "default": { From cba7b4f1df32d5a96d9907bb3b0bd06678331385 Mon Sep 17 00:00:00 2001 From: geraldglynn Date: Tue, 4 Aug 2020 18:52:44 +0100 Subject: [PATCH 28/29] Moved new url util functions to `utils/url` --- src/core/components/info.jsx | 3 ++- src/core/components/operation-tag.jsx | 3 ++- src/core/components/operation.jsx | 3 ++- src/core/utils.js | 24 ------------------------ src/core/utils/url.js | 23 +++++++++++++++++++++++ test/mocha/core/utils.js | 12 ++++++++---- 6 files changed, 37 insertions(+), 31 deletions(-) create mode 100644 src/core/utils/url.js diff --git a/src/core/components/info.jsx b/src/core/components/info.jsx index 1801bf32471..2926bd77a1d 100644 --- a/src/core/components/info.jsx +++ b/src/core/components/info.jsx @@ -1,7 +1,8 @@ import React from "react" import PropTypes from "prop-types" import ImPropTypes from "react-immutable-proptypes" -import { sanitizeUrl, buildUrl } from "core/utils" +import { sanitizeUrl } from "core/utils" +import { buildUrl } from "core/utils/url" export class InfoBasePath extends React.Component { diff --git a/src/core/components/operation-tag.jsx b/src/core/components/operation-tag.jsx index e0354240c1b..1e71c58797f 100644 --- a/src/core/components/operation-tag.jsx +++ b/src/core/components/operation-tag.jsx @@ -2,7 +2,8 @@ import React from "react" import PropTypes from "prop-types" import ImPropTypes from "react-immutable-proptypes" import Im from "immutable" -import { createDeepLinkPath, escapeDeepLinkPath, sanitizeUrl, buildUrl } from "core/utils" +import { createDeepLinkPath, escapeDeepLinkPath, sanitizeUrl } from "core/utils" +import { buildUrl } from "core/utils/url" export default class OperationTag extends React.Component { diff --git a/src/core/components/operation.jsx b/src/core/components/operation.jsx index 4216be72b12..117d8227137 100644 --- a/src/core/components/operation.jsx +++ b/src/core/components/operation.jsx @@ -1,7 +1,8 @@ import React, { PureComponent } from "react" import PropTypes from "prop-types" import { getList } from "core/utils" -import { getExtensions, sanitizeUrl, escapeDeepLinkPath, buildUrl } from "core/utils" +import { getExtensions, sanitizeUrl, escapeDeepLinkPath } from "core/utils" +import { buildUrl } from "core/utils/url" import { Iterable, List } from "immutable" import ImPropTypes from "react-immutable-proptypes" diff --git a/src/core/utils.js b/src/core/utils.js index d35dbaf7ce0..30b3f5fd641 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -640,30 +640,6 @@ export function sanitizeUrl(url) { return braintreeSanitizeUrl(url) } -export function isAbsoluteUrl(url) { - return url.match(/^(?:[a-z]+:)?\/\//i) // Matches http://, HTTP://, https://, ftp://, //example.com, -} - -export function addProtocol(url) { - if(!url.match(/^\/\//i)) return url // Checks if protocol is missing e.g. //example.com - return `${window.location.protocol}${url}` -} - -export function buildBaseUrl(selectedServer, specUrl) { - if(!selectedServer) return specUrl - if(isAbsoluteUrl(selectedServer)) return addProtocol(selectedServer) - - return new URL(selectedServer, specUrl).href -} - -export function buildUrl(url, specUrl, { selectedServer="" } = {}) { - if(!url) return - if(isAbsoluteUrl(url)) return url - - const baseUrl = buildBaseUrl(selectedServer, specUrl) - return new URL(url, baseUrl).href -} - export function requiresValidationURL(uri) { if (!uri || uri.indexOf("localhost") >= 0 || uri.indexOf("127.0.0.1") >= 0 || uri === "none") { return false diff --git a/src/core/utils/url.js b/src/core/utils/url.js new file mode 100644 index 00000000000..2f063445de4 --- /dev/null +++ b/src/core/utils/url.js @@ -0,0 +1,23 @@ +export function isAbsoluteUrl(url) { + return url.match(/^(?:[a-z]+:)?\/\//i) // Matches http://, HTTP://, https://, ftp://, //example.com, +} + +export function addProtocol(url) { + if(!url.match(/^\/\//i)) return url // Checks if protocol is missing e.g. //example.com + return `${window.location.protocol}${url}` +} + +export function buildBaseUrl(selectedServer, specUrl) { + if(!selectedServer) return specUrl + if(isAbsoluteUrl(selectedServer)) return addProtocol(selectedServer) + + return new URL(selectedServer, specUrl).href +} + +export function buildUrl(url, specUrl, { selectedServer="" } = {}) { + if(!url) return + if(isAbsoluteUrl(url)) return url + + const baseUrl = buildBaseUrl(selectedServer, specUrl) + return new URL(url, baseUrl).href +} diff --git a/test/mocha/core/utils.js b/test/mocha/core/utils.js index 31d59c4584a..3af771907ed 100644 --- a/test/mocha/core/utils.js +++ b/test/mocha/core/utils.js @@ -22,10 +22,7 @@ import { escapeDeepLinkPath, getExtensions, getCommonExtensions, - sanitizeUrl, - isAbsoluteUrl, - buildBaseUrl, - buildUrl, + sanitizeUrl, requiresValidationURL, extractFileNameFromContentDispositionHeader, deeplyStripKey, @@ -35,6 +32,13 @@ import { generateCodeVerifier, createCodeChallenge, } from "core/utils" + +import { + isAbsoluteUrl, + buildBaseUrl, + buildUrl, +} from "core/utils/url" + import win from "core/window" describe("utils", function() { From 89b8d1a44654f608e96d6d6140399bec2c225c32 Mon Sep 17 00:00:00 2001 From: Tim Lai Date: Tue, 4 Aug 2020 12:47:33 -0700 Subject: [PATCH 29/29] style: Update test/mocha/core/utils.js --- test/mocha/core/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mocha/core/utils.js b/test/mocha/core/utils.js index 3af771907ed..e55c4cbb3d9 100644 --- a/test/mocha/core/utils.js +++ b/test/mocha/core/utils.js @@ -22,7 +22,7 @@ import { escapeDeepLinkPath, getExtensions, getCommonExtensions, - sanitizeUrl, + sanitizeUrl, requiresValidationURL, extractFileNameFromContentDispositionHeader, deeplyStripKey,