From abf34961e92efdf041445a353612a0ddb2d84aed Mon Sep 17 00:00:00 2001 From: kyle Date: Tue, 29 Jan 2019 16:46:03 -0600 Subject: [PATCH] bug: parameter `allowEmptyValue` + `required` interactions (via #5142) * add failing tests * standardize parameter keying * validateParam test migrations * migrate test cases to new pattern * disambiguate name/in ordering in `body.body` test cases * `name+in`=> `{in}.{name}` * consider allowEmptyValue parameter inclusion in runtime validation * use config object for all validateParam options * drop isXml flag from validateParams --- src/core/plugins/spec/actions.js | 14 +- src/core/plugins/spec/reducers.js | 33 +- src/core/plugins/spec/selectors.js | 11 +- src/core/utils.js | 46 +- test/core/plugins/spec/reducer.js | 14 +- test/core/plugins/spec/selectors.js | 50 +- test/core/utils.js | 787 +++++++++++------- .../static/documents/bugs/5129.yaml | 26 + test/e2e-cypress/tests/bugs/5129.js | 121 +++ 9 files changed, 719 insertions(+), 383 deletions(-) create mode 100644 test/e2e-cypress/static/documents/bugs/5129.yaml create mode 100644 test/e2e-cypress/tests/bugs/5129.js diff --git a/src/core/plugins/spec/actions.js b/src/core/plugins/spec/actions.js index 980ba284..4e051bd4 100644 --- a/src/core/plugins/spec/actions.js +++ b/src/core/plugins/spec/actions.js @@ -5,7 +5,7 @@ import serializeError from "serialize-error" import isString from "lodash/isString" import debounce from "lodash/debounce" import set from "lodash/set" -import { isJSONObject } from "core/utils" +import { isJSONObject, paramToValue } from "core/utils" // Actions conform to FSA (flux-standard-actions) // {type: string,payload: Any|Error, meta: obj, error: bool} @@ -345,19 +345,19 @@ export const executeRequest = (req) => // ensure that explicitly-included params are in the request - if(op && op.parameters && op.parameters.length) { - op.parameters - .filter(param => param && param.allowEmptyValue === true) + if (operation && operation.get("parameters")) { + operation.get("parameters") + .filter(param => param && param.get("allowEmptyValue") === true) .forEach(param => { - if (specSelectors.parameterInclusionSettingFor([pathName, method], param.name, param.in)) { + if (specSelectors.parameterInclusionSettingFor([pathName, method], param.get("name"), param.get("in"))) { req.parameters = req.parameters || {} - const paramValue = req.parameters[param.name] + const paramValue = paramToValue(param, req.parameters) // if the value is falsy or an empty Immutable iterable... if(!paramValue || (paramValue && paramValue.size === 0)) { // set it to empty string, so Swagger Client will treat it as // present but empty. - req.parameters[param.name] = "" + req.parameters[param.get("name")] = "" } } }) diff --git a/src/core/plugins/spec/reducers.js b/src/core/plugins/spec/reducers.js index ef0424b7..e9eba190 100644 --- a/src/core/plugins/spec/reducers.js +++ b/src/core/plugins/spec/reducers.js @@ -1,10 +1,12 @@ import { fromJS, List } from "immutable" -import { fromJSOrdered, validateParam } from "core/utils" +import { fromJSOrdered, validateParam, paramToValue } from "core/utils" import win from "../../window" // selector-in-reducer is suboptimal, but `operationWithMeta` is more of a helper import { - operationWithMeta + specJsonWithResolvedSubtrees, + parameterValues, + parameterInclusionSettingFor, } from "./selectors" import { @@ -25,6 +27,7 @@ import { CLEAR_VALIDATE_PARAMS, SET_SCHEME } from "./actions" +import { paramToIdentifier } from "../../utils" export default { @@ -54,14 +57,7 @@ export default { [UPDATE_PARAM]: ( state, {payload} ) => { let { path: pathMethod, paramName, paramIn, param, value, isXml } = payload - let paramKey - - // `hashCode` is an Immutable.js Map method - if(param && param.hashCode && !paramIn && !paramName) { - paramKey = `${param.get("name")}.${param.get("in")}.hash-${param.hashCode()}` - } else { - paramKey = `${paramName}.${paramIn}` - } + let paramKey = param ? paramToIdentifier(param) : `${paramIn}.${paramName}` const valueKey = isXml ? "value_xml" : "value" @@ -79,7 +75,7 @@ export default { return state } - const paramKey = `${paramName}.${paramIn}` + const paramKey = `${paramIn}.${paramName}` return state.setIn( ["meta", "paths", ...pathMethod, "parameter_inclusions", paramKey], @@ -88,15 +84,18 @@ export default { }, [VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => { - let meta = state.getIn( [ "meta", "paths", ...pathMethod ], fromJS({}) ) - let isXml = /xml/i.test(meta.get("consumes_value")) - - const op = operationWithMeta(state, ...pathMethod) + const op = specJsonWithResolvedSubtrees(state).getIn(["paths", ...pathMethod]) + const paramValues = parameterValues(state, pathMethod).toJS() return state.updateIn(["meta", "paths", ...pathMethod, "parameters"], fromJS({}), paramMeta => { return op.get("parameters", List()).reduce((res, param) => { - const errors = validateParam(param, isXml, isOAS3) - return res.setIn([`${param.get("name")}.${param.get("in")}`, "errors"], fromJS(errors)) + const value = paramToValue(param, paramValues) + const isEmptyValueIncluded = parameterInclusionSettingFor(state, pathMethod, param.get("name"), param.get("in")) + const errors = validateParam(param, value, { + bypassRequiredCheck: isEmptyValueIncluded, + isOAS3, + }) + return res.setIn([paramToIdentifier(param), "errors"], fromJS(errors)) }, paramMeta) }) }, diff --git a/src/core/plugins/spec/selectors.js b/src/core/plugins/spec/selectors.js index 0274e213..c3d0ab2b 100644 --- a/src/core/plugins/spec/selectors.js +++ b/src/core/plugins/spec/selectors.js @@ -1,6 +1,7 @@ import { createSelector } from "reselect" import { sorters } from "core/utils" import { fromJS, Set, Map, OrderedMap, List } from "immutable" +import { paramToIdentifier } from "../../utils" const DEFAULT_TAG = "default" @@ -302,11 +303,11 @@ export const parameterWithMetaByIdentity = (state, pathMethod, param) => { const metaParams = state.getIn(["meta", "paths", ...pathMethod, "parameters"], OrderedMap()) const mergedParams = opParams.map((currentParam) => { - const nameInKeyedMeta = metaParams.get(`${param.get("name")}.${param.get("in")}`) - const hashKeyedMeta = metaParams.get(`${param.get("name")}.${param.get("in")}.hash-${param.hashCode()}`) + const inNameKeyedMeta = metaParams.get(`${param.get("in")}.${param.get("name")}`) + const hashKeyedMeta = metaParams.get(`${param.get("in")}.${param.get("name")}.hash-${param.hashCode()}`) return OrderedMap().merge( currentParam, - nameInKeyedMeta, + inNameKeyedMeta, hashKeyedMeta ) }) @@ -315,7 +316,7 @@ export const parameterWithMetaByIdentity = (state, pathMethod, param) => { } export const parameterInclusionSettingFor = (state, pathMethod, paramName, paramIn) => { - const paramKey = `${paramName}.${paramIn}` + const paramKey = `${paramIn}.${paramName}` return state.getIn(["meta", "paths", ...pathMethod, "parameter_inclusions", paramKey], false) } @@ -364,7 +365,7 @@ export function parameterValues(state, pathMethod, isXml) { let paramValues = operationWithMeta(state, ...pathMethod).get("parameters", List()) return paramValues.reduce( (hash, p) => { let value = isXml && p.get("in") === "body" ? p.get("value_xml") : p.get("value") - return hash.set(`${p.get("in")}.${p.get("name")}`, value) + return hash.set(paramToIdentifier(p, { allowHashes: false }), value) }, fromJS({})) } diff --git a/src/core/utils.js b/src/core/utils.js index 7bd61314..d3e6104e 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -484,9 +484,8 @@ export const validatePattern = (val, rxPattern) => { } // validation of parameters before execute -export const validateParam = (param, isXml, isOAS3 = false) => { +export const validateParam = (param, value, { isOAS3 = false, bypassRequiredCheck = false } = {}) => { let errors = [] - let value = isXml && param.get("in") === "body" ? param.get("value_xml") : param.get("value") let required = param.get("required") let paramDetails = isOAS3 ? param.get("schema") : param @@ -501,7 +500,6 @@ export const validateParam = (param, isXml, isOAS3 = false) => { let minLength = paramDetails.get("minLength") let pattern = paramDetails.get("pattern") - /* If the parameter is required OR the parameter has a value (meaning optional, but filled in) then we should do our validation routine. @@ -540,7 +538,7 @@ export const validateParam = (param, isXml, isOAS3 = false) => { const passedAnyCheck = allChecks.some(v => !!v) - if ( required && !passedAnyCheck ) { + if (required && !passedAnyCheck && !bypassRequiredCheck ) { errors.push("Required field is not provided") return errors } @@ -805,3 +803,43 @@ export function numberToString(thing) { return thing } + +export function paramToIdentifier(param, { returnAll = false, allowHashes = true } = {}) { + if(!Im.Map.isMap(param)) { + throw new Error("paramToIdentifier: received a non-Im.Map parameter as input") + } + const paramName = param.get("name") + const paramIn = param.get("in") + + let generatedIdentifiers = [] + + // Generate identifiers in order of most to least specificity + + if (param && param.hashCode && paramIn && paramName && allowHashes) { + generatedIdentifiers.push(`${paramIn}.${paramName}.hash-${param.hashCode()}`) + } + + if(paramIn && paramName) { + generatedIdentifiers.push(`${paramIn}.${paramName}`) + } + + generatedIdentifiers.push(paramName) + + // Return the most preferred identifier, or all if requested + + return returnAll ? generatedIdentifiers : (generatedIdentifiers[0] || "") +} + +export function paramToValue(param, paramValues) { + const allIdentifiers = paramToIdentifier(param, { returnAll: true }) + + // Map identifiers to values in the provided value hash, filter undefined values, + // and return the first value found + const values = allIdentifiers + .map(id => { + return paramValues[id] + }) + .filter(value => value !== undefined) + + return values[0] +} \ No newline at end of file diff --git a/test/core/plugins/spec/reducer.js b/test/core/plugins/spec/reducer.js index 643d249f..b6098f09 100644 --- a/test/core/plugins/spec/reducer.js +++ b/test/core/plugins/spec/reducer.js @@ -130,7 +130,7 @@ describe("spec plugin - reducer", function(){ }) }) describe("SPEC_UPDATE_PARAM", function() { - it("should store parameter values by name+in", () => { + it("should store parameter values by {in}.{name}", () => { const updateParam = reducer["spec_update_param"] const path = "/pet/post" @@ -140,14 +140,14 @@ describe("spec plugin - reducer", function(){ const result = updateParam(state, { payload: { path: [path, method], - paramName: "body", + paramName: "myBody", paramIn: "body", value: `{ "a": 123 }`, isXml: false } }) - const response = result.getIn(["meta", "paths", path, method, "parameters", "body.body", "value"]) + const response = result.getIn(["meta", "paths", path, method, "parameters", "body.myBody", "value"]) expect(response).toEqual(`{ "a": 123 }`) }) it("should store parameter values by identity", () => { @@ -157,7 +157,7 @@ describe("spec plugin - reducer", function(){ const method = "POST" const param = fromJS({ - name: "body", + name: "myBody", in: "body", schema: { type: "string" @@ -174,12 +174,12 @@ describe("spec plugin - reducer", function(){ } }) - const value = result.getIn(["meta", "paths", path, method, "parameters", `body.body.hash-${param.hashCode()}`, "value"]) + const value = result.getIn(["meta", "paths", path, method, "parameters", `body.myBody.hash-${param.hashCode()}`, "value"]) expect(value).toEqual(`{ "a": 123 }`) }) }) describe("SPEC_UPDATE_EMPTY_PARAM_INCLUSION", function() { - it("should store parameter values by name+in", () => { + it("should store parameter values by {in}.{name}", () => { const updateParam = reducer["spec_update_empty_param_inclusion"] const path = "/pet/post" @@ -196,7 +196,7 @@ describe("spec plugin - reducer", function(){ } }) - const response = result.getIn(["meta", "paths", path, method, "parameter_inclusions", "param.query"]) + const response = result.getIn(["meta", "paths", path, method, "parameter_inclusions", "query.param"]) expect(response).toEqual(true) }) }) diff --git a/test/core/plugins/spec/selectors.js b/test/core/plugins/spec/selectors.js index 0244bcff..6f3e8335 100644 --- a/test/core/plugins/spec/selectors.js +++ b/test/core/plugins/spec/selectors.js @@ -497,7 +497,7 @@ describe("spec plugin - selectors", function(){ }) describe("operationWithMeta", function() { - it("should support merging in name+in keyed param metadata", function () { + it("should support merging in {in}.{name} keyed param metadata", function () { const state = fromJS({ json: { paths: { @@ -505,7 +505,7 @@ describe("spec plugin - selectors", function(){ "get": { parameters: [ { - name: "body", + name: "myBody", in: "body" } ] @@ -518,7 +518,7 @@ describe("spec plugin - selectors", function(){ "/": { "get": { parameters: { - "body.body": { + "body.myBody": { value: "abc123" } } @@ -533,7 +533,7 @@ describe("spec plugin - selectors", function(){ expect(result.toJS()).toEqual({ parameters: [ { - name: "body", + name: "myBody", in: "body", value: "abc123" } @@ -542,7 +542,7 @@ describe("spec plugin - selectors", function(){ }) it("should support merging in hash-keyed param metadata", function () { const bodyParam = fromJS({ - name: "body", + name: "myBody", in: "body" }) @@ -563,7 +563,7 @@ describe("spec plugin - selectors", function(){ "/": { "get": { parameters: { - [`body.body.hash-${bodyParam.hashCode()}`]: { + [`body.myBody.hash-${bodyParam.hashCode()}`]: { value: "abc123" } } @@ -578,7 +578,7 @@ describe("spec plugin - selectors", function(){ expect(result.toJS()).toEqual({ parameters: [ { - name: "body", + name: "myBody", in: "body", value: "abc123" } @@ -587,7 +587,7 @@ describe("spec plugin - selectors", function(){ }) }) describe("parameterWithMeta", function() { - it("should support merging in name+in keyed param metadata", function () { + it("should support merging in {in}.{name} keyed param metadata", function () { const state = fromJS({ json: { paths: { @@ -595,7 +595,7 @@ describe("spec plugin - selectors", function(){ "get": { parameters: [ { - name: "body", + name: "myBody", in: "body" } ] @@ -608,7 +608,7 @@ describe("spec plugin - selectors", function(){ "/": { "get": { parameters: { - "body.body": { + "body.myBody": { value: "abc123" } } @@ -618,17 +618,17 @@ describe("spec plugin - selectors", function(){ } }) - const result = parameterWithMeta(state, ["/", "get"], "body", "body") + const result = parameterWithMeta(state, ["/", "get"], "myBody", "body") expect(result.toJS()).toEqual({ - name: "body", + name: "myBody", in: "body", value: "abc123" }) }) it("should give best-effort when encountering hash-keyed param metadata", function () { const bodyParam = fromJS({ - name: "body", + name: "myBody", in: "body" }) @@ -649,7 +649,7 @@ describe("spec plugin - selectors", function(){ "/": { "get": { parameters: { - [`body.body.hash-${bodyParam.hashCode()}`]: { + [`body.myBody.hash-${bodyParam.hashCode()}`]: { value: "abc123" } } @@ -659,10 +659,10 @@ describe("spec plugin - selectors", function(){ } }) - const result = parameterWithMeta(state, ["/", "get"], "body", "body") + const result = parameterWithMeta(state, ["/", "get"], "myBody", "body") expect(result.toJS()).toEqual({ - name: "body", + name: "myBody", in: "body", value: "abc123" }) @@ -670,9 +670,9 @@ describe("spec plugin - selectors", function(){ }) describe("parameterWithMetaByIdentity", function() { - it("should support merging in name+in keyed param metadata", function () { + it("should support merging in {in}.{name} keyed param metadata", function () { const bodyParam = fromJS({ - name: "body", + name: "myBody", in: "body" }) @@ -691,7 +691,7 @@ describe("spec plugin - selectors", function(){ "/": { "get": { parameters: { - "body.body": { + "body.myBody": { value: "abc123" } } @@ -704,14 +704,14 @@ describe("spec plugin - selectors", function(){ const result = parameterWithMetaByIdentity(state, ["/", "get"], bodyParam) expect(result.toJS()).toEqual({ - name: "body", + name: "myBody", in: "body", value: "abc123" }) }) it("should support merging in hash-keyed param metadata", function () { const bodyParam = fromJS({ - name: "body", + name: "myBody", in: "body" }) @@ -732,7 +732,7 @@ describe("spec plugin - selectors", function(){ "/": { "get": { parameters: { - [`body.body.hash-${bodyParam.hashCode()}`]: { + [`body.myBody.hash-${bodyParam.hashCode()}`]: { value: "abc123" } } @@ -745,14 +745,14 @@ describe("spec plugin - selectors", function(){ const result = parameterWithMetaByIdentity(state, ["/", "get"], bodyParam) expect(result.toJS()).toEqual({ - name: "body", + name: "myBody", in: "body", value: "abc123" }) }) }) describe("parameterInclusionSettingFor", function() { - it("should support getting name+in param inclusion settings", function () { + it("should support getting {in}.{name} param inclusion settings", function () { const param = fromJS({ name: "param", in: "query", @@ -776,7 +776,7 @@ describe("spec plugin - selectors", function(){ "/": { "get": { "parameter_inclusions": { - [`param.query`]: true + [`query.param`]: true } } } diff --git a/test/core/utils.js b/test/core/utils.js index b292a6be..b5af0870 100644 --- a/test/core/utils.js +++ b/test/core/utils.js @@ -25,14 +25,16 @@ import { sanitizeUrl, extractFileNameFromContentDispositionHeader, deeplyStripKey, - getSampleSchema + getSampleSchema, + paramToIdentifier, + paramToValue, } from "core/utils" 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({ @@ -43,17 +45,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({ @@ -71,39 +73,39 @@ 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" @@ -121,47 +123,47 @@ describe("utils", function() { 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() @@ -173,25 +175,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) @@ -201,10 +203,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() @@ -216,25 +218,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) @@ -244,10 +246,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() @@ -257,23 +259,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() @@ -282,56 +284,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() @@ -339,82 +341,87 @@ describe("utils", function() { expect(() => validatePattern("aValue", [])).toNotThrow() }) }) - + describe("validateParam", function() { let param = null + let value = null let result = null - - const assertValidateParam = (param, expectedError) => { + + const assertValidateParam = (param, value, expectedError) => { // Swagger 2.0 version - result = validateParam( fromJS(param), false ) + result = validateParam( fromJS(param), fromJS(value)) expect( result ).toEqual( expectedError ) - + // OAS3 version, using `schema` sub-object let oas3Param = { - value: param.value, required: param.required, schema: { ...param, - value: undefined, required: undefined } } - result = validateParam( fromJS(oas3Param), false, true ) + result = validateParam( fromJS(oas3Param), fromJS(value), { + isOAS3: true + }) expect( result ).toEqual( expectedError ) } - - const assertValidateOas3Param = (param, expectedError) => { + + const assertValidateOas3Param = (param, value, expectedError) => { // for cases where you _only_ want to try OAS3 - result = validateParam( fromJS(param), false, true ) + result = validateParam(fromJS(param), value, { + isOAS3: true + }) 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 param = fromJS({ - value: "", required: true }) - result = validateParam( param, false, true ) + value = "" + result = validateParam( param, value, { + isOAS3: true + } ) expect( result ).toEqual( [] ) }) - + it("validates required OAS3 objects", function() { // valid object param = { required: true, schema: { type: "object" - }, - value: { - abc: 123 } } - assertValidateOas3Param(param, []) - + value = { + abc: 123 + } + assertValidateOas3Param(param, value, []) + // valid object-as-string param = { required: true, schema: { type: "object" - }, - value: JSON.stringify({ - abc: 123 - }) + } } - assertValidateOas3Param(param, []) - + value = JSON.stringify({ + abc: 123 + }) + assertValidateOas3Param(param, value, []) + // invalid object-as-string param = { required: true, schema: { type: "object" - }, - value: "{{}" + } } - assertValidateOas3Param(param, ["Parameter string value must be valid JSON"]) - + value = "{{}" + assertValidateOas3Param(param, value, ["Parameter string value must be valid JSON"]) + // missing when required param = { required: true, @@ -422,487 +429,490 @@ describe("utils", function() { type: "object" }, } - assertValidateOas3Param(param, ["Required field is not provided"]) + value = undefined + assertValidateOas3Param(param, value, ["Required field is not provided"]) }) - + it("validates optional OAS3 objects", function() { // valid object param = { schema: { type: "object" - }, - value: { - abc: 123 } } - assertValidateOas3Param(param, []) - + value = { + abc: 123 + } + assertValidateOas3Param(param, value, []) + // valid object-as-string param = { schema: { type: "object" - }, - value: JSON.stringify({ - abc: 123 - }) + } } - assertValidateOas3Param(param, []) - + value = JSON.stringify({ + abc: 123 + }) + assertValidateOas3Param(param, value, []) + // invalid object-as-string param = { schema: { type: "object" - }, - value: "{{}" + } } - assertValidateOas3Param(param, ["Parameter string value must be valid JSON"]) - + value = "{{}" + assertValidateOas3Param(param, value, ["Parameter string value must be valid JSON"]) + // missing when not required param = { schema: { type: "object" }, } - assertValidateOas3Param(param, []) + value = undefined + assertValidateOas3Param(param, value, []) }) - + it("validates required strings", function() { // invalid string param = { required: true, - type: "string", - value: "" + type: "string" } - assertValidateParam(param, ["Required field is not provided"]) - + value = "" + assertValidateParam(param, value, ["Required field is not provided"]) + // valid string param = { required: true, - type: "string", - value: "test string" + type: "string" } - assertValidateParam(param, []) - + value = "test string" + assertValidateParam(param, value, []) + // valid string with min and max length param = { required: true, type: "string", - value: "test string", maxLength: 50, minLength: 1 } - assertValidateParam(param, []) + value = "test string" + assertValidateParam(param, value, []) }) - + it("validates required strings with min and max length", function() { // invalid string with max length param = { required: true, type: "string", - value: "test string", maxLength: 5 } - assertValidateParam(param, ["Value must be less than MaxLength"]) - + value = "test string" + assertValidateParam(param, value, ["Value must be less than MaxLength"]) + // invalid string with max length 0 param = { required: true, type: "string", - value: "test string", maxLength: 0 } - assertValidateParam(param, ["Value must be less than MaxLength"]) - + value = "test string" + assertValidateParam(param, value, ["Value must be less than MaxLength"]) + // invalid string with min length param = { required: true, type: "string", - value: "test string", minLength: 50 } - assertValidateParam(param, ["Value must be greater than MinLength"]) + value = "test string" + assertValidateParam(param, value, ["Value must be greater than MinLength"]) }) - + it("validates optional strings", function() { // valid (empty) string param = { required: false, - type: "string", - value: "" + type: "string" } - assertValidateParam(param, []) - + value = "" + assertValidateParam(param, value, []) + // valid string param = { required: false, - type: "string", - value: "test" + type: "string" } - assertValidateParam(param, []) + value = "test" + assertValidateParam(param, value, []) }) - + it("validates required files", function() { // invalid file param = { required: true, - type: "file", - value: undefined + type: "file" } - assertValidateParam(param, ["Required field is not provided"]) - + value = undefined + assertValidateParam(param, value, ["Required field is not provided"]) + // valid file param = { required: true, - type: "file", - value: new win.File() + type: "file" } - assertValidateParam(param, []) + value = new win.File() + assertValidateParam(param, value, []) }) - + it("validates optional files", function() { // invalid file param = { required: false, - type: "file", - value: "not a file" + type: "file" } - assertValidateParam(param, ["Value must be a file"]) - + value = "not a file" + assertValidateParam(param, value, ["Value must be a file"]) + // valid (empty) file param = { required: false, - type: "file", - value: undefined + type: "file" } - assertValidateParam(param, []) - + value = undefined + assertValidateParam(param, value, []) + // valid file param = { required: false, - type: "file", - value: new win.File() + type: "file" } - assertValidateParam(param, []) + value = new win.File() + assertValidateParam(param, value, []) }) - + it("validates required arrays", function() { // invalid (empty) array param = { required: true, - type: "array", - value: [] + type: "array" } - assertValidateParam(param, ["Required field is not provided"]) - + value = [] + assertValidateParam(param, value, ["Required field is not provided"]) + // invalid (not an array) param = { required: true, - type: "array", - value: undefined + type: "array" } - assertValidateParam(param, ["Required field is not provided"]) - + value = undefined + assertValidateParam(param, value, ["Required field is not provided"]) + // invalid array, items do not match correct type param = { required: true, type: "array", - value: [1], items: { type: "string" } } - assertValidateParam(param, [{index: 0, error: "Value must be a string"}]) - + value = [1] + assertValidateParam(param, value, [{index: 0, error: "Value must be a string"}]) + // valid array, with no 'type' for items param = { required: true, - type: "array", - value: ["1"] + type: "array" } - assertValidateParam(param, []) - + value = [1] + assertValidateParam(param, value, []) + // valid array, items match type param = { required: true, type: "array", - value: ["1"], items: { type: "string" } } - assertValidateParam(param, []) + value = ["1"] + assertValidateParam(param, value, []) }) - + it("validates optional arrays", function() { // valid, empty array param = { required: false, - type: "array", - value: [] + type: "array" } - assertValidateParam(param, []) - + value = [] + assertValidateParam(param, value, []) + // invalid, items do not match correct type param = { required: false, type: "array", - value: ["number"], items: { type: "number" } } - assertValidateParam(param, [{index: 0, error: "Value must be a number"}]) - + value = ["number"] + assertValidateParam(param, value, [{index: 0, error: "Value must be a number"}]) + // valid param = { required: false, type: "array", - value: ["test"], items: { type: "string" } } - assertValidateParam(param, []) + value = ["test"] + assertValidateParam(param, value, []) }) - + it("validates required booleans", function() { // invalid boolean value param = { required: true, - type: "boolean", - value: undefined + type: "boolean" } - assertValidateParam(param, ["Required field is not provided"]) - + value = undefined + assertValidateParam(param, value, ["Required field is not provided"]) + // invalid boolean value (not a boolean) param = { required: true, - type: "boolean", - value: "test string" + type: "boolean" } - assertValidateParam(param, ["Value must be a boolean"]) - + value = "test string" + assertValidateParam(param, value, ["Value must be a boolean"]) + // valid boolean value param = { required: true, - type: "boolean", - value: "true" + type: "boolean" } - assertValidateParam(param, []) - + value = "true" + assertValidateParam(param, value, []) + // valid boolean value param = { required: true, - type: "boolean", - value: false + type: "boolean" } - assertValidateParam(param, []) + value = false + assertValidateParam(param, value, []) }) - + it("validates optional booleans", function() { // valid (empty) boolean value param = { required: false, - type: "boolean", - value: undefined + type: "boolean" } - assertValidateParam(param, []) - + value = undefined + assertValidateParam(param, value, []) + // invalid boolean value (not a boolean) param = { required: false, - type: "boolean", - value: "test string" + type: "boolean" } - assertValidateParam(param, ["Value must be a boolean"]) - + value = "test string" + assertValidateParam(param, value, ["Value must be a boolean"]) + // valid boolean value param = { required: false, - type: "boolean", - value: "true" + type: "boolean" } - assertValidateParam(param, []) - + value = "true" + assertValidateParam(param, value, []) + // valid boolean value param = { required: false, - type: "boolean", - value: false + type: "boolean" } - assertValidateParam(param, []) + value = false + assertValidateParam(param, value, []) }) - + it("validates required numbers", function() { // invalid number, string instead of a number param = { required: true, - type: "number", - value: "test" + type: "number" } - assertValidateParam(param, ["Value must be a number"]) - + value = "test" + assertValidateParam(param, value, ["Value must be a number"]) + // invalid number, undefined value param = { required: true, - type: "number", - value: undefined + type: "number" } - assertValidateParam(param, ["Required field is not provided"]) - + value = undefined + assertValidateParam(param, value, ["Required field is not provided"]) + // valid number with min and max param = { required: true, type: "number", - value: 10, minimum: 5, maximum: 99 } - assertValidateParam(param, []) - + value = 10 + assertValidateParam(param, value, []) + // valid negative number with min and max param = { required: true, type: "number", - value: -10, minimum: -50, maximum: -5 } - assertValidateParam(param, []) - + value = -10 + assertValidateParam(param, value, []) + // invalid number with maximum:0 param = { required: true, type: "number", - value: 1, + maximum: 0 } - assertValidateParam(param, ["Value must be less than Maximum"]) - + value = 1 + assertValidateParam(param, value, ["Value must be less than Maximum"]) + // invalid number with minimum:0 param = { required: true, type: "number", - value: -10, minimum: 0 } - assertValidateParam(param, ["Value must be greater than Minimum"]) + value = -10 + assertValidateParam(param, value, ["Value must be greater than Minimum"]) }) - + it("validates optional numbers", function() { // invalid number, string instead of a number param = { required: false, - type: "number", - value: "test" + type: "number" } - assertValidateParam(param, ["Value must be a number"]) - + value = "test" + assertValidateParam(param, value, ["Value must be a number"]) + // valid (empty) number param = { required: false, - type: "number", - value: undefined + type: "number" } - assertValidateParam(param, []) - + value = undefined + assertValidateParam(param, value, []) + // valid number param = { required: false, - type: "number", - value: 10 + type: "number" } - assertValidateParam(param, []) + value = 10 + assertValidateParam(param, value, []) }) - + it("validates required integers", function() { // invalid integer, string instead of an integer param = { required: true, - type: "integer", - value: "test" + type: "integer" } - assertValidateParam(param, ["Value must be an integer"]) - + value = "test" + assertValidateParam(param, value, ["Value must be an integer"]) + // invalid integer, undefined value param = { required: true, - type: "integer", - value: undefined + type: "integer" } - assertValidateParam(param, ["Required field is not provided"]) - + value = undefined + assertValidateParam(param, value, ["Required field is not provided"]) + // valid integer, but 0 is falsy in JS param = { required: true, - type: "integer", - value: 0 + type: "integer" } - assertValidateParam(param, []) - + value = 0 + assertValidateParam(param, value, []) + // valid integer param = { required: true, - type: "integer", - value: 10 + type: "integer" } - assertValidateParam(param, []) + value = 10 + assertValidateParam(param, value, []) }) - + it("validates optional integers", function() { // invalid integer, string instead of an integer param = { required: false, - type: "integer", - value: "test" + type: "integer" } - assertValidateParam(param, ["Value must be an integer"]) - + value = "test" + assertValidateParam(param, value, ["Value must be an integer"]) + // valid (empty) integer param = { required: false, - type: "integer", - value: undefined + type: "integer" } - assertValidateParam(param, []) - + value = undefined + assertValidateParam(param, value, []) + // integers param = { required: false, - type: "integer", - value: 10 + type: "integer" } - assertValidateParam(param, []) + value = 10 + 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({ @@ -925,7 +935,7 @@ describe("utils", function() { } } }) - + expect(getAcceptControllingResponse(responses)).toEqual(responses.get("200")) }) it("should skip 2xx responses without defined media types", () => { @@ -949,7 +959,7 @@ describe("utils", function() { } } }) - + expect(getAcceptControllingResponse(responses)).toEqual(responses.get("201")) }) it("should default to the `default` response if it has defined media types", () => { @@ -970,7 +980,7 @@ describe("utils", function() { } } }) - + expect(getAcceptControllingResponse(responses)).toEqual(responses.get("default")) }) it("should return null if there are no suitable controlling responses", () => { @@ -985,40 +995,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("") @@ -1027,39 +1037,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() { @@ -1077,7 +1087,7 @@ describe("utils", function() { expect(result).toEqual(Map([[ "minimum", "b"]])) }) }) - + describe("deeplyStripKey", function() { it("should filter out a specified key", function() { const input = { @@ -1094,7 +1104,7 @@ describe("utils", function() { } }) }) - + it("should filter out a specified key by predicate", function() { const input = { $$ref: "#/this/is/my/ref", @@ -1111,7 +1121,7 @@ describe("utils", function() { } }) }) - + it("should only call the predicate when the key matches", function() { const input = { $$ref: "#/this/is/my/ref", @@ -1121,7 +1131,7 @@ describe("utils", function() { } } let count = 0 - + const result = deeplyStripKey(input, "$$ref", () => { count++ return true @@ -1129,78 +1139,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("") @@ -1209,9 +1219,10 @@ describe("utils", function() { expect(sanitizeUrl({})).toEqual("") }) }) + describe("getSampleSchema", function() { const oriDate = Date - + before(function() { Date = function () { this.toISOString = function () { @@ -1219,7 +1230,7 @@ describe("utils", function() { } } }) - + after(function() { Date = oriDate }) @@ -1230,9 +1241,149 @@ describe("utils", function() { 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({ + name: "id", + 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", () => { + const param = fromJS({ + name: "id", + 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") + }) + }) }) diff --git a/test/e2e-cypress/static/documents/bugs/5129.yaml b/test/e2e-cypress/static/documents/bugs/5129.yaml new file mode 100644 index 00000000..0508ec8d --- /dev/null +++ b/test/e2e-cypress/static/documents/bugs/5129.yaml @@ -0,0 +1,26 @@ +openapi: "3.0.0" + +paths: + /aev: + get: + parameters: + - name: param + in: query + allowEmptyValue: true + schema: + type: string + responses: + 200: + description: ok + /aev/and/required: + get: + parameters: + - name: param + in: query + allowEmptyValue: true + required: true + schema: + type: string + responses: + 200: + description: ok \ No newline at end of file diff --git a/test/e2e-cypress/tests/bugs/5129.js b/test/e2e-cypress/tests/bugs/5129.js new file mode 100644 index 00000000..bd03bc59 --- /dev/null +++ b/test/e2e-cypress/tests/bugs/5129.js @@ -0,0 +1,121 @@ +describe("#5129: parameter required + allowEmptyValue interactions", () => { + describe("allowEmptyValue parameter", () => { + const opId = "#operations-default-get_aev" + it("should omit the parameter by default", () => { + cy + .visit("/?url=/documents/bugs/5129.yaml") + .get(opId) + .click() + .get(".btn.try-out__btn") + .click() + .get(".btn.execute") + .click() + .get(".request-url pre") + .should("have.text", "http://localhost:3230/aev") + }) + it("should include a value", () => { + cy + .visit("/?url=/documents/bugs/5129.yaml") + .get(opId) + .click() + .get(".btn.try-out__btn") + .click() + .get(`.parameters-col_description input[type=text]`) + .type("asdf") + .get(".btn.execute") + .click() + .get(".request-url pre") + .should("have.text", "http://localhost:3230/aev?param=asdf") + }) + it("should include an empty value when empty value box is checked", () => { + cy + .visit("/?url=/documents/bugs/5129.yaml") + .get(opId) + .click() + .get(".btn.try-out__btn") + .click() + .get(`.parameters-col_description input[type=checkbox]`) + .check() + .get(".btn.execute") + .click() + .get(".request-url pre") + .should("have.text", "http://localhost:3230/aev?param=") + }) + it("should include a value when empty value box is checked and then input is provided", () => { + cy + .visit("/?url=/documents/bugs/5129.yaml") + .get(opId) + .click() + .get(".btn.try-out__btn") + .click() + .get(`.parameters-col_description input[type=checkbox]`) + .check() + .get(`.parameters-col_description input[type=text]`) + .type("1234") + .get(".btn.execute") + .click() + .get(".request-url pre") + .should("have.text", "http://localhost:3230/aev?param=1234") + }) + }) + describe("allowEmptyValue + required parameter", () => { + const opId = "#operations-default-get_aev_and_required" + it("should refuse to execute by default", () => { + cy + .visit("/?url=/documents/bugs/5129.yaml") + .get(opId) + .click() + .get(".btn.try-out__btn") + .click() + .get(".btn.execute") + .click() + .wait(1000) + .get(".request-url pre") + .should("not.exist") + }) + it("should include a value", () => { + cy + .visit("/?url=/documents/bugs/5129.yaml") + .get(opId) + .click() + .get(".btn.try-out__btn") + .click() + .get(`.parameters-col_description input[type=text]`) + .type("asdf") + .get(".btn.execute") + .click() + .get(".request-url pre") + .should("have.text", "http://localhost:3230/aev/and/required?param=asdf") + }) + it("should include an empty value when empty value box is checked", () => { + cy + .visit("/?url=/documents/bugs/5129.yaml") + .get(opId) + .click() + .get(".btn.try-out__btn") + .click() + .get(`.parameters-col_description input[type=checkbox]`) + .check() + .get(".btn.execute") + .click() + .get(".request-url pre") + .should("have.text", "http://localhost:3230/aev/and/required?param=") + }) + it("should include a value when empty value box is checked and then input is provided", () => { + cy + .visit("/?url=/documents/bugs/5129.yaml") + .get(opId) + .click() + .get(".btn.try-out__btn") + .click() + .get(`.parameters-col_description input[type=checkbox]`) + .check() + .get(`.parameters-col_description input[type=text]`) + .type("1234") + .get(".btn.execute") + .click() + .get(".request-url pre") + .should("have.text", "http://localhost:3230/aev/and/required?param=1234") + }) + }) +}) \ No newline at end of file