From 7049a5960c2b96d4f6528f1dffcc134ba591262f Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Sun, 25 Jun 2017 09:16:35 -0600 Subject: [PATCH 1/8] #3135 - Add request duration to response details --- src/core/components/live-response.jsx | 18 +++++++++++++++++- src/core/plugins/spec/actions.js | 8 +++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/core/components/live-response.jsx b/src/core/components/live-response.jsx index 550208fa..7c6f1384 100644 --- a/src/core/components/live-response.jsx +++ b/src/core/components/live-response.jsx @@ -8,11 +8,23 @@ const Headers = ( { headers } )=>{
{headers}
) } - Headers.propTypes = { headers: PropTypes.array.isRequired } +const Duration = ( { duration } ) => { + return ( +
+
Request duration
+
{duration} ms
+
+ ) +} +Duration.propTypes = { + duration: PropTypes.number.isRequired +} + + export default class LiveResponse extends React.Component { static propTypes = { response: PropTypes.object.isRequired, @@ -27,6 +39,7 @@ export default class LiveResponse extends React.Component { const headers = response.get("headers").toJS() const notDocumented = response.get("notDocumented") const isError = response.get("error") + const duration = response.get("duration") const body = isError ? response.get("response").get("text") : response.get("text") @@ -80,6 +93,9 @@ export default class LiveResponse extends React.Component { { hasHeaders ? : null } + { + duration ? : null + } diff --git a/src/core/plugins/spec/actions.js b/src/core/plugins/spec/actions.js index d531630a..9424891e 100644 --- a/src/core/plugins/spec/actions.js +++ b/src/core/plugins/spec/actions.js @@ -207,8 +207,14 @@ export const executeRequest = (req) => ({fn, specActions, specSelectors}) => { specActions.setRequest(req.pathName, req.method, parsedRequest) + // track duration of request + const startTime = Date.now() + return fn.execute(req) - .then( res => specActions.setResponse(req.pathName, req.method, res)) + .then( res => { + res.duration = Date.now() - startTime + specActions.setResponse(req.pathName, req.method, res) + } ) .catch( err => specActions.setResponse(req.pathName, req.method, { error: true, err: serializeError(err) } ) ) } From ddb57046377cd5ba93baa86d357881f719e13ebc Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Sun, 25 Jun 2017 13:28:14 -0600 Subject: [PATCH 2/8] #3072 - Display response body and headers for requests that result in non-200 responses. Add test to ensure error and response are merged correctly --- src/core/components/live-response.jsx | 4 +-- src/core/plugins/spec/actions.js | 40 ++++++++++----------- src/core/plugins/spec/reducers.js | 9 +++-- test/core/plugins/spec-reducer.js | 52 +++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 25 deletions(-) diff --git a/src/core/components/live-response.jsx b/src/core/components/live-response.jsx index 550208fa..624360a0 100644 --- a/src/core/components/live-response.jsx +++ b/src/core/components/live-response.jsx @@ -27,9 +27,7 @@ export default class LiveResponse extends React.Component { const headers = response.get("headers").toJS() const notDocumented = response.get("notDocumented") const isError = response.get("error") - - const body = isError ? response.get("response").get("text") : response.get("text") - + const body = response.get("text") const headersKeys = Object.keys(headers) const contentType = headers["content-type"] diff --git a/src/core/plugins/spec/actions.js b/src/core/plugins/spec/actions.js index d531630a..8484ea82 100644 --- a/src/core/plugins/spec/actions.js +++ b/src/core/plugins/spec/actions.js @@ -92,28 +92,28 @@ export const resolveSpec = (json, url) => ({specActions, specSelectors, errActio let specStr = specSelectors.specStr() return resolve({fetch, spec: json, baseDoc: url, modelPropertyMacro, parameterMacro }) - .then( ({spec, errors}) => { - errActions.clear({ - type: "thrown" - }) + .then( ({spec, errors}) => { + errActions.clear({ + type: "thrown" + }) - if(errors.length > 0) { - let preparedErrors = errors - .map(err => { - console.error(err) - err.line = err.fullPath ? getLineNumberForPath(specStr, err.fullPath) : null - err.path = err.fullPath ? err.fullPath.join(".") : null - err.level = "error" - err.type = "thrown" - err.source = "resolver" - Object.defineProperty(err, "message", { enumerable: true, value: err.message }) - return err - }) - errActions.newThrownErrBatch(preparedErrors) - } + if(errors.length > 0) { + let preparedErrors = errors + .map(err => { + console.error(err) + err.line = err.fullPath ? getLineNumberForPath(specStr, err.fullPath) : null + err.path = err.fullPath ? err.fullPath.join(".") : null + err.level = "error" + err.type = "thrown" + err.source = "resolver" + Object.defineProperty(err, "message", { enumerable: true, value: err.message }) + return err + }) + errActions.newThrownErrBatch(preparedErrors) + } - return specActions.updateResolved(spec) - }) + return specActions.updateResolved(spec) + }) } export const formatIntoYaml = () => ({specActions, specSelectors}) => { diff --git a/src/core/plugins/spec/reducers.js b/src/core/plugins/spec/reducers.js index 6bc51d53..d9670d7b 100644 --- a/src/core/plugins/spec/reducers.js +++ b/src/core/plugins/spec/reducers.js @@ -75,7 +75,12 @@ export default { [SET_RESPONSE]: (state, { payload: { res, path, method } } ) =>{ let result if ( res.error ) { - result = Object.assign({error: true}, res.err) + result = Object.assign({ + error: true, + name: res.err.name, + message: res.err.message, + statusCode: res.err.statusCode + }, res.err.response) } else { result = res } @@ -86,7 +91,7 @@ export default { let newState = state.setIn( [ "responses", path, method ], fromJSOrdered(result) ) // ImmutableJS messes up Blob. Needs to reset its value. - if (res.data instanceof win.Blob) { + if (win.Blob && res.data instanceof win.Blob) { newState = newState.setIn( [ "responses", path, method, "text" ], res.data) } return newState diff --git a/test/core/plugins/spec-reducer.js b/test/core/plugins/spec-reducer.js index 32113543..09e73b7c 100644 --- a/test/core/plugins/spec-reducer.js +++ b/test/core/plugins/spec-reducer.js @@ -69,4 +69,56 @@ describe("spec plugin - reducer", function(){ expect(result.toJS()).toEqual(state.toJS()) }) }) + + describe("set response value", function() { + it("should combine the response and error objects", () => { + const setResponse = reducer["spec_set_response"] + + const path = "/pet/post" + const method = "POST" + + const state = fromJS({}) + const result = setResponse(state, { + payload: { + path: path, + method: method, + res: { + error: true, + err: { + message: "Not Found", + name: "Error", + response: { + data: "response data", + headers: { + key: "value" + }, + ok: false, + status: 404, + statusText: "Not Found" + }, + status: 404, + statusCode: 404 + } + } + } + }) + + let expectedResult = { + error: true, + message: "Not Found", + name: "Error", + data: "response data", + headers: { + key: "value" + }, + ok: false, + status: 404, + statusCode: 404, + statusText: "Not Found" + } + + const response = result.getIn(["responses", path, method]).toJS() + expect(response).toEqual(expectedResult) + }) + }) }) From 789534c587a2d9100fc63bb9f0d10f7f2dccc9c6 Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Sun, 25 Jun 2017 15:51:07 -0600 Subject: [PATCH 3/8] Bug fix for #3136 - Update state when scheme options change and previously selected option is no longer available --- src/core/components/operation.jsx | 4 +++- src/core/components/schemes.jsx | 12 ++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/core/components/operation.jsx b/src/core/components/operation.jsx index 9b5133c7..f968f68c 100644 --- a/src/core/components/operation.jsx +++ b/src/core/components/operation.jsx @@ -131,6 +131,7 @@ export default class Operation extends React.Component { let schemes = operation.get("schemes") let parameters = getList(operation, ["parameters"]) let operationId = operation.get("__originalOperationId") + let operationScheme = specSelectors.operationScheme(path, method) const Responses = getComponent("responses") const Parameters = getComponent( "parameters" ) @@ -216,7 +217,8 @@ export default class Operation extends React.Component { + specActions={ specActions } + operationScheme={ operationScheme } /> : null } diff --git a/src/core/components/schemes.jsx b/src/core/components/schemes.jsx index 21d954f0..0b397d03 100644 --- a/src/core/components/schemes.jsx +++ b/src/core/components/schemes.jsx @@ -6,7 +6,8 @@ export default class Schemes extends React.Component { specActions: PropTypes.object.isRequired, schemes: PropTypes.object.isRequired, path: PropTypes.string, - method: PropTypes.string + method: PropTypes.string, + operationScheme: PropTypes.string } componentWillMount() { @@ -16,11 +17,18 @@ export default class Schemes extends React.Component { this.setScheme(schemes.first()) } + componentWillReceiveProps(nextProps) { + if ( this.props.operationScheme && !nextProps.schemes.has(this.props.operationScheme) ) { + //fire 'change' event if our selected scheme is no longer an option + this.setScheme(nextProps.schemes.first()) + } + } + onChange =( e ) => { this.setScheme( e.target.value ) } - setScheme =( value ) => { + setScheme = ( value ) => { let { path, method, specActions } = this.props specActions.setScheme( value, path, method ) From b2d52e2629931d8f10f52402da9a60081713397b Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Sun, 25 Jun 2017 16:10:14 -0600 Subject: [PATCH 4/8] Remove isRequired from ResponseBody's `contentType` property as responses without a Content-Type header will not have a value to pass --- src/core/components/response-body.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/components/response-body.jsx b/src/core/components/response-body.jsx index 45f8e90c..e048c33b 100644 --- a/src/core/components/response-body.jsx +++ b/src/core/components/response-body.jsx @@ -6,7 +6,7 @@ export default class ResponseBody extends React.Component { static propTypes = { content: PropTypes.any.isRequired, - contentType: PropTypes.string.isRequired, + contentType: PropTypes.string, getComponent: PropTypes.func.isRequired, headers: PropTypes.object, url: PropTypes.string From cf63f9b9dd0715b2df2f228f5e839d5344eee346 Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Sun, 25 Jun 2017 22:31:14 -0600 Subject: [PATCH 5/8] Fix for #2947 - Display property names for non-object models --- src/core/components/model.jsx | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/core/components/model.jsx b/src/core/components/model.jsx index a043c325..b5f620c5 100644 --- a/src/core/components/model.jsx +++ b/src/core/components/model.jsx @@ -117,12 +117,13 @@ class ObjectModel extends Component { class Primitive extends Component { static propTypes = { schema: PropTypes.object.isRequired, + name: PropTypes.string, getComponent: PropTypes.func.isRequired, required: PropTypes.bool } render(){ - let { schema, getComponent, required } = this.props + let { schema, getComponent, name, required } = this.props if(!schema || !schema.get) { // don't render if schema isn't correctly formed @@ -133,12 +134,18 @@ class Primitive extends Component { let format = schema.get("format") let xml = schema.get("xml") let enumArray = schema.get("enum") + let title = schema.get("title") || name let description = schema.get("description") let properties = schema.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 ) let style = required ? { fontWeight: "bold" } : {} const Markdown = getComponent("Markdown") - return + return + { + title && + { title } + + } { type } { required && *} { format && (${format})} { @@ -176,17 +183,20 @@ class ArrayModel extends Component { } render(){ - let { required, schema, depth, expandDepth } = this.props + let { required, schema, depth, name, expandDepth } = this.props let items = schema.get("items") + let title = schema.get("title") || name let properties = schema.filter( ( v, key) => ["type", "items", "$$ref"].indexOf(key) === -1 ) - + return - - { schema.get("title") } - + { + title && + { title } + + } expandDepth } collapsedContent="[...]"> [ - + ] { properties.size ? @@ -249,13 +259,13 @@ class Model extends Component { name={ name || modelName } isRef={ isRef!== undefined ? isRef : !!$$ref }/> case "array": - return + return case "string": case "number": case "integer": case "boolean": default: - return + return } } } From 0c6f5e8d329ff0ac4ab77661709c88c448b8a652 Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Mon, 26 Jun 2017 21:13:17 -0600 Subject: [PATCH 6/8] Add displayRequestDuration configuration option. --- README.md | 1 + src/core/components/live-response.jsx | 7 ++++--- src/core/components/operation.jsx | 5 ++++- src/core/components/operations.jsx | 3 ++- src/core/components/responses.jsx | 9 ++++++--- src/core/index.js | 3 ++- 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index eff32bc7..538b6e0b 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ parameterMacro | MUST be a function. Function to set default value to parameters modelPropertyMacro | MUST be a function. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable docExpansion | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). The default is 'list'. displayOperationId | Controls the display of operationId in operations list. The default is `false`. +displayRequestDuration | Controls the display of the request duration (in milliseconds) for `Try it out` requests. The default is `false`. ### Plugins diff --git a/src/core/components/live-response.jsx b/src/core/components/live-response.jsx index 7c6f1384..ab039043 100644 --- a/src/core/components/live-response.jsx +++ b/src/core/components/live-response.jsx @@ -28,11 +28,12 @@ Duration.propTypes = { export default class LiveResponse extends React.Component { static propTypes = { response: PropTypes.object.isRequired, - getComponent: PropTypes.func.isRequired + getComponent: PropTypes.func.isRequired, + displayRequestDuration: PropTypes.bool.isRequired } render() { - const { request, response, getComponent } = this.props + const { request, response, getComponent, displayRequestDuration } = this.props const status = response.get("status") const url = response.get("url") @@ -94,7 +95,7 @@ export default class LiveResponse extends React.Component { hasHeaders ? : null } { - duration ? : null + displayRequestDuration && duration ? : null } diff --git a/src/core/components/operation.jsx b/src/core/components/operation.jsx index 31f52cda..704b729e 100644 --- a/src/core/components/operation.jsx +++ b/src/core/components/operation.jsx @@ -17,6 +17,7 @@ export default class Operation extends PureComponent { allowTryItOut: PropTypes.bool, displayOperationId: PropTypes.bool, + displayRequestDuration: PropTypes.bool, response: PropTypes.object, request: PropTypes.object, @@ -37,6 +38,7 @@ export default class Operation extends PureComponent { response: null, allowTryItOut: true, displayOperationId: false, + displayRequestDuration: false } constructor(props, context) { @@ -107,7 +109,7 @@ export default class Operation extends PureComponent { request, allowTryItOut, displayOperationId, - + displayRequestDuration, fn, getComponent, specActions, @@ -250,6 +252,7 @@ export default class Operation extends PureComponent { produces={ produces } producesValue={ operation.get("produces_value") } pathMethod={ [path, method] } + displayRequestDuration={ displayRequestDuration } fn={fn} /> } diff --git a/src/core/components/operations.jsx b/src/core/components/operations.jsx index 16c8a86e..7eb7b4e7 100644 --- a/src/core/components/operations.jsx +++ b/src/core/components/operations.jsx @@ -32,7 +32,7 @@ export default class Operations extends React.Component { const Collapse = getComponent("Collapse") let showSummary = layoutSelectors.showSummary() - let { docExpansion, displayOperationId } = getConfigs() + let { docExpansion, displayOperationId, displayRequestDuration } = getConfigs() return (
@@ -87,6 +87,7 @@ export default class Operations extends React.Component { allowTryItOut={allowTryItOut} displayOperationId={displayOperationId} + displayRequestDuration={displayRequestDuration} specActions={ specActions } specSelectors={ specSelectors } diff --git a/src/core/components/responses.jsx b/src/core/components/responses.jsx index 8bbe4f0f..1da9fb69 100644 --- a/src/core/components/responses.jsx +++ b/src/core/components/responses.jsx @@ -14,19 +14,21 @@ export default class Responses extends React.Component { specSelectors: PropTypes.object.isRequired, specActions: PropTypes.object.isRequired, pathMethod: PropTypes.array.isRequired, + displayRequestDuration: PropTypes.bool.isRequired, fn: PropTypes.object.isRequired } static defaultProps = { request: null, tryItOutResponse: null, - produces: fromJS(["application/json"]) + produces: fromJS(["application/json"]), + displayRequestDuration: false } onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val) render() { - let { responses, request, tryItOutResponse, getComponent, specSelectors, fn, producesValue } = this.props + let { responses, request, tryItOutResponse, getComponent, specSelectors, fn, producesValue, displayRequestDuration } = this.props let defaultCode = defaultStatusCode( responses ) const ContentType = getComponent( "contentType" ) @@ -53,7 +55,8 @@ export default class Responses extends React.Component { :
+ getComponent={ getComponent } + displayRequestDuration={ displayRequestDuration } />

Responses

diff --git a/src/core/index.js b/src/core/index.js index 461a67cd..228ff838 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -8,7 +8,7 @@ import { parseSeach, filterConfigs } from "core/utils" const CONFIGS = [ "url", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion", "apisSorter", "operationsSorter", "supportedSubmitMethods", "dom_id", "defaultModelRendering", "oauth2RedirectUrl", - "showRequestHeaders", "custom", "modelPropertyMacro", "parameterMacro", "displayOperationId" ] + "showRequestHeaders", "custom", "modelPropertyMacro", "parameterMacro", "displayOperationId" , "displayRequestDuration"] // eslint-disable-next-line no-undef const { GIT_DIRTY, GIT_COMMIT, PACKAGE_VERSION } = buildInfo @@ -29,6 +29,7 @@ module.exports = function SwaggerUI(opts) { configs: {}, custom: {}, displayOperationId: false, + displayRequestDuration: false, // Initial set of plugins ( TODO rename this, or refactor - we don't need presets _and_ plugins. Its just there for performance. // Instead, we can compile the first plugin ( it can be a collection of plugins ), then batch the rest. From 5119c7a5dba4df320acf7552c2dfc5505e1f5550 Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Tue, 27 Jun 2017 20:01:09 -0600 Subject: [PATCH 7/8] Fixes #3299 - export validateNumber and validateInteger for easy reuse and testing. Broke validateParam required check onto multiple lines. Added tests for validateNumber, validateInteger, and validateParam --- src/core/utils.js | 15 ++-- test/core/utils.js | 176 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 182 insertions(+), 9 deletions(-) diff --git a/src/core/utils.js b/src/core/utils.js index 1412c344..bca20876 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -450,15 +450,15 @@ export const propChecker = (props, nextProps, objectList=[], ignoreList=[]) => { || objectList.some( objectPropName => !eq(props[objectPropName], nextProps[objectPropName]))) } -const validateNumber = ( val ) => { - if ( !/^-?\d+(.?\d+)?$/.test(val)) { +export const validateNumber = ( val ) => { + if ( !/^-?\d+(\.?\d+)?$/.test(val)) { return "Value must be a number" } } -const validateInteger = ( val ) => { +export const validateInteger = ( val ) => { if ( !/^-?\d+$/.test(val)) { - return "Value must be integer" + return "Value must be an integer" } } @@ -469,13 +469,14 @@ export const validateParam = (param, isXml) => { let required = param.get("required") let type = param.get("type") - if ( required && (!value || (type==="array" && Array.isArray(value) && !value.length ))) { + let stringCheck = type === "string" && !value + let arrayCheck = type === "array" && Array.isArray(value) && !value.length + let listCheck = type === "array" && Im.List.isList(value) && !value.count() + if ( required && (stringCheck || arrayCheck || listCheck) ) { errors.push("Required field is not provided") return errors } - if ( !value ) return errors - if ( type === "number" ) { let err = validateNumber(value) if (!err) return errors diff --git a/test/core/utils.js b/test/core/utils.js index 636c5f00..baf7dbf7 100644 --- a/test/core/utils.js +++ b/test/core/utils.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ import expect from "expect" import { fromJS } from "immutable" -import { mapToList } from "core/utils" +import { mapToList, validateNumber, validateInteger, validateParam } from "core/utils" describe("utils", function(){ @@ -67,9 +67,181 @@ describe("utils", function(){ // Then expect(aList.toJS()).toEqual([]) - }) }) + 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() + expect(validateNumber(20)).toBeFalsy() + expect(validateNumber(5000000)).toBeFalsy() + expect(validateNumber("1")).toBeFalsy() + expect(validateNumber("2")).toBeFalsy() + expect(validateNumber(-1)).toBeFalsy() + 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) + expect(validateNumber({})).toEqual(errorMessage) + expect(validateNumber([])).toEqual(errorMessage) + expect(validateNumber(true)).toEqual(errorMessage) + 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() + expect(validateInteger(20)).toBeFalsy() + expect(validateInteger(5000000)).toBeFalsy() + expect(validateInteger("1")).toBeFalsy() + expect(validateInteger("2")).toBeFalsy() + expect(validateInteger(-1)).toBeFalsy() + 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) + expect(validateInteger({})).toEqual(errorMessage) + expect(validateInteger([])).toEqual(errorMessage) + expect(validateInteger(true)).toEqual(errorMessage) + expect(validateInteger(false)).toEqual(errorMessage) + }) + }) + + describe("validateParam", function() { + let param = null + let result = null + + it("validates required strings", function() { + param = fromJS({ + required: true, + type: "string", + value: "" + }) + result = validateParam( param, false ) + expect( result ).toEqual( ["Required field is not provided"] ) + }) + + it("validates required arrays", function() { + param = fromJS({ + required: true, + type: "array", + value: [] + }) + result = validateParam( param, false ) + expect( result ).toEqual( ["Required field is not provided"] ) + + param = fromJS({ + required: true, + type: "array", + value: [] + }) + result = validateParam( param, false ) + expect( result ).toEqual( ["Required field is not provided"] ) + }) + + it("validates numbers", function() { + param = fromJS({ + required: false, + type: "number", + value: "test" + }) + result = validateParam( param, false ) + expect( result ).toEqual( ["Value must be a number"] ) + }) + + it("validates integers", function() { + param = fromJS({ + required: false, + type: "integer", + value: "test" + }) + result = validateParam( param, false ) + expect( result ).toEqual( ["Value must be an integer"] ) + }) + + it("validates arrays", function() { + // empty array + param = fromJS({ + required: false, + type: "array", + value: [] + }) + result = validateParam( param, false ) + expect( result ).toEqual( [] ) + + // numbers + param = fromJS({ + required: false, + type: "array", + value: ["number"], + items: { + type: "number" + } + }) + result = validateParam( param, false ) + expect( result ).toEqual( [{index: 0, error: "Value must be a number"}] ) + + // integers + param = fromJS({ + required: false, + type: "array", + value: ["not", "numbers"], + items: { + type: "integer" + } + }) + result = validateParam( param, false ) + expect( result ).toEqual( [{index: 0, error: "Value must be an integer"}, {index: 1, error: "Value must be an integer"}] ) + }) + }) }) From 3484b83d1f02e5a542b4fa6cfa95c54de226ae1e Mon Sep 17 00:00:00 2001 From: Kyle Shockey Date: Thu, 29 Jun 2017 13:22:58 -0700 Subject: [PATCH 8/8] trigger setScheme when a new set of schemes come in --- src/core/components/schemes.jsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/components/schemes.jsx b/src/core/components/schemes.jsx index 21d954f0..f8adeb28 100644 --- a/src/core/components/schemes.jsx +++ b/src/core/components/schemes.jsx @@ -20,6 +20,12 @@ export default class Schemes extends React.Component { this.setScheme( e.target.value ) } + componentWillReceiveProps(nextProps) { + if(nextProps.schemes !== this.props.schemes) { + this.setScheme(nextProps.schemes.first()) + } + } + setScheme =( value ) => { let { path, method, specActions } = this.props