Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22ccdfff10 | ||
|
|
efc1194e4a | ||
|
|
bb84da4e1d | ||
|
|
e531c40356 | ||
|
|
f29202fed8 | ||
|
|
c28213d457 | ||
|
|
286c0e0f72 | ||
|
|
7af7121e64 | ||
|
|
ec4890299c | ||
|
|
31ccaf3ea3 | ||
|
|
253573200a | ||
|
|
024b7db81e | ||
|
|
5b1dbec9b0 | ||
|
|
64f99356a5 | ||
|
|
b3e80cc500 | ||
|
|
53ea2003c1 | ||
|
|
a037c7205b | ||
|
|
63d5f164c6 | ||
|
|
2e816e0440 | ||
|
|
0818882a90 | ||
|
|
0751022b77 | ||
|
|
fc070408f6 | ||
|
|
a47da98b54 | ||
|
|
12a9fbc0ca | ||
|
|
bd41b736a8 | ||
|
|
a8f204181e | ||
|
|
67d472acc7 | ||
|
|
aebcfe8e5d | ||
|
|
71c0742735 | ||
|
|
8e4f94b33f | ||
|
|
a9ea10da2c | ||
|
|
8f3535b5ff | ||
|
|
0ca0b8120f | ||
|
|
603c436427 | ||
|
|
c601f9b0ca | ||
|
|
c742df9573 | ||
|
|
ecfda531de | ||
|
|
8c4b511227 |
@@ -0,0 +1,7 @@
|
||||
/.git
|
||||
/.github
|
||||
/dev-helpers
|
||||
/docs
|
||||
/src
|
||||
/swagger-ui-dist-package
|
||||
/test
|
||||
@@ -18,7 +18,7 @@ or anything that violates the specifications.
|
||||
<!--- Provide a general summary of the issue in the title above -->
|
||||
|
||||
<!---
|
||||
If you aren't sure what Swagger-UI version, see this guide: https://github.com/swagger-api/swagger-ui/blob/master/docs/version-detection.md
|
||||
If you aren't sure what Swagger-UI version, see this guide: https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/version-detection.md
|
||||
--->
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
!package.json
|
||||
!dist/swagger-ui.js
|
||||
!dist/swagger-ui.js.map
|
||||
!dist/swagger-ui-standalone-preset.js
|
||||
!dist/swagger-ui-standalone-preset.js.map
|
||||
!dist/swagger-ui.css
|
||||
!dist/swagger-ui.css.map
|
||||
!dist/oauth2-redirect.html
|
||||
|
||||
@@ -52,3 +52,7 @@ _Adapted from [How to Write a Git Commit Message](https://chris.beams.io/posts/g
|
||||
- **Don't** include any changes to files in the `dist/` directory - we update those files only during releases.
|
||||
- **Don't** mention maintainers in your original PR body - we probably would've seen it anyway, so it just increases the noise in our inboxes. Do feel free to ping maintainers if a week has passed and you've heard nothing from us.
|
||||
- **Don't** open PRs for custom functionality that only serves a small subset of our users - custom functionality should be implemented outside of our codebase, via a plugin.
|
||||
|
||||
#### Merging pull requests
|
||||
- **Do** use GitHub's `Squash and merge` strategy.
|
||||
- **Do** follow the [Conventional Commits](https://conventionalcommits.org) standard format for your squash commit.
|
||||
|
||||
@@ -5,6 +5,7 @@ MAINTAINER fehguy
|
||||
ENV VERSION "v2.2.10"
|
||||
ENV FOLDER "swagger-ui-2.2.10"
|
||||
ENV API_URL "http://petstore.swagger.io/v2/swagger.json"
|
||||
ENV API_URLS ""
|
||||
ENV API_KEY "**None**"
|
||||
ENV OAUTH_CLIENT_ID "**None**"
|
||||
ENV OAUTH_CLIENT_SECRET "**None**"
|
||||
@@ -12,7 +13,7 @@ ENV OAUTH_REALM "**None**"
|
||||
ENV OAUTH_APP_NAME "**None**"
|
||||
ENV OAUTH_ADDITIONAL_PARAMS "**None**"
|
||||
ENV SWAGGER_JSON "/app/swagger.json"
|
||||
ENV PORT 80
|
||||
ENV PORT 8080
|
||||
ENV BASE_URL ""
|
||||
|
||||
RUN apk add --update nginx
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright 2017 SmartBear Software
|
||||
Copyright 2018 SmartBear Software
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -22,7 +22,7 @@ The OpenAPI Specification has undergone 5 revisions since initial creation in 20
|
||||
|
||||
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
|
||||
------------------ | ------------ | -------------------------- | -----
|
||||
3.8.0 | 2017-12-15 | 2.0, 3.0 | [tag v3.8.1](https://github.com/swagger-api/swagger-ui/tree/v3.8.1)
|
||||
3.9.2 | 2018-01-19 | 2.0, 3.0 | [tag v3.9.2](https://github.com/swagger-api/swagger-ui/tree/v3.9.2)
|
||||
3.0.21 | 2017-07-26 | 2.0 | [tag v3.0.21](https://github.com/swagger-api/swagger-ui/tree/v3.0.21)
|
||||
2.2.10 | 2017-01-04 | 1.1, 1.2, 2.0 | [tag v2.2.10](https://github.com/swagger-api/swagger-ui/tree/v2.2.10)
|
||||
2.1.5 | 2016-07-20 | 1.1, 1.2, 2.0 | [tag v2.1.5](https://github.com/swagger-api/swagger-ui/tree/v2.1.5)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
set -e
|
||||
|
||||
BASE_URL=${BASE_URL:-/}
|
||||
NGINX_ROOT=/usr/share/nginx/html
|
||||
INDEX_FILE=$NGINX_ROOT/index.html
|
||||
|
||||
@@ -22,13 +23,7 @@ replace_or_delete_in_index () {
|
||||
}
|
||||
|
||||
if [ "${BASE_URL}" ]; then
|
||||
NGINX_WITH_BASE_URL="${NGINX_ROOT}${BASE_URL}"
|
||||
|
||||
mkdir -p ${NGINX_WITH_BASE_URL}
|
||||
mv ${NGINX_ROOT}/*.* ${NGINX_WITH_BASE_URL}/
|
||||
|
||||
INDEX_FILE=$NGINX_WITH_BASE_URL/index.html
|
||||
NGINX_ROOT=$NGINX_WITH_BASE_URL
|
||||
sed -i "s|location .* {|location $BASE_URL {|g" /etc/nginx/nginx.conf
|
||||
fi
|
||||
|
||||
replace_in_index myApiKeyXXXX123456789 $API_KEY
|
||||
@@ -41,7 +36,7 @@ if [ "$OAUTH_ADDITIONAL_PARAMS" != "**None**" ]; then
|
||||
fi
|
||||
|
||||
if [[ -f $SWAGGER_JSON ]]; then
|
||||
cp $SWAGGER_JSON $NGINX_ROOT
|
||||
cp -s $SWAGGER_JSON $NGINX_ROOT
|
||||
REL_PATH="./$(basename $SWAGGER_JSON)"
|
||||
sed -i "s|http://petstore.swagger.io/v2/swagger.json|$REL_PATH|g" $INDEX_FILE
|
||||
sed -i "s|http://example.com/api|$REL_PATH|g" $INDEX_FILE
|
||||
@@ -58,4 +53,14 @@ if [[ -n "$VALIDATOR_URL" ]]; then
|
||||
unset TMP_VU
|
||||
fi
|
||||
|
||||
# replace `url` with `urls` option if API_URLS is set
|
||||
if [[ -n "$API_URLS" ]]; then
|
||||
sed -i "s|url: .*,|urls: $API_URLS,|g" $INDEX_FILE
|
||||
fi
|
||||
|
||||
# replace the PORT that nginx listens on if PORT is supplied
|
||||
if [[ -n "${PORT}" ]]; then
|
||||
sed -i "s|8080|${PORT}|g" /etc/nginx/nginx.conf
|
||||
fi
|
||||
|
||||
exec nginx -g 'daemon off;'
|
||||
|
||||
@@ -334,7 +334,7 @@ const MyWrapComponentPlugin = function(system) {
|
||||
if(props.number > 10) {
|
||||
return <div>
|
||||
<h3>Warning! Big number ahead.</h3>
|
||||
<OriginalComponent {...props} />
|
||||
<Original {...props} />
|
||||
</div>
|
||||
} else {
|
||||
return <Original {...props} />
|
||||
@@ -353,7 +353,7 @@ const MyWrapComponentPlugin = function(system) {
|
||||
if(props.number > 10) {
|
||||
return <div>
|
||||
<h3>Warning! Big number ahead.</h3>
|
||||
<OriginalComponent {...props} />
|
||||
<Original {...props} />
|
||||
</div>
|
||||
} else {
|
||||
return <Original {...props} />
|
||||
|
||||
|
Before Width: | Height: | Size: 390 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 297 KiB After Width: | Height: | Size: 126 KiB |
@@ -27,7 +27,7 @@ The module's contents mirrors the `dist` folder you see in the Git repository. T
|
||||
|
||||
```javascript
|
||||
const express = require('express')
|
||||
const pathToSwaggerUi = require('swagger-ui').absolutePath()
|
||||
const pathToSwaggerUi = require('swagger-ui-dist').absolutePath()
|
||||
|
||||
const app = express()
|
||||
|
||||
@@ -37,7 +37,7 @@ app.listen(3000)
|
||||
```
|
||||
|
||||
The module also exports `SwaggerUIBundle` and `SwaggerUIStandalonePreset`, so
|
||||
if you're in a JavaScript project that can't handle a tranditional npm module,
|
||||
if you're in a JavaScript project that can't handle a traditional npm module,
|
||||
you could do something like this:
|
||||
|
||||
```js
|
||||
|
||||
@@ -15,10 +15,11 @@ http {
|
||||
server {
|
||||
listen 8080;
|
||||
server_name localhost;
|
||||
index index.html index.htm;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
alias /usr/share/nginx/html/;
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "swagger-ui",
|
||||
"version": "3.8.1",
|
||||
"version": "3.9.2",
|
||||
"main": "dist/swagger-ui.js",
|
||||
"repository": "git@github.com:swagger-api/swagger-ui.git",
|
||||
"contributors": [
|
||||
@@ -82,7 +82,7 @@
|
||||
"scroll-to-element": "^2.0.0",
|
||||
"serialize-error": "2.0.0",
|
||||
"shallowequal": "0.2.2",
|
||||
"swagger-client": "^3.4.4",
|
||||
"swagger-client": "^3.4.6",
|
||||
"url-parse": "^1.1.8",
|
||||
"whatwg-fetch": "0.11.1",
|
||||
"worker-loader": "^0.7.1",
|
||||
|
||||
@@ -57,6 +57,9 @@ export class Col extends React.Component {
|
||||
let classesAr = []
|
||||
|
||||
for (let device in DEVICES) {
|
||||
if (!DEVICES.hasOwnProperty(device)) {
|
||||
continue
|
||||
}
|
||||
let deviceClass = DEVICES[device]
|
||||
if(device in this.props) {
|
||||
let val = this.props[device]
|
||||
|
||||
@@ -8,13 +8,14 @@ export default class ModelCollapse extends Component {
|
||||
children: PropTypes.any,
|
||||
title: PropTypes.element,
|
||||
modelName: PropTypes.string,
|
||||
onToggle: PropTypes.func.isRequired
|
||||
onToggle: PropTypes.func
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
collapsedContent: "{...}",
|
||||
expanded: false,
|
||||
title: null
|
||||
title: null,
|
||||
onToggle: () => {}
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
|
||||
@@ -12,7 +12,7 @@ export default class ObjectModel extends Component {
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
expanded: PropTypes.bool,
|
||||
onToggle: PropTypes.func.isRequired,
|
||||
onToggle: PropTypes.func,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
isRef: PropTypes.bool,
|
||||
|
||||
@@ -248,7 +248,7 @@ export default class Operation extends PureComponent {
|
||||
oas3Actions={oas3Actions}
|
||||
specActions={ specActions }
|
||||
produces={ produces }
|
||||
producesValue={ operation.get("produces_value") }
|
||||
producesValue={ specSelectors.currentProducesFor([path, method]) }
|
||||
specPath={specPath.push("responses")}
|
||||
path={ path }
|
||||
method={ method }
|
||||
|
||||
@@ -108,6 +108,34 @@ export default class ParameterRow extends Component {
|
||||
let value = parameter ? parameter.get("value") : ""
|
||||
let extensions = getExtensions(param)
|
||||
|
||||
|
||||
let paramItems // undefined
|
||||
let paramItemsEnum // undefined
|
||||
let isDisplayParamItemsEnum = false
|
||||
if ( param !== undefined ) {
|
||||
paramItems = param.get("items")
|
||||
}
|
||||
if ( paramItems !== undefined ) {
|
||||
paramItemsEnum = param.get("items").get("enum")
|
||||
}
|
||||
if ( paramItemsEnum !== undefined ) {
|
||||
if (paramItemsEnum.size > 0) {
|
||||
isDisplayParamItemsEnum = true
|
||||
}
|
||||
}
|
||||
|
||||
// Default and Example Value for readonly doc
|
||||
let paramDefaultValue // undefined
|
||||
let paramExample // undefined
|
||||
if ( param !== undefined ) {
|
||||
paramDefaultValue = param.get("default")
|
||||
paramExample = param.get("example")
|
||||
}
|
||||
|
||||
if (isDisplayParamItemsEnum) { // if we have an array, default value is in "items"
|
||||
paramDefaultValue = paramItems.get("default")
|
||||
}
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td className="col parameters-col_name">
|
||||
@@ -125,6 +153,25 @@ export default class ParameterRow extends Component {
|
||||
|
||||
<td className="col parameters-col_description">
|
||||
<Markdown source={ param.get("description") }/>
|
||||
|
||||
{ (bodyParam || !isExecute) && isDisplayParamItemsEnum ?
|
||||
<Markdown source={
|
||||
"<i>Available values</i> : " + paramItemsEnum.map(function(item) {
|
||||
return item
|
||||
}).toArray()}/>
|
||||
: null
|
||||
}
|
||||
|
||||
{ (bodyParam || !isExecute) && paramDefaultValue !== undefined ?
|
||||
<Markdown source={"<i>Default value</i> : " + paramDefaultValue}/>
|
||||
: null
|
||||
}
|
||||
|
||||
{ (bodyParam || !isExecute) && paramExample !== undefined ?
|
||||
<Markdown source={"<i>Example</i> : " + paramExample}/>
|
||||
: null
|
||||
}
|
||||
|
||||
{(isFormData && !isFormDataSupported) && <div>Error: your browser does not support FormData</div>}
|
||||
|
||||
{ bodyParam || !isExecute ? null
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import formatXml from "xml-but-prettier"
|
||||
import lowerCase from "lodash/lowerCase"
|
||||
import { extractFileNameFromContentDispositionHeader } from "core/utils"
|
||||
|
||||
export default class ResponseBody extends React.Component {
|
||||
|
||||
@@ -19,8 +20,41 @@ export default class ResponseBody extends React.Component {
|
||||
let body, bodyEl
|
||||
url = url || ""
|
||||
|
||||
// JSON
|
||||
if (/json/i.test(contentType)) {
|
||||
if (
|
||||
/^application\/octet-stream/i.test(contentType) ||
|
||||
(headers["Content-Disposition"] && (/attachment/i).test(headers["Content-Disposition"])) ||
|
||||
(headers["content-disposition"] && (/attachment/i).test(headers["content-disposition"])) ||
|
||||
(headers["Content-Description"] && (/File Transfer/i).test(headers["Content-Description"])) ||
|
||||
(headers["content-description"] && (/File Transfer/i).test(headers["content-description"]))) {
|
||||
// Download
|
||||
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
|
||||
|
||||
if (!isSafari && "Blob" in window) {
|
||||
let type = contentType || "text/html"
|
||||
let blob = (content instanceof Blob) ? content : new Blob([content], {type: type})
|
||||
let href = window.URL.createObjectURL(blob)
|
||||
let fileName = url.substr(url.lastIndexOf("/") + 1)
|
||||
let download = [type, fileName, href].join(":")
|
||||
|
||||
// Use filename from response header,
|
||||
// First check if filename is quoted (e.g. contains space), if no, fallback to not quoted check
|
||||
let disposition = headers["content-disposition"] || headers["Content-Disposition"]
|
||||
if (typeof disposition !== "undefined") {
|
||||
let responseFilename = extractFileNameFromContentDispositionHeader(disposition)
|
||||
if (responseFilename !== null) {
|
||||
download = responseFilename
|
||||
}
|
||||
}
|
||||
|
||||
bodyEl = <div><a href={ href } download={ download }>{ "Download file" }</a></div>
|
||||
} else {
|
||||
bodyEl = <pre>Download headers detected but your browser does not support downloading binary via XHR (Blob).</pre>
|
||||
}
|
||||
|
||||
// Anything else (CORS)
|
||||
} else if (/json/i.test(contentType)) {
|
||||
// JSON
|
||||
try {
|
||||
body = JSON.stringify(JSON.parse(content), null, " ")
|
||||
} catch (error) {
|
||||
@@ -52,39 +86,6 @@ export default class ResponseBody extends React.Component {
|
||||
// Audio
|
||||
} else if (/^audio\//i.test(contentType)) {
|
||||
bodyEl = <pre><audio controls><source src={ url } type={ contentType } /></audio></pre>
|
||||
|
||||
// Download
|
||||
} else if (
|
||||
/^application\/octet-stream/i.test(contentType) ||
|
||||
(headers["Content-Disposition"] && (/attachment/i).test(headers["Content-Disposition"])) ||
|
||||
(headers["content-disposition"] && (/attachment/i).test(headers["content-disposition"])) ||
|
||||
(headers["Content-Description"] && (/File Transfer/i).test(headers["Content-Description"])) ||
|
||||
(headers["content-description"] && (/File Transfer/i).test(headers["content-description"]))) {
|
||||
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
|
||||
|
||||
if (!isSafari && "Blob" in window) {
|
||||
let type = contentType || "text/html"
|
||||
let blob = (content instanceof Blob) ? content : new Blob([content], {type: type})
|
||||
let href = window.URL.createObjectURL(blob)
|
||||
let fileName = url.substr(url.lastIndexOf("/") + 1)
|
||||
let download = [type, fileName, href].join(":")
|
||||
|
||||
// Use filename from response header
|
||||
let disposition = headers["content-disposition"] || headers["Content-Disposition"]
|
||||
if (typeof disposition !== "undefined") {
|
||||
let responseFilename = /filename=([^;]*);?/i.exec(disposition)
|
||||
if (responseFilename !== null && responseFilename.length > 1) {
|
||||
download = responseFilename[1]
|
||||
}
|
||||
}
|
||||
|
||||
bodyEl = <div><a href={ href } download={ download }>{ "Download file" }</a></div>
|
||||
} else {
|
||||
bodyEl = <pre>Download headers detected but your browser does not support downloading binary via XHR (Blob).</pre>
|
||||
}
|
||||
|
||||
// Anything else (CORS)
|
||||
} else if (typeof content === "string") {
|
||||
bodyEl = <HighlightCode value={ content } />
|
||||
} else if ( content.size > 0 ) {
|
||||
|
||||
@@ -82,27 +82,9 @@ export default class OperationContainer extends PureComponent {
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const defaultContentType = "application/json"
|
||||
let { specActions, path, method, op } = nextProps
|
||||
let operation = op.get("operation")
|
||||
let producesValue = operation.get("produces_value")
|
||||
let produces = operation.get("produces")
|
||||
let consumes = operation.get("consumes")
|
||||
let consumesValue = operation.get("consumes_value")
|
||||
|
||||
if(nextProps.response !== this.props.response) {
|
||||
this.setState({ executeInProgress: false })
|
||||
}
|
||||
|
||||
if (producesValue === undefined) {
|
||||
producesValue = produces && produces.size ? produces.first() : defaultContentType
|
||||
specActions.changeProducesValue([path, method], producesValue)
|
||||
}
|
||||
|
||||
if (consumesValue === undefined) {
|
||||
consumesValue = consumes && consumes.size ? consumes.first() : defaultContentType
|
||||
specActions.changeConsumesValue([path, method], consumesValue)
|
||||
}
|
||||
}
|
||||
|
||||
toggleShown =() => {
|
||||
|
||||
@@ -44,7 +44,7 @@ export default {
|
||||
let { auth, token } = payload
|
||||
let parsedAuth
|
||||
|
||||
auth.token = token
|
||||
auth.token = Object.assign({}, token)
|
||||
parsedAuth = fromJS(auth)
|
||||
|
||||
return state.setIn( [ "authorized", parsedAuth.get("name") ], parsedAuth )
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function downloadUrlPlugin (toolbox) {
|
||||
function next(res) {
|
||||
if(res instanceof Error || res.status >= 400) {
|
||||
specActions.updateLoadingStatus("failed")
|
||||
return errActions.newThrownErr( new Error(res.statusText + " " + url) )
|
||||
return errActions.newThrownErr( new Error((res.message || res.statusText) + " " + url) )
|
||||
}
|
||||
specActions.updateLoadingStatus("success")
|
||||
specActions.updateSpec(res.text)
|
||||
|
||||
@@ -7,10 +7,10 @@ export const NEW_SPEC_ERR_BATCH = "err_new_spec_err_batch"
|
||||
export const NEW_AUTH_ERR = "err_new_auth_err"
|
||||
export const CLEAR = "err_clear"
|
||||
|
||||
export function newThrownErr(err, action) {
|
||||
export function newThrownErr(err) {
|
||||
return {
|
||||
type: NEW_THROWN_ERR,
|
||||
payload: { action, error: serializeError(err) }
|
||||
payload: serializeError(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,11 @@ export default class Servers extends React.Component {
|
||||
if(this.props.currentServer !== nextProps.currentServer) {
|
||||
// Server has changed, we may need to set default values
|
||||
let currentServerDefinition = servers
|
||||
.find(v => v.get("url") === nextProps.currentServer) || OrderedMap()
|
||||
.find(v => v.get("url") === nextProps.currentServer)
|
||||
|
||||
if(!currentServerDefinition) {
|
||||
return this.setServer(servers.first().get("url"))
|
||||
}
|
||||
|
||||
let currentServerVariableDefs = currentServerDefinition.get("variables") || OrderedMap()
|
||||
|
||||
@@ -90,6 +94,7 @@ export default class Servers extends React.Component {
|
||||
getEffectiveServerValue
|
||||
} = this.props
|
||||
|
||||
|
||||
let currentServerDefinition = servers.find(v => v.get("url") === currentServer) || OrderedMap()
|
||||
|
||||
let currentServerVariableDefs = currentServerDefinition.get("variables") || OrderedMap()
|
||||
|
||||
@@ -181,6 +181,9 @@ export const sampleXmlFromSchema = (schema, config={}) => {
|
||||
example = example || {}
|
||||
|
||||
for (let propName in props) {
|
||||
if (!props.hasOwnProperty(propName)) {
|
||||
continue
|
||||
}
|
||||
if ( props[propName].readOnly && !includeReadOnly ) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export const LOG_REQUEST = "spec_log_request"
|
||||
export const CLEAR_RESPONSE = "spec_clear_response"
|
||||
export const CLEAR_REQUEST = "spec_clear_request"
|
||||
export const CLEAR_VALIDATE_PARAMS = "spec_clear_validate_param"
|
||||
export const UPDATE_OPERATION_VALUE = "spec_update_operation_value"
|
||||
export const UPDATE_OPERATION_META_VALUE = "spec_update_operation_meta_value"
|
||||
export const UPDATE_RESOLVED = "spec_update_resolved"
|
||||
export const SET_SCHEME = "set_scheme"
|
||||
|
||||
@@ -150,14 +150,14 @@ export function clearValidateParams( payload ){
|
||||
|
||||
export function changeConsumesValue(path, value) {
|
||||
return {
|
||||
type: UPDATE_OPERATION_VALUE,
|
||||
type: UPDATE_OPERATION_META_VALUE,
|
||||
payload:{ path, value, key: "consumes_value" }
|
||||
}
|
||||
}
|
||||
|
||||
export function changeProducesValue(path, value) {
|
||||
return {
|
||||
type: UPDATE_OPERATION_VALUE,
|
||||
type: UPDATE_OPERATION_META_VALUE,
|
||||
payload:{ path, value, key: "produces_value" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
SET_REQUEST,
|
||||
SET_MUTATED_REQUEST,
|
||||
UPDATE_RESOLVED,
|
||||
UPDATE_OPERATION_VALUE,
|
||||
UPDATE_OPERATION_META_VALUE,
|
||||
CLEAR_RESPONSE,
|
||||
CLEAR_REQUEST,
|
||||
CLEAR_VALIDATE_PARAMS,
|
||||
@@ -52,8 +52,8 @@ export default {
|
||||
},
|
||||
|
||||
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {
|
||||
let operation = state.getIn( [ "resolved", "paths", ...pathMethod ] )
|
||||
let isXml = /xml/i.test(operation.get("consumes_value"))
|
||||
let meta = state.getIn( [ "meta", "paths", ...pathMethod ] )
|
||||
let isXml = /xml/i.test(meta.get("consumes_value"))
|
||||
|
||||
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
|
||||
return parameters.withMutations( parameters => {
|
||||
@@ -107,12 +107,17 @@ export default {
|
||||
return state.setIn( [ "mutatedRequests", path, method ], fromJSOrdered(req))
|
||||
},
|
||||
|
||||
[UPDATE_OPERATION_VALUE]: (state, { payload: { path, value, key } }) => {
|
||||
[UPDATE_OPERATION_META_VALUE]: (state, { payload: { path, value, key } }) => {
|
||||
// path is a pathMethod tuple... can't change the name now.
|
||||
let operationPath = ["resolved", "paths", ...path]
|
||||
let metaPath = ["meta", "paths", ...path]
|
||||
|
||||
if(!state.getIn(operationPath)) {
|
||||
// do nothing if the operation does not exist
|
||||
return state
|
||||
}
|
||||
return state.setIn([...operationPath, key], fromJS(value))
|
||||
|
||||
return state.setIn([...metaPath, key], fromJS(value))
|
||||
},
|
||||
|
||||
[CLEAR_RESPONSE]: (state, { payload: { path, method } } ) =>{
|
||||
|
||||
@@ -306,10 +306,13 @@ export function parametersIncludeType(parameters, typeValue="") {
|
||||
export function contentTypeValues(state, pathMethod) {
|
||||
pathMethod = pathMethod || []
|
||||
let op = spec(state).getIn(["paths", ...pathMethod], fromJS({}))
|
||||
let meta = state.getIn(["meta", "paths", ...pathMethod], fromJS({}))
|
||||
let producesValue = currentProducesFor(state, pathMethod)
|
||||
|
||||
const parameters = op.get("parameters") || new List()
|
||||
|
||||
const requestContentType = (
|
||||
op.get("consumes_value") ? op.get("consumes_value")
|
||||
meta.get("consumes_value") ? meta.get("consumes_value")
|
||||
: parametersIncludeType(parameters, "file") ? "multipart/form-data"
|
||||
: parametersIncludeType(parameters, "formData") ? "application/x-www-form-urlencoded"
|
||||
: undefined
|
||||
@@ -317,7 +320,7 @@ export function contentTypeValues(state, pathMethod) {
|
||||
|
||||
return fromJS({
|
||||
requestContentType,
|
||||
responseContentType: op.get("produces_value")
|
||||
responseContentType: producesValue
|
||||
})
|
||||
}
|
||||
|
||||
@@ -327,6 +330,24 @@ export function operationConsumes(state, pathMethod) {
|
||||
return spec(state).getIn(["paths", ...pathMethod, "consumes"], fromJS({}))
|
||||
}
|
||||
|
||||
// Get the currently selected produces value for an operation
|
||||
export function currentProducesFor(state, pathMethod) {
|
||||
pathMethod = pathMethod || []
|
||||
|
||||
const operation = spec(state).getIn(["paths", ...pathMethod], null)
|
||||
|
||||
if(operation === null) {
|
||||
// return nothing if the operation does not exist
|
||||
return
|
||||
}
|
||||
|
||||
const currentProducesValue = state.getIn(["meta", "paths", ...pathMethod, "produces_value"], null)
|
||||
const firstProducesArrayItem = operation.getIn(["produces", 0], null)
|
||||
|
||||
return currentProducesValue || firstProducesArrayItem || "application/json"
|
||||
|
||||
}
|
||||
|
||||
export const operationScheme = ( state, path, method ) => {
|
||||
let url = state.get("url")
|
||||
let matchResult = url.match(/^([a-z][a-z0-9+\-.]*):/)
|
||||
|
||||
@@ -18,7 +18,6 @@ function createStoreWithMiddleware(rootReducer, initialState, getSystem) {
|
||||
// createLogger( {
|
||||
// stateTransformer: state => state && state.toJS()
|
||||
// } ),
|
||||
// errorLog(getSystem), Need to properly handle errors that occur during a render. Ie: let them be...
|
||||
systemThunkMiddleware( getSystem )
|
||||
]
|
||||
|
||||
@@ -176,7 +175,7 @@ export default class Store {
|
||||
if(!isFn(newAction)) {
|
||||
throw new TypeError("wrapActions needs to return a function that returns a new function (ie the wrapped action)")
|
||||
}
|
||||
return newAction
|
||||
return wrapWithTryCatch(newAction)
|
||||
}, action || Function.prototype)
|
||||
})
|
||||
}
|
||||
@@ -256,11 +255,11 @@ export default class Store {
|
||||
|
||||
return objMap(obj, (fn) => {
|
||||
return (...args) => {
|
||||
let res = fn.apply(null, [getNestedState(), ...args])
|
||||
let res = wrapWithTryCatch(fn).apply(null, [getNestedState(), ...args])
|
||||
|
||||
// If a selector returns a function, give it the system - for advanced usage
|
||||
if(typeof(res) === "function")
|
||||
res = res(getSystem())
|
||||
res = wrapWithTryCatch(res)(getSystem())
|
||||
|
||||
return res
|
||||
}
|
||||
@@ -331,7 +330,7 @@ function callAfterLoad(plugins, system, { hasLoaded } = {}) {
|
||||
if(isObject(plugins) && !isArray(plugins)) {
|
||||
if(typeof plugins.afterLoad === "function") {
|
||||
calledSomething = true
|
||||
plugins.afterLoad.call(this, system)
|
||||
wrapWithTryCatch(plugins.afterLoad).call(this, system)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,14 +435,36 @@ function makeReducer(reducerObj) {
|
||||
if(!reducerObj)
|
||||
return state
|
||||
|
||||
let redFn = reducerObj[action.type]
|
||||
let redFn = (reducerObj[action.type])
|
||||
if(redFn) {
|
||||
return redFn(state, action)
|
||||
const res = wrapWithTryCatch(redFn)(state, action)
|
||||
// If the try/catch wrapper kicks in, we'll get null back...
|
||||
// in that case, we want to avoid making any changes to state
|
||||
return res === null ? state : res
|
||||
}
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
function wrapWithTryCatch(fn, {
|
||||
logErrors = true
|
||||
} = {}) {
|
||||
if(typeof fn !== "function") {
|
||||
return fn
|
||||
}
|
||||
|
||||
return function(...args) {
|
||||
try {
|
||||
return fn.call(this, ...args)
|
||||
} catch(e) {
|
||||
if(logErrors) {
|
||||
console.error(e)
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function configureStore(rootReducer, initialState, getSystem) {
|
||||
const store = createStoreWithMiddleware(rootReducer, initialState, getSystem)
|
||||
|
||||
|
||||
@@ -126,15 +126,6 @@ export function systemThunkMiddleware(getSystem) {
|
||||
}
|
||||
}
|
||||
|
||||
export const errorLog = getSystem => () => next => action => {
|
||||
try{
|
||||
next( action )
|
||||
}
|
||||
catch( e ) {
|
||||
getSystem().errActions.newThrownErr( e, action )
|
||||
}
|
||||
}
|
||||
|
||||
export function defaultStatusCode ( responses ) {
|
||||
let codes = responses.keySeq()
|
||||
return codes.contains(DEFAULT_REPONSE_KEY) ? DEFAULT_REPONSE_KEY : codes.filter( key => (key+"")[0] === "2").sort().first()
|
||||
@@ -351,6 +342,17 @@ export function mapToList(map, keyNames="key", collectedKeys=Im.Map()) {
|
||||
return list
|
||||
}
|
||||
|
||||
export function extractFileNameFromContentDispositionHeader(value){
|
||||
let responseFilename = /filename="([^;]*);?"/i.exec(value)
|
||||
if (responseFilename === null) {
|
||||
responseFilename = /filename=([^;]*);?/i.exec(value)
|
||||
}
|
||||
if (responseFilename !== null && responseFilename.length > 1) {
|
||||
return responseFilename[1]
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// PascalCase, aka UpperCamelCase
|
||||
export function pascalCase(str) {
|
||||
return upperFirst(camelCase(str))
|
||||
@@ -442,6 +444,7 @@ export const validateDateTime = (val) => {
|
||||
}
|
||||
|
||||
export const validateGuid = (val) => {
|
||||
val = val.toString().toLowerCase()
|
||||
if (!/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}[)}]?$/.test(val)) {
|
||||
return "Value must be a Guid"
|
||||
}
|
||||
@@ -616,6 +619,9 @@ export const parseSearch = () => {
|
||||
let params = search.substr(1).split("&")
|
||||
|
||||
for (let i in params) {
|
||||
if (!params.hasOwnProperty(i)) {
|
||||
continue
|
||||
}
|
||||
i = params[i].split("=")
|
||||
map[decodeURIComponent(i[0])] = decodeURIComponent(i[1])
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 770 B After Width: | Height: | Size: 458 B |
|
Before Width: | Height: | Size: 770 B After Width: | Height: | Size: 458 B |
@@ -1,6 +1,8 @@
|
||||
// Promise global, Used ( at least ) by 'whatwg-fetch'. And required by IE 11
|
||||
|
||||
if(typeof Promise === "undefined") {
|
||||
import win from "core/window"
|
||||
|
||||
if(typeof win.Promise === "undefined") {
|
||||
require("core-js/fn/promise")
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export default class StandaloneLayout extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
let { getComponent, specSelectors } = this.props
|
||||
let { getComponent, specSelectors, errSelectors } = this.props
|
||||
|
||||
let Container = getComponent("Container")
|
||||
let Row = getComponent("Row")
|
||||
@@ -27,6 +27,8 @@ export default class StandaloneLayout extends React.Component {
|
||||
const OnlineValidatorBadge = getComponent("onlineValidatorBadge", true)
|
||||
|
||||
const loadingStatus = specSelectors.loadingStatus()
|
||||
const lastErr = errSelectors.lastError()
|
||||
const lastErrMsg = lastErr ? lastErr.get("message") : ""
|
||||
|
||||
return (
|
||||
|
||||
@@ -43,12 +45,16 @@ export default class StandaloneLayout extends React.Component {
|
||||
<div className="info">
|
||||
<div className="loading-container">
|
||||
<h4 className="title">Failed to load API definition.</h4>
|
||||
</div>
|
||||
<p>{lastErrMsg}</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{ loadingStatus === "failedConfig" &&
|
||||
<div className="info" style={{ maxWidth: "880px", marginLeft: "auto", marginRight: "auto", textAlign: "center" }}>
|
||||
<h4 className="title">Failed to load remote configuration.</h4>
|
||||
<div className="loading-container">
|
||||
<h4 className="title">Failed to load remote configuration.</h4>
|
||||
<p>{lastErrMsg}</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{ !loadingStatus || loadingStatus === "success" && <BaseLayout /> }
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
max-width: 1460px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.opblock-tag-section
|
||||
@@ -657,6 +658,8 @@
|
||||
min-height: 1px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
.loading
|
||||
{
|
||||
|
||||
@@ -171,4 +171,4 @@ $browser-context: 16;
|
||||
animation: shake .4s 1;
|
||||
border-color: $_color-delete;
|
||||
background: lighten($_color-delete, 35%);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,15 +84,6 @@ table
|
||||
|
||||
.parameters-col_description
|
||||
{
|
||||
p
|
||||
{
|
||||
font-size: 14px;
|
||||
|
||||
margin: 0;
|
||||
|
||||
@include text_body();
|
||||
}
|
||||
|
||||
input[type=text]
|
||||
{
|
||||
width: 100%;
|
||||
|
||||
@@ -5,9 +5,9 @@ import reducer from "corePlugins/spec/reducers"
|
||||
|
||||
describe("spec plugin - reducer", function(){
|
||||
|
||||
describe("update operation value", function() {
|
||||
it("should update the operation at the specified key", () => {
|
||||
const updateOperationValue = reducer["spec_update_operation_value"]
|
||||
describe("update operation meta value", function() {
|
||||
it("should update the operation metadata at the specified key", () => {
|
||||
const updateOperationValue = reducer["spec_update_operation_meta_value"]
|
||||
|
||||
const state = fromJS({
|
||||
resolved: {
|
||||
@@ -34,7 +34,15 @@ describe("spec plugin - reducer", function(){
|
||||
"paths": {
|
||||
"/pet": {
|
||||
"post": {
|
||||
"description": "my operation",
|
||||
"description": "my operation"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
paths: {
|
||||
"/pet": {
|
||||
post: {
|
||||
"consumes_value": "application/json"
|
||||
}
|
||||
}
|
||||
@@ -46,7 +54,7 @@ describe("spec plugin - reducer", function(){
|
||||
})
|
||||
|
||||
it("shouldn't throw an error if we try to update the consumes_value of a null operation", () => {
|
||||
const updateOperationValue = reducer["spec_update_operation_value"]
|
||||
const updateOperationValue = reducer["spec_update_operation_meta_value"]
|
||||
|
||||
const state = fromJS({
|
||||
resolved: {
|
||||
@@ -56,6 +56,13 @@ describe("spec plugin - selectors", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
@@ -76,6 +83,71 @@ describe("spec plugin - selectors", function(){
|
||||
})
|
||||
})
|
||||
|
||||
it("should default to the first `produces` array value if current is not set", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
produces: [
|
||||
"application/xml",
|
||||
"application/whatever"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
"consumes_value": "one"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// When
|
||||
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
|
||||
// Then
|
||||
expect(contentTypes.toJS()).toEqual({
|
||||
requestContentType: "one",
|
||||
responseContentType: "application/xml"
|
||||
})
|
||||
})
|
||||
|
||||
it("should default to `application/json` if a default produces value is not available", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
"consumes_value": "one"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// When
|
||||
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
|
||||
// Then
|
||||
expect(contentTypes.toJS()).toEqual({
|
||||
requestContentType: "one",
|
||||
responseContentType: "application/json"
|
||||
})
|
||||
})
|
||||
|
||||
it("should prioritize consumes value first from an operation", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
@@ -83,13 +155,21 @@ describe("spec plugin - selectors", function(){
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
"consumes_value": "one",
|
||||
"parameters": [{
|
||||
"parameters": [{
|
||||
"type": "file"
|
||||
}],
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
"consumes_value": "one",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -106,7 +186,7 @@ describe("spec plugin - selectors", function(){
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
"parameters": [{
|
||||
"parameters": [{
|
||||
"type": "file"
|
||||
}],
|
||||
}
|
||||
@@ -128,7 +208,7 @@ describe("spec plugin - selectors", function(){
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
"parameters": [{
|
||||
"parameters": [{
|
||||
"type": "formData"
|
||||
}],
|
||||
}
|
||||
@@ -143,7 +223,7 @@ describe("spec plugin - selectors", function(){
|
||||
expect(contentTypes.toJS().requestContentType).toEqual("application/x-www-form-urlencoded")
|
||||
})
|
||||
|
||||
it("should be ok, if no operation found", function(){
|
||||
it("should return nothing, if the operation does not exist", function(){
|
||||
// Given
|
||||
let state = fromJS({ })
|
||||
|
||||
|
||||
@@ -571,116 +571,6 @@ describe("bound system", function(){
|
||||
// Then
|
||||
expect(renderedComponent.text()).toEqual("This came from mapStateToProps and this came from the system and this came from my own props")
|
||||
})
|
||||
|
||||
it("should catch errors thrown inside of React Component Class render methods", function() {
|
||||
// Given
|
||||
// eslint-disable-next-line react/require-render-return
|
||||
class BrokenComponent extends React.Component {
|
||||
render() {
|
||||
throw new Error("This component is broken")
|
||||
}
|
||||
}
|
||||
const system = new System({
|
||||
plugins: [
|
||||
ViewPlugin,
|
||||
{
|
||||
components: {
|
||||
BrokenComponent
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// When
|
||||
var Component = system.getSystem().getComponent("BrokenComponent")
|
||||
const renderedComponent = render(<Component />)
|
||||
|
||||
// Then
|
||||
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
|
||||
})
|
||||
|
||||
it("should catch errors thrown inside of pure component render methods", function() {
|
||||
// Given
|
||||
// eslint-disable-next-line react/require-render-return
|
||||
class BrokenComponent extends PureComponent {
|
||||
render() {
|
||||
throw new Error("This component is broken")
|
||||
}
|
||||
}
|
||||
|
||||
const system = new System({
|
||||
plugins: [
|
||||
ViewPlugin,
|
||||
{
|
||||
components: {
|
||||
BrokenComponent
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// When
|
||||
var Component = system.getSystem().getComponent("BrokenComponent")
|
||||
const renderedComponent = render(<Component />)
|
||||
|
||||
// Then
|
||||
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
|
||||
})
|
||||
|
||||
it("should catch errors thrown inside of stateless component functions", function() {
|
||||
// Given
|
||||
// eslint-disable-next-line react/require-render-return
|
||||
let BrokenComponent = function BrokenComponent() { throw new Error("This component is broken") }
|
||||
const system = new System({
|
||||
plugins: [
|
||||
ViewPlugin,
|
||||
{
|
||||
components: {
|
||||
BrokenComponent
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// When
|
||||
var Component = system.getSystem().getComponent("BrokenComponent")
|
||||
const renderedComponent = render(<Component />)
|
||||
|
||||
// Then
|
||||
expect(renderedComponent.text().startsWith("😱 Could not render")).toEqual(true)
|
||||
})
|
||||
|
||||
it("should catch errors thrown inside of container components", function() {
|
||||
// Given
|
||||
// eslint-disable-next-line react/require-render-return
|
||||
class BrokenComponent extends React.Component {
|
||||
render() {
|
||||
throw new Error("This component is broken")
|
||||
}
|
||||
}
|
||||
|
||||
const system = new System({
|
||||
plugins: [
|
||||
ViewPlugin,
|
||||
{
|
||||
components: {
|
||||
BrokenComponent
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// When
|
||||
var Component = system.getSystem().getComponent("BrokenComponent", true)
|
||||
const renderedComponent = render(
|
||||
<Provider store={system.getStore()}>
|
||||
<Component />
|
||||
</Provider>
|
||||
)
|
||||
|
||||
// Then
|
||||
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
|
||||
})
|
||||
})
|
||||
|
||||
describe("afterLoad", function() {
|
||||
@@ -793,4 +683,299 @@ describe("bound system", function(){
|
||||
expect(res).toEqual("so selective")
|
||||
})
|
||||
})
|
||||
|
||||
describe("error catching", function() {
|
||||
it("should encapsulate thrown errors in an afterLoad method", function() {
|
||||
// Given
|
||||
const ThrowyPlugin = {
|
||||
afterLoad(system) {
|
||||
throw new Error("afterLoad BREAKS STUFF!")
|
||||
},
|
||||
statePlugins: {
|
||||
doge: {
|
||||
selectors: {
|
||||
wow: () => (system) => {
|
||||
return "so selective"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const system = new System({
|
||||
plugins: []
|
||||
})
|
||||
|
||||
|
||||
// When
|
||||
expect(function() {
|
||||
system.register([ThrowyPlugin])
|
||||
// var resSystem = system.getSystem()
|
||||
}).toNotThrow()
|
||||
})
|
||||
|
||||
it("should encapsulate thrown errors in an action creator", function(){
|
||||
|
||||
// Given
|
||||
const system = new System({
|
||||
plugins: {
|
||||
statePlugins: {
|
||||
throw: {
|
||||
actions: {
|
||||
func() {
|
||||
throw new Error("this action creator THROWS!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
expect(function() {
|
||||
// TODO: fix existing action error catcher that creates THROWN ERR actions
|
||||
system.getSystem().throwActions.func()
|
||||
}).toNotThrow()
|
||||
})
|
||||
|
||||
it("should encapsulate thrown errors in a reducer", function(){
|
||||
|
||||
// Given
|
||||
const system = new System({
|
||||
plugins: {
|
||||
statePlugins: {
|
||||
throw: {
|
||||
actions: {
|
||||
func: () => {
|
||||
return {
|
||||
type: "THROW_FUNC",
|
||||
payload: "BOOM!"
|
||||
}
|
||||
}
|
||||
},
|
||||
reducers: {
|
||||
"THROW_FUNC": (state, action) => {
|
||||
throw new Error("this reducer EXPLODES!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
expect(function() {
|
||||
system.getSystem().throwActions.func()
|
||||
}).toNotThrow()
|
||||
})
|
||||
|
||||
it("should encapsulate thrown errors in a selector", function(){
|
||||
|
||||
// Given
|
||||
const system = new System({
|
||||
plugins: {
|
||||
statePlugins: {
|
||||
throw: {
|
||||
selectors: {
|
||||
func: (state, arg1) => {
|
||||
throw new Error("this selector THROWS!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
expect(system.getSystem().throwSelectors.func).toNotThrow()
|
||||
})
|
||||
|
||||
it("should encapsulate thrown errors in a complex selector", function(){
|
||||
|
||||
// Given
|
||||
const system = new System({
|
||||
plugins: {
|
||||
statePlugins: {
|
||||
throw: {
|
||||
selectors: {
|
||||
func: (state, arg1) => system => {
|
||||
throw new Error("this selector THROWS!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
expect(system.getSystem().throwSelectors.func).toNotThrow()
|
||||
})
|
||||
|
||||
it("should encapsulate thrown errors in a wrapAction", function(){
|
||||
|
||||
// Given
|
||||
const system = new System({
|
||||
plugins: {
|
||||
statePlugins: {
|
||||
throw: {
|
||||
actions: {
|
||||
func: () => {
|
||||
return {
|
||||
type: "THROW_FUNC",
|
||||
payload: "this original action does NOT throw"
|
||||
}
|
||||
}
|
||||
},
|
||||
wrapActions: {
|
||||
func: (ori) => (...args) => {
|
||||
throw new Error("this wrapAction UNRAVELS EVERYTHING!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
expect(system.getSystem().throwActions.func).toNotThrow()
|
||||
})
|
||||
|
||||
it("should encapsulate thrown errors in a wrapSelector", function(){
|
||||
|
||||
// Given
|
||||
const system = new System({
|
||||
plugins: {
|
||||
statePlugins: {
|
||||
throw: {
|
||||
selectors: {
|
||||
func: (state, arg1) => {
|
||||
return 123
|
||||
}
|
||||
},
|
||||
wrapSelectors: {
|
||||
func: (ori) => (...props) => {
|
||||
return ori(...props)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
expect(system.getSystem().throwSelectors.func).toNotThrow()
|
||||
})
|
||||
|
||||
describe("components", function() {
|
||||
it("should catch errors thrown inside of React Component Class render methods", function() {
|
||||
// Given
|
||||
// eslint-disable-next-line react/require-render-return
|
||||
class BrokenComponent extends React.Component {
|
||||
render() {
|
||||
throw new Error("This component is broken")
|
||||
}
|
||||
}
|
||||
const system = new System({
|
||||
plugins: [
|
||||
ViewPlugin,
|
||||
{
|
||||
components: {
|
||||
BrokenComponent
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// When
|
||||
var Component = system.getSystem().getComponent("BrokenComponent")
|
||||
const renderedComponent = render(<Component />)
|
||||
|
||||
// Then
|
||||
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
|
||||
})
|
||||
|
||||
it("should catch errors thrown inside of pure component render methods", function() {
|
||||
// Given
|
||||
// eslint-disable-next-line react/require-render-return
|
||||
class BrokenComponent extends PureComponent {
|
||||
render() {
|
||||
throw new Error("This component is broken")
|
||||
}
|
||||
}
|
||||
|
||||
const system = new System({
|
||||
plugins: [
|
||||
ViewPlugin,
|
||||
{
|
||||
components: {
|
||||
BrokenComponent
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// When
|
||||
var Component = system.getSystem().getComponent("BrokenComponent")
|
||||
const renderedComponent = render(<Component />)
|
||||
|
||||
// Then
|
||||
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
|
||||
})
|
||||
|
||||
it("should catch errors thrown inside of stateless component functions", function() {
|
||||
// Given
|
||||
// eslint-disable-next-line react/require-render-return
|
||||
let BrokenComponent = function BrokenComponent() { throw new Error("This component is broken") }
|
||||
const system = new System({
|
||||
plugins: [
|
||||
ViewPlugin,
|
||||
{
|
||||
components: {
|
||||
BrokenComponent
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// When
|
||||
var Component = system.getSystem().getComponent("BrokenComponent")
|
||||
const renderedComponent = render(<Component />)
|
||||
|
||||
// Then
|
||||
expect(renderedComponent.text().startsWith("😱 Could not render")).toEqual(true)
|
||||
})
|
||||
|
||||
it("should catch errors thrown inside of container components", function() {
|
||||
// Given
|
||||
// eslint-disable-next-line react/require-render-return
|
||||
class BrokenComponent extends React.Component {
|
||||
render() {
|
||||
throw new Error("This component is broken")
|
||||
}
|
||||
}
|
||||
|
||||
const system = new System({
|
||||
plugins: [
|
||||
ViewPlugin,
|
||||
{
|
||||
components: {
|
||||
BrokenComponent
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// When
|
||||
var Component = system.getSystem().getComponent("BrokenComponent", true)
|
||||
const renderedComponent = render(
|
||||
<Provider store={system.getStore()}>
|
||||
<Component />
|
||||
</Provider>
|
||||
)
|
||||
|
||||
// Then
|
||||
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -18,7 +18,8 @@ import {
|
||||
getAcceptControllingResponse,
|
||||
createDeepLinkPath,
|
||||
escapeDeepLinkPath,
|
||||
sanitizeUrl
|
||||
sanitizeUrl,
|
||||
extractFileNameFromContentDispositionHeader
|
||||
} from "core/utils"
|
||||
import win from "core/window"
|
||||
|
||||
@@ -90,6 +91,26 @@ describe("utils", function() {
|
||||
|
||||
})
|
||||
|
||||
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 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"
|
||||
|
||||
@@ -238,6 +259,7 @@ describe("utils", function() {
|
||||
it("doesn't return for valid guid", function() {
|
||||
expect(validateGuid("8ce4811e-cec5-4a29-891a-15d1917153c1")).toBeFalsy()
|
||||
expect(validateGuid("{8ce4811e-cec5-4a29-891a-15d1917153c1}")).toBeFalsy()
|
||||
expect(validateGuid("8CE4811E-CEC5-4A29-891A-15D1917153C1")).toBeFalsy()
|
||||
})
|
||||
|
||||
it("returns a message for invalid input'", function() {
|
||||
|
||||
@@ -67,6 +67,7 @@ module.exports = require("./make-webpack-config")(rules, {
|
||||
publicPath: "/",
|
||||
noInfo: true,
|
||||
hot: true,
|
||||
disableHostCheck: true, // for development within VMs
|
||||
stats: {
|
||||
colors: true
|
||||
},
|
||||
|
||||