Compare commits

..
17 Commits
Author SHA1 Message Date
kyle 14ef0cc379 v3.13.0 (#4342)
* v3.13.0

* Rebuild dist
2018-03-17 00:27:24 -07:00
kyle 08cfc77fde fix: default to empty ImmutableMap when schema is missing (#4341) 2018-03-16 22:59:46 -07:00
kyle b8cf080e6c docs: fix broken method table (#4340) 2018-03-16 22:34:54 -07:00
kyle 245428e7cd feat: preauthorization (#4339)
* feat: swagger 2.0 preauthorization methods
* tests: add cases for oas3 preauthorization
* docs: add new preauth docs; touch up existing auth docs
* tests: add `rootInject` tests
* docs: remove unfinished sentence
2018-03-16 22:25:04 -07:00
kyle 624a81201f feat: onComplete config option (#4322)
* feat: `onComplete` config option

* tests(e2e): add case for onComplete option
2018-03-16 00:08:39 -07:00
kyle b0be261ef8 fix: remove $$ref key from example if it exists (#4333)
* fix: remove $$ref key from example if it exists

* guard $$ref property access
2018-03-15 16:40:04 -07:00
kyle 6c5a370ef7 create InfoUrl and InfoBasePath (#4330)
* create `InfoUrl` component
* create `InfoBasePath` component
* linter fixes
2018-03-14 16:06:29 -07:00
Scott O'Hara fb7f12551f improve: show possible reasons when url fetch fails (#4295)
* improve: show possible reasons when url fetch fails
* Only check for fail reasons if no HTTP status code
* Check for mixed content errors first
* Harmonise error messages
* Set error source to 'fetch'
* improve: hide loading validator image (#4287)
* hide missing validator image
* Render nothing if validator badge has not loaded/received errors
* Revert style changes
* Allow images with data scheme (#4305)
* Add UnitTest for image
allows image elements with https scheme
* Test images with data scheme
* Add allowedSchemesByTag
* Fix error  Strings must use doublequote  quotes
* Add empty div (#4236)
if there are properties show an empty div
* v3.12.1 (#4311)
* v3.12.1
* Rebuild dist for v3.12.1
* Use window wrapper instead of direct reference
* Add trailing period to error messages
* improve readability
2018-03-12 16:57:51 -07:00
kyle 49e0466641 v3.12.1 (#4311)
* v3.12.1

* Rebuild dist for v3.12.1
2018-03-09 20:54:22 -08:00
Helder Sepulveda ad57f5a549 Add empty div (#4236)
if there are properties show an empty div
2018-03-08 17:46:21 -08:00
Helder Sepulveda ebf94dfccf Allow images with data scheme (#4305)
* Add UnitTest for images

allows image elements with https scheme

* Test images with data scheme

* Add allowedSchemesByTag

* Fix error  Strings must use doublequote  quotes
2018-03-08 17:17:30 -08:00
Helder Sepulveda 6ba93ad43f improve: hide loading validator image (#4287)
* hide missing validator image

* Render nothing if validator badge has not loaded/received errors

* Revert style changes
2018-03-06 21:43:06 -08:00
kyle 54153dd929 remove docker login email param (#4283) 2018-03-02 22:22:39 -08:00
kyle 83232dc2d8 v3.12.0 (#4282)
* Use `parameterWithMeta` to get parameter data in <ParameterRow>

* Prefer specPath when fetching resolved subtrees in OperationContainer

* Add test for OAS3 callback rendering

* Remove debugger statement

* Pass base resolution URL directly to Swagger-Client subtree resolver

* Remove accidental comment

* Migrate additional options

* Don't default to empty Map when getting subtree

* fix(validateParam): check for ImList type before using count method

* Use `replaceState` to update `urls.primaryName`

This gives us the stateful URL we want, without:
(a) refreshing the page on update
(b) creating a long, useless history for the user
(c) implying that browser history is two-way bound
    to Swagger-UI (it isn't, we don't have a router)

* Add `fn.opsFilter` docs and internal API versioning note

* restrict `x-example` functionality to Swagger 2.0

* polish Authorize + Close buttons

* add tachyons; use it for padding the new Reset button

* v3.12.0

* rebuild dist
2018-03-02 21:13:00 -08:00
russell ad43965d16 improve: add button to reset example when user modifies request body (#4185)
* Add a button to reset example when user modifys request body

* lint fixes

* Revert "lint fixes"

This reverts commit 5395005ea7.

* reapply lint fixes
2018-03-02 18:46:47 -08:00
Charles Capps 861cc65cff improve: add "Close" button to OAuth dialog, rename "Done" button elsewhere (#4212)
* Rename "Done" button to "Close", and add "Close" button OAuth dialog
* move Close button to the right
2018-03-02 18:40:54 -08:00
Davide Zipeto 4900b11079 feature: read the x-example field and use them as values (#3538)
* Read the x-example field and use them as values
Allow to show the value defined in the "x-example" field when pressing try-out. The "default" value will have the precedence over the x-example

* Use `parameterWithMeta` to get parameter data in <ParameterRow>
* Prefer specPath when fetching resolved subtrees in OperationContainer
* Add test for OAS3 callback rendering
* Remove debugger statement
* Pass base resolution URL directly to Swagger-Client subtree resolver
* Remove accidental comment
* Migrate additional options
* Don't default to empty Map when getting subtree
* make "default" and "x-example" mutually exclusive
"x-example" wins when both are present
* limit "x-example" to non-body parameters
2018-03-02 18:31:08 -08:00
51 changed files with 730 additions and 254 deletions
+1 -2
View File
@@ -15,7 +15,6 @@ before_deploy:
- npm run build
env:
- DOCKER_IMAGE_NAME=swaggerapi/swagger-ui
script: npm run test:ci
deploy:
- provider: npm
email: apiteam@swagger.io
@@ -35,7 +34,7 @@ deploy:
node: '6.9'
after_success:
- if [ $DOCKER_HUB_USERNAME ]; then
docker login --email=$DOCKER_HUB_EMAIL --username=$DOCKER_HUB_USERNAME --password=$DOCKER_HUB_PASSWORD;
docker login --username=$DOCKER_HUB_USERNAME --password=$DOCKER_HUB_PASSWORD;
if [ ! -z "$TRAVIS_TAG" ]; then
DOCKER_IMAGE_TAG=$TRAVIS_TAG;
+1 -1
View File
@@ -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.11.0 | 2018-02-09 | 2.0, 3.0 | [tag v3.11.0](https://github.com/swagger-api/swagger-ui/tree/v3.11.0)
3.13.0 | 2018-03-16 | 2.0, 3.0 | [tag v3.13.0](https://github.com/swagger-api/swagger-ui/tree/v3.13.0)
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)
+14 -20
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -1
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+43
View File
@@ -0,0 +1,43 @@
# Plug points
Swagger-UI exposes most of its internal logic through the plugin system.
Often, it is beneficial to override the core internals to achieve custom behavior.
### Note: Semantic Versioning
Swagger-UI's internal APIs are _not_ part of our public contract, which means that they can change without the major version changing.
If your custom plugins wrap, extend, override, or consume any internal core APIs, we recommend specifying a specific minor version of Swagger-UI to use in your application, because they will _not_ change between patch versions.
If you're installing Swagger-UI via NPM, for example, you can do this by using a tilde:
```js
{
"dependencies": {
"swagger-ui": "~3.11.0"
}
}
```
### `fn.opsFilter`
When using the `filter` option, tag names will be filtered by the user-provided value. If you'd like to customize this behavior, you can override the default `opsFilter` function.
For example, you can implement a multiple-phrase filter:
```js
const MultiplePhraseFilterPlugin = function() {
return {
fn: {
opsFilter: (taggedOps, phrase) => {
const phrases = phrase.split(", ")
return taggedOps.filter((val, key) => {
return phrases.some(item => key.indexOf(item) > -1)
})
}
}
}
}
```
+16
View File
@@ -2,6 +2,22 @@
A plugin is a function that returns an object - more specifically, the object may contain functions and components that augment and modify Swagger-UI's functionality.
### Note: Semantic Versioning
Swagger-UI's internal APIs are _not_ part of our public contract, which means that they can change without the major version changing.
If your custom plugins wrap, extend, override, or consume any internal core APIs, we recommend specifying a specific minor version of Swagger-UI to use in your application, because they will _not_ change between patch versions.
If you're installing Swagger-UI via NPM, for example, you can do this by using a tilde:
```js
{
"dependencies": {
"swagger-ui": "~3.11.0"
}
}
```
### Format
A plugin return value may contain any of these keys, where `stateKey` is a name for a piece of state:
+9
View File
@@ -59,6 +59,7 @@ Parameter Name | Description
`operationsSorter` | `Function=(a => a)`. Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), 'method' (sort by HTTP method) or a function (see Array.prototype.sort() to know how sort function works). Default is the order returned by the server unchanged.
`showExtensions` | `Boolean=false`. Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema.
`tagsSorter` | `Function=(a => a)`. Apply a sort to the tag list of each API. It can be 'alpha' (sort by paths alphanumerically) or a function (see [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to learn how to write a sort function). Two tag name strings are passed to the sorter for each pass. Default is the order determined by Swagger-UI.
`onComplete` | `Function=NOOP`. Provides a mechanism to be notified when Swagger-UI has finished rendering a newly provided definition.
##### Network
@@ -77,3 +78,11 @@ Parameter Name | Description
--- | ---
`modelPropertyMacro` | `Function`. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable
`parameterMacro` | `Function`. Function to set default value to parameters. Accepts two arguments parameterMacro(operation, parameter). Operation and parameter are objects passed for context, both remain immutable
### Instance methods
Method Name | Description
--- | ---
`initOAuth` | `(configObj) => void`. Provide Swagger-UI with information about your OAuth server - see the OAuth2 documentation for more information.
`preauthorizeBasic` | `(authDefinitionKey, username, password) => action`. Programmatically set values for a Basic authorization scheme.
`preauthorizeApiKey` | `(authDefinitionKey, apiKeyValue) => action`. Programmatically set values for an API key authorization scheme.
+2 -4
View File
@@ -1,7 +1,5 @@
# OAuth2 configuration
You can configure OAuth2 authorization by calling `initOAuth` method with passed configs under the instance of `SwaggerUIBundle`
default `client_id` and `client_secret`, `realm`, an application name `appName`, `scopeSeparator`, `additionalQueryStringParams`,
`useBasicAuthenticationWithAccessCodeGrant`.
You can configure OAuth2 authorization by calling the `initOAuth` method.
Config Name | Description
--- | ---
@@ -11,7 +9,7 @@ realm | realm query parameter (for oauth1) added to `authorizationUrl` and `toke
appName | application name, displayed in authorization popup. MUST be a string
scopeSeparator | scope separator for passing scopes, encoded before calling, default value is a space (encoded value `%20`). MUST be a string
additionalQueryStringParams | Additional query parameters added to `authorizationUrl` and `tokenUrl`. MUST be an object
useBasicAuthenticationWithAccessCodeGrant | Only activated for the `accessCode` flow. During the `authorization_code` request to the `tokenUrl`, pass the [Client Password](https://tools.ietf.org/html/rfc6749#section-2.3.1) using the HTTP Basic Authentication scheme (`Authorization` header with `Basic base64encoded[client_id:client_secret]`). The default is `false`
useBasicAuthenticationWithAccessCodeGrant | Only activated for the `accessCode` flow. During the `authorization_code` request to the `tokenUrl`, pass the [Client Password](https://tools.ietf.org/html/rfc6749#section-2.3.1) using the HTTP Basic Authentication scheme (`Authorization` header with `Basic base64encode(client_id + client_secret)`). The default is `false`
```javascript
const ui = SwaggerUI({...})
+27 -28
View File
@@ -1,6 +1,6 @@
{
"name": "swagger-ui",
"version": "3.11.0",
"version": "3.13.0",
"main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [
@@ -14,32 +14,31 @@
],
"license": "Apache-2.0",
"scripts": {
"build": "npm run build:core && npm run build:bundle && npm run build:standalone",
"build:bundle": "webpack --config webpack-dist-bundle.config.js --colors",
"build:core": "webpack --config webpack-dist.config.js --colors",
"build:standalone": "webpack --config webpack-dist-standalone.config.js --colors",
"deps:check": "npm run deps:license && npm run deps:size",
"deps:license": "license-checker --production --csv --out $npm_package_config_deps_check_dir/licenses.csv && license-checker --development --csv --out $npm_package_config_deps_check_dir/licenses-dev.csv",
"deps:size": "webpack -p --config webpack.check.js --json | webpack-bundle-size-analyzer >| $npm_package_config_deps_check_dir/sizes.txt",
"dev": "npm-run-all --parallel hot-server open-localhost",
"hot-server": "webpack-dev-server --host 0.0.0.0 --config webpack-hot-dev-server.config.js --inline --hot --progress --content-base dev-helpers/",
"lint": "eslint --cache --ext '.js,.jsx' src test",
"lint:errors": "eslint --cache --quiet --ext '.js,.jsx' src test",
"lint:fix": "eslint --cache --ext '.js,.jsx' src test --fix",
"open-localhost": "node -e 'require(\"open\")(\"http://localhost:3200\")'",
"build": "npm run build-core && npm run build-bundle && npm run build-standalone",
"build-bundle": "webpack --config webpack-dist-bundle.config.js --colors",
"build-core": "webpack --config webpack-dist.config.js --colors",
"build-standalone": "webpack --config webpack-dist-standalone.config.js --colors",
"predev": "npm install",
"test": "npm run lint:errors && npm run test:nodejs",
"test:ci": "npm-run-all lint:errors test:nodejs test:ci:e2e",
"test:browser": "karma start --config karma.conf.js",
"test:ci:e2e": "if [ \"$ENABLE_CI_E2E\" = \"true\" ]; then npm-run-all --parallel -r test:e2e:mock-api test:e2e:all:cbt; else exit 0; fi",
"test:coverage": "nyc npm run test:nodejs",
"test:e2e": "npm-run-all --parallel -r hot-server test:e2e:mock-api test:e2e:all",
"test:e2e:all": "sleep 3 && nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json",
"test:e2e:all:cbt": "sleep 3 && nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch-cbt.json",
"test:e2e:initial-render": "nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json --group initial-render",
"test:e2e:mock-api": "json-server --static test/e2e/specs --watch test/e2e/db.json --port 3204",
"test:nodejs": "mocha --recursive --compilers js:babel-core/register test/core test/components test/bugs test/swagger-ui-dist-package test/xss",
"watch": "webpack --config webpack-watch.config.js --watch --progress"
"dev": "npm-run-all --parallel hot-server open-localhost",
"watch": "webpack --config webpack-watch.config.js --watch --progress",
"open-localhost": "node -e 'require(\"open\")(\"http://localhost:3200\")'",
"hot-server": "webpack-dev-server --host 0.0.0.0 --config webpack-hot-dev-server.config.js --inline --hot --progress --content-base dev-helpers/",
"deps-license": "license-checker --production --csv --out $npm_package_config_deps_check_dir/licenses.csv && license-checker --development --csv --out $npm_package_config_deps_check_dir/licenses-dev.csv",
"deps-size": "webpack -p --config webpack.check.js --json | webpack-bundle-size-analyzer >| $npm_package_config_deps_check_dir/sizes.txt",
"deps-check": "npm run deps-license && npm run deps-size",
"lint": "eslint --cache --ext '.js,.jsx' src test",
"lint-errors": "eslint --cache --quiet --ext '.js,.jsx' src test",
"lint-fix": "eslint --cache --ext '.js,.jsx' src test --fix",
"test": "npm run lint-errors && npm run just-test-in-node",
"test-in-node": "npm run lint-errors && npm run just-test-in-node",
"just-test": "karma start --config karma.conf.js",
"just-test-in-node": "mocha --recursive --compilers js:babel-core/register test/core test/components test/bugs test/swagger-ui-dist-package test/xss",
"just-check-coverage": "nyc npm run just-test-in-node",
"test-e2e": "sleep 3 && nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json",
"e2e-initial-render": "nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json --group initial-render",
"mock-api": "json-server --watch test/e2e/db.json --port 3204",
"hot-e2e-server": "webpack-dev-server --content-base test/e2e/helpers --host 0.0.0.0 --config webpack-hot-dev-server.config.js --inline --hot --progress",
"e2e": "npm-run-all --parallel -r hot-e2e-server mock-api test-e2e"
},
"dependencies": {
"@braintree/sanitize-url": "^2.0.2",
@@ -85,7 +84,7 @@
"scroll-to-element": "^2.0.0",
"serialize-error": "2.0.0",
"shallowequal": "0.2.2",
"swagger-client": "^3.5.0",
"swagger-client": "^3.5.1",
"url-parse": "^1.1.8",
"whatwg-fetch": "0.11.1",
"worker-loader": "^0.7.1",
@@ -106,7 +105,6 @@
"babel-preset-react": "^6.23.0",
"babel-preset-stage-0": "^6.22.0",
"babel-runtime": "^6.23.0",
"cbt_tunnels": "^0.3.0",
"chromedriver": "^2.30.1",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "0.28.4",
@@ -147,6 +145,7 @@
"standard": "^10.0.2",
"standard-loader": "^6.0.1",
"style-loader": "0.18.2",
"tachyons-sass": "^4.9.2",
"url-loader": "0.5.9",
"webpack": "^2.6.1",
"webpack-bundle-size-analyzer": "^2.5.0"
+1 -1
View File
@@ -46,7 +46,7 @@ export default class ArrayModel extends Component {
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
}
{
!description ? null :
!description ? (properties.size ? <div className="markdown"></div> : null) :
<Markdown source={ description } />
}
<span>
+1 -1
View File
@@ -81,11 +81,11 @@ export default class Auths extends React.Component {
}).toArray()
}
<div className="auth-btn-wrapper">
<Button className="btn modal-btn auth btn-done" onClick={ this.close }>Done</Button>
{
nonOauthDefinitions.size === authorizedAuth.size ? <Button className="btn modal-btn auth" onClick={ this.logoutClick }>Logout</Button>
: <Button type="submit" className="btn modal-btn auth authorize">Authorize</Button>
}
<Button className="btn modal-btn auth btn-done" onClick={ this.close }>Close</Button>
</div>
</form>
}
+8
View File
@@ -39,6 +39,13 @@ export default class Oauth2 extends React.Component {
}
}
close = (e) => {
e.preventDefault()
let { authActions } = this.props
authActions.showDefinitions(false)
}
authorize =() => {
let { authActions, errActions, getConfigs, authSelectors } = this.props
let configs = getConfigs()
@@ -231,6 +238,7 @@ export default class Oauth2 extends React.Component {
: <Button className="btn modal-btn auth authorize" onClick={ this.authorize }>Authorize</Button>
)
}
<Button className="btn modal-btn auth btn-done" onClick={ this.close }>Close</Button>
</div>
</div>
+16 -3
View File
@@ -5,7 +5,7 @@ import ImPropTypes from "react-immutable-proptypes"
import { sanitizeUrl } from "core/utils"
class Path extends React.Component {
export class InfoBasePath extends React.Component {
static propTypes = {
host: PropTypes.string,
basePath: PropTypes.string
@@ -68,6 +68,17 @@ class License extends React.Component {
}
}
export class InfoUrl extends React.PureComponent {
static propTypes = {
url: PropTypes.string.isRequired
}
render() {
const { url } = this.props
return <a target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } </span></a>
}
}
export default class Info extends React.Component {
static propTypes = {
info: PropTypes.object,
@@ -90,6 +101,8 @@ export default class Info extends React.Component {
const Markdown = getComponent("Markdown")
const VersionStamp = getComponent("VersionStamp")
const InfoUrl = getComponent("InfoUrl")
const InfoBasePath = getComponent("InfoBasePath")
return (
<div className="info">
@@ -97,8 +110,8 @@ export default class Info extends React.Component {
<h2 className="title" >{ title }
{ version && <VersionStamp version={version}></VersionStamp> }
</h2>
{ host || basePath ? <Path host={ host } basePath={ basePath } /> : null }
{ url && <a target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } </span></a> }
{ host || basePath ? <InfoBasePath host={ host } basePath={ basePath } /> : null }
{ url && <InfoUrl url={url} /> }
</hgroup>
<div className="description">
+1 -1
View File
@@ -63,7 +63,7 @@ export default class Models extends Component {
const content = <ModelWrapper name={ name }
expandDepth={ defaultModelsExpandDepth }
schema={ schema }
schema={ schema || Im.Map() }
specPath={Im.List([...specPathBase, name])}
getComponent={ getComponent }
specSelectors={ specSelectors }
@@ -101,7 +101,7 @@ class ValidatorImage extends React.Component {
if (this.state.error) {
return <img alt={"Error"} />
} else if (!this.state.loaded) {
return <img alt= {"Loading..."} />
return null
}
return <img src={this.props.src} alt={this.props.alt} />
}
+9 -2
View File
@@ -24,11 +24,18 @@ export default class ParameterRow extends Component {
let { specSelectors, pathMethod, param } = props
let defaultValue = param.get("default")
let xExampleValue = param.get("x-example")
let parameter = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
let value = parameter ? parameter.get("value") : ""
if ( defaultValue !== undefined && value === undefined ) {
this.onChangeWrapper(defaultValue)
if( param.get("in") !== "body" ) {
if ( xExampleValue !== undefined && value === undefined && specSelectors.isSwagger2() ) {
this.onChangeWrapper(xExampleValue)
} else if ( defaultValue !== undefined && value === undefined ) {
this.onChangeWrapper(defaultValue)
}
}
}
componentWillReceiveProps(props) {
@@ -46,6 +46,7 @@ const sanitizeOptions = {
"td": [ "colspan" ],
"*": [ "class" ]
},
allowedSchemesByTag: { img: [ "http", "https", "data" ] },
textFilter: function(text) {
return text.replace(/&quot;/g, "\"")
}
+49
View File
@@ -8,6 +8,8 @@ export default function() {
afterLoad(system) {
this.rootInjects = this.rootInjects || {}
this.rootInjects.initOAuth = system.authActions.configureAuth
this.rootInjects.preauthorizeApiKey = preauthorizeApiKey.bind(null, system)
this.rootInjects.preauthorizeBasic = preauthorizeBasic.bind(null, system)
},
statePlugins: {
auth: {
@@ -21,3 +23,50 @@ export default function() {
}
}
}
export function preauthorizeBasic(system, key, username, password) {
const {
authActions: { authorize },
specSelectors: { specJson, isOAS3 }
} = system
const definitionBase = isOAS3() ? ["components", "securitySchemes"] : ["securityDefinitions"]
const schema = specJson().getIn([...definitionBase, key])
if(!schema) {
return null
}
return authorize({
[key]: {
value: {
username,
password,
},
schema: schema.toJS()
}
})
}
export function preauthorizeApiKey(system, key, value) {
const {
authActions: { authorize },
specSelectors: { specJson, isOAS3 }
} = system
const definitionBase = isOAS3() ? ["components", "securitySchemes"] : ["securityDefinitions"]
const schema = specJson().getIn([...definitionBase, key])
if(!schema) {
return null
}
return authorize({
[key]: {
value,
schema: schema.toJS()
}
})
}
+38 -1
View File
@@ -2,6 +2,7 @@
import { createSelector } from "reselect"
import { Map } from "immutable"
import win from "../window"
export default function downloadUrlPlugin (toolbox) {
let { fn } = toolbox
@@ -12,6 +13,7 @@ export default function downloadUrlPlugin (toolbox) {
const config = getConfigs()
url = url || specSelectors.url()
specActions.updateLoadingStatus("loading")
errActions.clear({source: "fetch"})
fetch({
url,
loadSpec: true,
@@ -26,7 +28,10 @@ export default function downloadUrlPlugin (toolbox) {
function next(res) {
if(res instanceof Error || res.status >= 400) {
specActions.updateLoadingStatus("failed")
return errActions.newThrownErr( new Error((res.message || res.statusText) + " " + url) )
errActions.newThrownErr(Object.assign( new Error((res.message || res.statusText) + " " + url), {source: "fetch"}))
// Check if the failure was possibly due to CORS or mixed content
if (!res.status && res instanceof Error) checkPossibleFailReasons()
return
}
specActions.updateLoadingStatus("success")
specActions.updateSpec(res.text)
@@ -35,6 +40,38 @@ export default function downloadUrlPlugin (toolbox) {
}
}
function checkPossibleFailReasons() {
try {
let specUrl
if("URL" in win ) {
specUrl = new URL(url)
} else {
// legacy browser, use <a href> to parse the URL
specUrl = document.createElement("a")
specUrl.href = url
}
if(specUrl.protocol !== "https:" && win.location.protocol === "https:") {
const error = Object.assign(
new Error(`Possible mixed-content issue? The page was loaded over https:// but a ${specUrl.protocol}// URL was specified. Check that you are not attempting to load mixed content.`),
{source: "fetch"}
)
errActions.newThrownErr(error)
return
}
if(specUrl.origin !== win.location.origin) {
const error = Object.assign(
new Error(`Possible cross-origin (CORS) issue? The URL origin (${specUrl.origin}) does not match the page (${win.location.origin}). Check the server returns the correct 'Access-Control-Allow-*' headers.`),
{source: "fetch"}
)
errActions.newThrownErr(error)
}
} catch (e) {
return
}
}
},
updateLoadingStatus: (status) => {
@@ -27,6 +27,7 @@ export default class RequestBodyEditor extends PureComponent {
this.state = {
isEditBox: false,
userDidModify: false,
value: ""
}
}
@@ -59,6 +60,11 @@ export default class RequestBodyEditor extends PureComponent {
this.onChange(this.sample(explicitMediaType))
}
resetValueToSample = (explicitMediaType) => {
this.setState({ userDidModify: false })
this.setValueToSample(explicitMediaType)
}
sample = (explicitMediaType) => {
let { requestBody, mediaType } = this.props
let schema = requestBody.getIn(["content", explicitMediaType || mediaType, "schema"]).toJS()
@@ -78,6 +84,7 @@ export default class RequestBodyEditor extends PureComponent {
const isJson = /json/i.test(mediaType)
const inputValue = isJson ? e.target.value.trim() : e.target.value
this.setState({ userDidModify: true })
this.onChange(inputValue)
}
@@ -87,13 +94,14 @@ export default class RequestBodyEditor extends PureComponent {
let {
isExecute,
getComponent,
mediaType,
} = this.props
const Button = getComponent("Button")
const TextArea = getComponent("TextArea")
const HighlightCode = getComponent("highlightCode")
let { value, isEditBox } = this.state
let { value, isEditBox, userDidModify } = this.state
return (
<div className="body-param">
@@ -104,14 +112,18 @@ export default class RequestBodyEditor extends PureComponent {
value={ value }/>)
}
<div className="body-param-options">
{
!isExecute ? null
: <div className="body-param-edit">
<Button className={isEditBox ? "btn cancel body-param__example-edit" : "btn edit body-param__example-edit"}
onClick={this.toggleIsEditBox}>{ isEditBox ? "Cancel" : "Edit"}
</Button>
</div>
}
<div className="body-param-edit">
{
!isExecute ? null
: <Button className={isEditBox ? "btn cancel body-param__example-edit" : "btn edit body-param__example-edit"}
onClick={this.toggleIsEditBox}>{ isEditBox ? "Cancel" : "Edit"}
</Button>
}
{ userDidModify &&
<Button className="btn ml3" onClick={() => { this.resetValueToSample(mediaType) }}>Reset</Button>
}
</div>
</div>
</div>
+28
View File
@@ -0,0 +1,28 @@
let engaged = false
export default function() {
return {
statePlugins: {
spec: {
wrapActions: {
updateSpec: (ori) => (...args) => {
engaged = true
return ori(...args)
},
updateJsonSpec: (ori, system) => (...args) => {
const cb = system.getConfigs().onComplete
if(engaged && typeof cb === "function") {
// call `onComplete` on next tick, which allows React to
// reconcile the DOM before we notify the user
setTimeout(cb, 0)
engaged = false
}
return ori(...args)
}
}
}
}
}
}
+4
View File
@@ -29,6 +29,10 @@ export const sampleFromSchema = (schema, config={}) => {
let { type, example, properties, additionalProperties, items } = objectify(schema)
let { includeReadOnly, includeWriteOnly } = config
if(example && example.$$ref) {
delete example.$$ref
}
if(example !== undefined)
return example
+10 -3
View File
@@ -13,6 +13,7 @@ import downloadUrlPlugin from "core/plugins/download-url"
import configsPlugin from "core/plugins/configs"
import deepLinkingPlugin from "core/plugins/deep-linking"
import filter from "core/plugins/filter"
import onComplete from "core/plugins/on-complete"
import OperationContainer from "core/containers/OperationContainer"
@@ -45,7 +46,10 @@ import Headers from "core/components/headers"
import Errors from "core/components/errors"
import ContentType from "core/components/content-type"
import Overview from "core/components/overview"
import Info from "core/components/info"
import Info, {
InfoUrl,
InfoBasePath
} from "core/components/info"
import Footer from "core/components/footer"
import ParamBody from "core/components/param-body"
import Curl from "core/components/curl"
@@ -124,7 +128,9 @@ export default function() {
OperationExtRow,
ParameterExt,
OperationContainer,
DeepLink
DeepLink,
InfoUrl,
InfoBasePath
}
}
@@ -154,6 +160,7 @@ export default function() {
SplitPaneModePlugin,
downloadUrlPlugin,
deepLinkingPlugin,
filter
filter,
onComplete
]
}
+2 -2
View File
@@ -350,7 +350,7 @@ export function extractFileNameFromContentDispositionHeader(value){
if (responseFilename !== null && responseFilename.length > 1) {
return responseFilename[1]
}
return null
return null
}
// PascalCase, aka UpperCamelCase
@@ -559,7 +559,7 @@ export const validateParam = (param, isXml, isOAS3 = false) => {
} else if ( type === "array" ) {
let itemType
if ( !value.count() ) { return errors }
if ( !listCheck || !value.count() ) { return errors }
itemType = paramDetails.getIn(["items", "type"])
+4 -1
View File
@@ -45,7 +45,10 @@ export default class Topbar extends React.Component {
setSearch = (spec) => {
let search = parseSearch()
search["urls.primaryName"] = spec.name
window.location.search = serializeSearch(search)
const newUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}`
if(window && window.history && window.history.pushState) {
window.history.replaceState(null, "", `${newUrl}?${serializeSearch(search)}`)
}
}
setSelectedUrl = (selectedUrl) => {
+3 -2
View File
@@ -21,6 +21,7 @@ export default class StandaloneLayout extends React.Component {
let Container = getComponent("Container")
let Row = getComponent("Row")
let Col = getComponent("Col")
let Errors = getComponent("errors", true)
const Topbar = getComponent("Topbar", true)
const BaseLayout = getComponent("BaseLayout", true)
@@ -45,8 +46,8 @@ export default class StandaloneLayout extends React.Component {
<div className="info">
<div className="loading-container">
<h4 className="title">Failed to load API definition.</h4>
<p>{lastErrMsg}</p>
</div>
<Errors/>
</div>
</div>
}
{ loadingStatus === "failedConfig" &&
+1
View File
@@ -21,6 +21,7 @@
.authorize
{
padding-right: 20px;
margin-right: 10px;
}
}
+1
View File
@@ -1,5 +1,6 @@
.swagger-ui
{
@import '~tachyons-sass/tachyons.scss';
@import 'mixins';
@import 'variables';
@import 'colors';
+24
View File
@@ -24,6 +24,18 @@ describe("Markdown component", function() {
const el = render(<Markdown source={str} />)
expect(el.html()).toEqual(`<div class="markdown"><p><img src="http://image.source" title="Image title"></p>\n</div>`)
})
it("allows image elements with https scheme", function() {
const str = `![Image alt text](https://image.source "Image title")`
const el = render(<Markdown source={str} />)
expect(el.html()).toEqual(`<div class="markdown"><p><img src="https://image.source" title="Image title"></p>\n</div>`)
})
it("allows image elements with data scheme", function() {
const str = `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==">`
const el = render(<Markdown source={str} />)
expect(el.html()).toEqual(`<div class="markdown"><p>` + str + `</p>\n</div>`)
})
it("allows heading elements", function() {
const str = `
@@ -51,6 +63,18 @@ describe("Markdown component", function() {
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img src="http://image.source" title="Image title"></p></div></div>`)
})
it("allows image elements with https scheme", function() {
const str = `![Image alt text](https://image.source "Image title")`
const el = render(<OAS3Markdown source={str} />)
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img src="https://image.source" title="Image title"></p></div></div>`)
})
it("allows image elements with data scheme", function() {
const str = `<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==">`
const el = render(<OAS3Markdown source={str} />)
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div>` + str + `</div></div>`)
})
it("allows heading elements", function() {
const str = `
# h1
+154
View File
@@ -0,0 +1,154 @@
/* eslint-env mocha */
import expect from "expect"
import { fromJS } from "immutable"
import { preauthorizeBasic, preauthorizeApiKey } from "corePlugins/auth"
import { authorize } from "corePlugins/auth/actions"
const S2_SYSTEM = {
authActions: {
authorize
},
specSelectors: {
isOAS3: () => false,
specJson: () => {
return fromJS({
swagger: "2.0",
securityDefinitions: {
"APIKeyHeader": {
"type": "apiKey",
"in": "header",
"name": "X-API-Key"
},
"basicAuth": {
"type": "basic"
}
}
})
}
}
}
const OAI3_SYSTEM = {
authActions: {
authorize
},
specSelectors: {
isOAS3: () => true,
specJson: () => {
return fromJS({
openapi: "3.0.0",
components: {
securitySchemes: {
basicAuth: {
type: "http",
scheme: "basic"
},
APIKeyHeader: {
type: "apiKey",
in: "header",
name: "X-API-Key"
}
}
}
})
}
}
}
describe("auth plugin - preauthorizers", () => {
describe("preauthorizeBasic", () => {
it("should return a valid authorize action in Swagger 2", () => {
const res = preauthorizeBasic(S2_SYSTEM, "basicAuth", "user", "pass")
expect(res).toEqual({
type: "authorize",
payload: {
basicAuth: {
schema: {
type: "basic"
},
value: {
username: "user",
password: "pass"
}
}
}
})
})
it("should return a valid authorize action in OpenAPI 3", () => {
const res = preauthorizeBasic(OAI3_SYSTEM, "basicAuth", "user", "pass")
expect(res).toEqual({
type: "authorize",
payload: {
basicAuth: {
schema: {
type: "http",
scheme: "basic"
},
value: {
username: "user",
password: "pass"
}
}
}
})
})
it("should return null when the authorization name is invalid in Swagger 2", () => {
const res = preauthorizeBasic(S2_SYSTEM, "fakeBasicAuth", "user", "pass")
expect(res).toEqual(null)
})
it("should return null when the authorization name is invalid in OpenAPI 3", () => {
const res = preauthorizeBasic(OAI3_SYSTEM, "fakeBasicAuth", "user", "pass")
expect(res).toEqual(null)
})
})
describe("preauthorizeApiKey", () => {
it("should return a valid authorize action in Swagger 2", () => {
const res = preauthorizeApiKey(S2_SYSTEM, "APIKeyHeader", "Asdf1234")
expect(res).toEqual({
type: "authorize",
payload: {
APIKeyHeader: {
schema: {
type: "apiKey",
name: "X-API-Key",
"in": "header"
},
value: "Asdf1234"
}
}
})
})
it("should return a valid authorize action in OpenAPI 3", () => {
const res = preauthorizeApiKey(OAI3_SYSTEM, "APIKeyHeader", "Asdf1234")
expect(res).toEqual({
type: "authorize",
payload: {
APIKeyHeader: {
schema: {
type: "apiKey",
"in": "header",
name: "X-API-Key"
},
value: "Asdf1234"
}
}
})
})
it("should return null when the authorization name is invalid in Swagger 2", () => {
const res = preauthorizeApiKey(S2_SYSTEM, "FakeAPIKeyHeader", "Asdf1234")
expect(res).toEqual(null)
})
it("should return null when the authorization name is invalid in OpenAPI 3", () => {
const res = preauthorizeApiKey(OAI3_SYSTEM, "FakeAPIKeyHeader", "Asdf1234")
expect(res).toEqual(null)
})
})
})
+30
View File
@@ -701,6 +701,36 @@ describe("bound system", function(){
})
})
describe("rootInjects", function() {
it("should attach a rootInject function as an instance method", function() {
// This is the same thing as the `afterLoad` tests, but is here for posterity
// Given
const system = new System({
plugins: [
{
afterLoad(system) {
this.rootInjects.wow = system.dogeSelectors.wow
},
statePlugins: {
doge: {
selectors: {
wow: () => (system) => {
return "so selective"
}
}
}
}
}
]
})
// When
var res = system.getSystem().wow()
expect(res).toEqual("so selective")
})
})
describe("error catching", function() {
it("should encapsulate thrown errors in an afterLoad method", function() {
// Given
-32
View File
@@ -1,32 +0,0 @@
/* eslint-disable no-console */
var cbt = require("cbt_tunnels")
module.exports = {
baseAppUrl: "http://swagger-ui-ci-asset-deployment-test.surge.sh",
// use rawgit.com to get test/e2e/specs/petstore.json from master
// not a great solution, but CBT's proxy is too slow to fetch
// the data directly.
specPath: "https://rawgit.com/swagger-api/swagger-ui/master/test/e2e/specs/petstore.json",
visibleTimeout: 10000,
before: function(done) {
console.log("Starting up tunnel")
cbt.start({
username: process.env.CBT_USERNAME || "",
authkey: process.env.CBT_AUTHKEY || ""
}, function(err, data) {
if (err) {
done(err)
} else {
done(data)
}
})
},
after: function(done) {
console.log("Closing Down Tunnel")
cbt.stop()
done()
}
}
-6
View File
@@ -1,6 +0,0 @@
var cbt = require("cbt_tunnels")
module.exports = {
baseAppUrl: "http://localhost:3200",
specPath: "http://localhost:3200/test-specs/petstore.json"
}
+111
View File
@@ -0,0 +1,111 @@
<!-- HTML for dev server -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Swagger UI</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body {
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>
</defs>
</svg>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
window["SwaggerUIBundle"] = window["swagger-ui-bundle"]
window["SwaggerUIStandalonePreset"] = window["swagger-ui-standalone-preset"]
// Build a system
const ui = SwaggerUIBundle({
url: "http://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
onComplete: () => {
if(window.completeCount) {
window.completeCount++
} else {
window.completeCount = 1
}
}
})
window.ui = ui
ui.initOAuth({
clientId: "your-client-id",
clientSecret: "your-client-secret-if-required",
realm: "your-realms",
appName: "your-app-name",
scopeSeparator: " ",
additionalQueryStringParams: {}
})
}
</script>
</body>
</html>
-57
View File
@@ -1,57 +0,0 @@
{
"src_folders" : ["test/e2e/scenarios"],
"output_folder" : "reports",
"live_output": true,
"custom_commands_path" : "",
"custom_assertions_path" : "",
"page_objects_path" : "test/e2e/pages",
"globals_path" : "test/e2e/cbt-globals.js",
"test_runner" : {
"type" : "mocha",
"options" : {
"ui" : "bdd",
"reporter" : "list"
}
},
"test_workers": {
"enabled": true,
"workers": 3
},
"selenium" : {
"start_process" : false,
"server_path" : "",
"log_path" : "",
"host" : "127.0.0.1",
"port" : 4444,
"cli_args" : {
"webdriver.chrome.driver" : "",
"webdriver.ie.driver" : ""
}
},
"test_settings" : {
"default" : {
"launch_url" : "http://localhost",
"selenium_port" : 80,
"selenium_host" : "hub.crossbrowsertesting.com",
"silent": true,
"screenshots" : {
"enabled" : false,
"path" : ""
},
"username": "${CBT_USERNAME}",
"access_key": "${CBT_AUTHKEY}",
"desiredCapabilities": {
"acceptSslCerts": "1",
"browserName": "chrome",
"javascriptEnabled": "1",
"record_network" : "true",
"record_video" : "true",
"max_duration": 1200
}
}
}
}
+2 -2
View File
@@ -5,7 +5,7 @@
"custom_commands_path" : "",
"custom_assertions_path" : "",
"page_objects_path" : "test/e2e/pages",
"globals_path" : "test/e2e/globals.js",
"globals_path" : "",
"test_workers" : {
"enabled" : true,
"workers" : "auto"
@@ -60,4 +60,4 @@
}
}
}
}
}
+1 -1
View File
@@ -20,7 +20,7 @@ module.exports = {
version: {
selector: ".version"
},
baseAppUrl: {
baseUrl: {
selector: ".base-url"
},
mainUrl: {
+13 -13
View File
@@ -2,35 +2,35 @@ describe("render informationContainer", function () {
let mainPage
let informationContainer
beforeEach(function (client, done) {
mainPage = client
.url(client.globals.baseAppUrl)
.url("localhost:3200")
.page.main()
client.waitForElementVisible(".download-url-input", client.globals.visibleTimeout || 5000)
.pause(client.globals.visibleTimeout || 5000)
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", client.globals.specPath)
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
informationContainer = mainPage.section.informationContainer
done()
})
it("renders section", function (client) {
mainPage.expect.section("@informationContainer").to.be.visible.before(client.globals.visibleTimeout || 5000)
mainPage.expect.section("@informationContainer").to.be.visible.before(5000)
client.end()
})
it("renders content", function (client) {
informationContainer.waitForElementVisible("@title", client.globals.visibleTimeout || 5000)
informationContainer.waitForElementVisible("@title", 5000)
.assert.containsText("@title", "Swagger Petstore")
.assert.containsText("@version", "1.0.0")
.assert.containsText("@baseAppUrl", "[ Base URL: localhost:3204/ ]")
.assert.attributeEquals("@mainUrl", "href", client.globals.specPath)
.assert.containsText("@mainUrlContent", client.globals.specPath)
.assert.containsText("@baseUrl", "[ Base URL: localhost:3204/ ]")
.assert.attributeEquals("@mainUrl", "href", "http://localhost:3200/test-specs/petstore.json")
.assert.containsText("@mainUrlContent", "http://localhost:3200/test-specs/petstore.json")
.assert.containsText("@description", "This is a sample server Petstore server. You can find out more about Swagger at http://swagger.io or on irc.freenode.net, #swagger. For this sample, you can use the api key special-key to test the authorization filters.")
.assert.attributeEquals("@swaggerUrl", "href", "http://swagger.io/")
.assert.attributeEquals("@swaggerircUrl", "href", "http://swagger.io/irc/")
@@ -42,7 +42,7 @@ describe("render informationContainer", function () {
.assert.containsText("@contactDevLink", "Contact the developer")
.assert.attributeEquals("@aboutSwaggerLink", "href", "http://swagger.io/")
.assert.containsText("@aboutSwaggerLink", "Find out more about Swagger")
client.end()
})
})
})
+6 -6
View File
@@ -3,12 +3,12 @@ describe("Render Model Wrapper", function () {
beforeEach(function (client, done) {
mainPage = client
.url(client.globals.baseAppUrl)
.url("localhost:3200")
.page.main()
client.waitForElementVisible(".download-url-input", client.globals.visibleTimeout || 5000)
.pause(client.globals.visibleTimeout || 5000)
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", client.globals.specPath)
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
@@ -20,13 +20,13 @@ describe("Render Model Wrapper", function () {
done()
})
it("Render model wrapper", function(client){
mainPage.expect.section("@modelWrapper").to.be.visible.before(client.globals.visibleTimeout || 5000)
mainPage.expect.section("@modelWrapper").to.be.visible.before(5000)
client.end()
})
it("Render model wrapper collapse", function(client){
modelWrapper.waitForElementVisible("@modelContainer", client.globals.visibleTimeout || 5000)
modelWrapper.waitForElementVisible("@modelContainer", 5000)
.click("@modelCollapse")
.assert.cssClassNotPresent("@modelContainer", "is-open")
+22
View File
@@ -0,0 +1,22 @@
const expect = require("expect")
describe("onComplete option", function () {
let mainPage
beforeEach(function (client, done) {
mainPage = client
.url("localhost:3200")
.page.main()
client.waitForElementVisible(".opblock-tag-section", 5000)
done()
})
it("triggers the page-provided onComplete exactly 1 times", function (client, done) {
client.execute(function() {
return window.completeCount
}, [], (result) => {
expect(result.value).toEqual(1)
client.end()
})
})
})
+15 -15
View File
@@ -3,13 +3,13 @@ describe("render pet api container", function () {
let apiWrapper
beforeEach(function (client, done) {
mainPage = client
.url(client.globals.baseAppUrl)
.url("localhost:3200")
.page.main()
client.waitForElementVisible(".download-url-input", client.globals.visibleTimeout || 5000)
.pause(client.globals.visibleTimeout || 5000)
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", client.globals.specPath)
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
@@ -25,13 +25,13 @@ describe("render pet api container", function () {
client.end()
})
it("test rendered pet container", function (client) {
apiWrapper.waitForElementVisible("@petAPIWrapper", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@petAPIWrapper", 5000)
.expect.element("@petAPIWrapper").to.be.visible
client.end()
})
it("collapse pet wrapper", function (client) {
apiWrapper.waitForElementVisible("@petAPIWrapper", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@petAPIWrapper", 5000)
.click("@petAPIWrapperBar")
.assert.cssClassNotPresent("@petAPIWrapper", "is-open")
@@ -41,7 +41,7 @@ describe("render pet api container", function () {
apiWrapper.waitForElementVisible("@petOperationPostContainer", 10000)
.assert.containsText("@petOperationPostTitle", "/pet")
.click("@petOperationPostCollpase")
.waitForElementVisible("@petOperationPostCollapseContainer", client.globals.visibleTimeout || 5000)
.waitForElementVisible("@petOperationPostCollapseContainer", 5000)
.click("@petOperationPostTryBtn")
.waitForElementVisible("@petOperationPostTryText", 1000)
.waitForElementVisible("@petOperationPostExecuteBtn", 1000)
@@ -52,9 +52,9 @@ describe("render pet api container", function () {
})
it("Testing post /pet api Mock data", function (client) {
apiWrapper.waitForElementVisible("@petOperationPostContainer", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@petOperationPostContainer", 5000)
.click("@petOperationPostCollpase")
.waitForElementVisible("@petOperationPostCollapseContainer", client.globals.visibleTimeout || 5000)
.waitForElementVisible("@petOperationPostCollapseContainer", 5000)
.click("@petOperationPostTryBtn")
.waitForElementVisible("@petOperationPostExecuteBtn", 1000)
.click("@petOperationPostExecuteBtn")
@@ -72,7 +72,7 @@ describe("render pet api container", function () {
})
it("render put /pet api container", function (client) {
apiWrapper.waitForElementVisible("@petOperationPutContainer", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@petOperationPutContainer", 5000)
.assert.containsText("@petOperationPutTitle", "/pet")
.click("@petOperationPutCollpase")
.waitForElementVisible("@petOperationPutCollapseContainer", 3000)
@@ -86,7 +86,7 @@ describe("render pet api container", function () {
})
it("Testing put /pet api Mock data", function (client) {
apiWrapper.waitForElementVisible("@petOperationPutContainer", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@petOperationPutContainer", 5000)
.click("@petOperationPutCollpase")
.waitForElementVisible("@petOperationPutCollapseContainer", 3000)
.click("@petOperationPutTryBtn")
@@ -106,7 +106,7 @@ describe("render pet api container", function () {
})
it("render get by tag /pet api container", function (client) {
apiWrapper.waitForElementVisible("@petOperationGetByTagContainer", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@petOperationGetByTagContainer", 5000)
.assert.containsText("@petOperationGetByTagTitle", "/pet/findByTags")
.click("@petOperationGetByTagCollpase")
.waitForElementVisible("@petOperationGetByTagCollapseContainer", 3000)
@@ -120,7 +120,7 @@ describe("render pet api container", function () {
})
it("Testing get by tag /pet api Mock data", function (client) {
apiWrapper.waitForElementVisible("@petOperationGetByTagContainer", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@petOperationGetByTagContainer", 5000)
.click("@petOperationGetByTagCollpase")
.waitForElementVisible("@petOperationGetByTagCollapseContainer", 3000)
.click("@petOperationGetByTagTryBtn")
@@ -140,7 +140,7 @@ describe("render pet api container", function () {
})
it("render get by ID /pet/{petId} api container", function (client) {
apiWrapper.waitForElementVisible("@petOperationGetByIdContainer", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@petOperationGetByIdContainer", 5000)
.assert.containsText("@petOperationGetByIdTitle", "/pet/{petId}")
.click("@petOperationGetByIdCollpase")
.waitForElementVisible("@petOperationGetByIdCollapseContainer", 3000)
@@ -153,7 +153,7 @@ describe("render pet api container", function () {
})
it("render get by ID /pet/{petId} api Mock data", function (client) {
apiWrapper.waitForElementVisible("@petOperationGetByIdContainer", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@petOperationGetByIdContainer", 5000)
.assert.containsText("@petOperationGetByIdTitle", "/pet/{petId}")
.click("@petOperationGetByIdCollpase")
.waitForElementVisible("@petOperationGetByIdCollapseContainer", 3000)
+8 -8
View File
@@ -3,13 +3,13 @@ describe("render store api container", function(){
let apiWrapper
beforeEach( function(client, done){
mainPage = client
.url(client.globals.baseAppUrl)
.url("localhost:3200")
.page.main()
client.waitForElementVisible(".download-url-input", client.globals.visibleTimeout || 5000)
client.waitForElementVisible(".download-url-input", 5000)
.pause(3000)
.clearValue(".download-url-input")
.setValue(".download-url-input", client.globals.specPath)
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
@@ -21,23 +21,23 @@ describe("render store api container", function(){
done()
})
it("test rendered store container", function(client){
apiWrapper.waitForElementVisible("@storeAPIWrapper", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@storeAPIWrapper", 5000)
.expect.element("@storeAPIWrapper").to.be.visible
client.end()
})
it("callapse store wrapper", function(client){
apiWrapper.waitForElementVisible("@storeAPIWrapper", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@storeAPIWrapper", 5000)
.click("@storeAPIWrapperBar")
.assert.cssClassNotPresent("@storeAPIWrapper", "is-open")
client.end()
})
it("render get /store/inventory api container", function (client) {
apiWrapper.waitForElementVisible("@storeOperationGetContainer", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@storeOperationGetContainer", 5000)
.assert.containsText("@storeOperationGetTitle", "/store/inventory")
.click("@storeOperationGetCollpase")
.waitForElementVisible("@storeOperationGetCollapseContainer", client.globals.visibleTimeout || 5000)
.waitForElementVisible("@storeOperationGetCollapseContainer", 5000)
.click("@storeOperationGetTryBtn")
.waitForElementVisible("@storeOperationGetExecuteBtn", 1000)
.click("@storeOperationGetTryBtn")
@@ -47,7 +47,7 @@ describe("render store api container", function(){
})
it("Testing get /store/inventory api Mock data ", function (client) {
apiWrapper.waitForElementVisible("@storeOperationGetContainer", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@storeOperationGetContainer", 5000)
.assert.containsText("@storeOperationGetTitle", "/store/inventory")
.click("@storeOperationGetCollpase")
.waitForElementVisible("@storeOperationGetCollapseContainer", 3000)
+10 -10
View File
@@ -3,13 +3,13 @@ describe("render user api container", function(){
let apiWrapper
beforeEach( function(client, done){
mainPage = client
.url(client.globals.baseAppUrl)
.url("localhost:3200")
.page.main()
client.waitForElementVisible(".download-url-input", client.globals.visibleTimeout || 5000)
.pause(client.globals.visibleTimeout || 5000)
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", client.globals.specPath)
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
@@ -21,20 +21,20 @@ describe("render user api container", function(){
done()
})
it("test rendered user container", function(client){
apiWrapper.waitForElementVisible("@userAPIWrapper", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@userAPIWrapper", 5000)
.expect.element("@userAPIWrapper").to.be.visible
client.end()
})
it("callapse user wrapper", function(client){
apiWrapper.waitForElementVisible("@userAPIWrapper", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@userAPIWrapper", 5000)
.click("@userAPIWrapperBar")
.assert.cssClassNotPresent("@userAPIWrapper", "is-open")
client.end()
})
it("render put /user/{username} api container", function (client) {
apiWrapper.waitForElementVisible("@userOperationPutContainer", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@userOperationPutContainer", 5000)
.assert.containsText("@userOperationPutTitle", "/user/{username}")
.click("@userOperationPutCollpase")
.waitForElementVisible("@userOperationPutCollapseContainer", 3000)
@@ -46,7 +46,7 @@ describe("render user api container", function(){
client.end()
})
it("Test put /user/{username} api Mock data", function (client) {
apiWrapper.waitForElementVisible("@userOperationPutContainer", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@userOperationPutContainer", 5000)
.assert.containsText("@userOperationPutTitle", "/user/{username}")
.click("@userOperationPutCollpase")
.waitForElementVisible("@userOperationPutCollapseContainer", 3000)
@@ -63,7 +63,7 @@ describe("render user api container", function(){
client.end()
})
it("render delete /user/{username} api container", function (client) {
apiWrapper.waitForElementVisible("@userOperationDeleteContainer", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@userOperationDeleteContainer", 5000)
.assert.containsText("@userOperationDeleteTitle", "/user/{username}")
.click("@userOperationDeleteCollpase")
.waitForElementVisible("@userOperationDeleteCollapseContainer", 3000)
@@ -75,7 +75,7 @@ describe("render user api container", function(){
client.end()
})
it("Test delete /user/{username} api Mock data", function (client) {
apiWrapper.waitForElementVisible("@userOperationDeleteContainer", client.globals.visibleTimeout || 5000)
apiWrapper.waitForElementVisible("@userOperationDeleteContainer", 5000)
.assert.containsText("@userOperationDeleteTitle", "/user/{username}")
.click("@userOperationDeleteCollpase")
.waitForElementVisible("@userOperationDeleteCollapseContainer", 3000)
+9 -9
View File
@@ -4,15 +4,15 @@ describe("Render scheme", function () {
beforeEach(function (client, done) {
mainPage = client
.url(client.globals.baseAppUrl)
.url("localhost:3200")
.page.main()
schemeContainer = mainPage.section.schemeContainer
client.waitForElementVisible(".download-url-input", client.globals.visibleTimeout || 5000)
.pause(client.globals.visibleTimeout || 5000)
client.waitForElementVisible(".download-url-input", 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", client.globals.specPath)
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
@@ -21,25 +21,25 @@ describe("Render scheme", function () {
})
it("render section", function (client) {
mainPage.expect.section("@schemeContainer").to.be.visible.before(client.globals.visibleTimeout || 5000)
mainPage.expect.section("@schemeContainer").to.be.visible.before(5000)
client.end()
})
it("render scheme option", function (client) {
schemeContainer.waitForElementVisible("@httpOption", client.globals.visibleTimeout || 5000)
schemeContainer.waitForElementVisible("@httpOption", 5000)
.expect.element("@httpOption").to.be.selected
client.end()
})
it("render authorized button", function (client) {
schemeContainer.waitForElementVisible("@btnAuthorize", client.globals.visibleTimeout || 5000)
schemeContainer.waitForElementVisible("@btnAuthorize", 5000)
.expect.element("@btnAuthorize").to.be.visible
client.end()
})
it("render click event", function(client) {
schemeContainer.waitForElementVisible("@btnAuthorize", client.globals.visibleTimeout || 5000)
schemeContainer.waitForElementVisible("@btnAuthorize", 5000)
.click("@btnAuthorize")
.assert.visible("@authorizationModal")
.assert.containsText("@appName", "Application: your-app-name")
@@ -49,4 +49,4 @@ describe("Render scheme", function () {
client.end()
})
})
})
+3 -3
View File
@@ -18,15 +18,15 @@ describe("initial render", function () {
beforeEach(function (client, done) {
mainPage = client
.url(client.globals.baseAppUrl)
.url("localhost:3200")
.page.main()
topbar = mainPage.section.topbar
client.waitForElementVisible(".download-url-input", 10000)
.pause(client.globals.visibleTimeout || 5000)
.pause(5000)
.clearValue(".download-url-input")
.setValue(".download-url-input", client.globals.specPath)
.setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json")
.click("button.download-url-button")
.pause(1000)
-1
View File
@@ -63,7 +63,6 @@ module.exports = require("./make-webpack-config")(rules, {
},
devServer: {
port: 3200,
contentBase: path.join(__dirname, "dev-helpers"),
publicPath: "/",
noInfo: true,
hot: true,